编译C++程序(经验版)

一、g++编译

# 编译
g++ 文件名
g++ helloWorld.cpp

g++ 文件名 -o 输出文件名
g++ open_video_v3.cpp -o open_video_v3.out
# 直接把编译后的文件 open_video_v3.out 拖到Terminal上,通过按回车键运行

Bus 002 Device 001: ID 1d6b:0003 Linux Foundation 3.0 root hub
Bus 001 Device 003: ID 04d9:a096 Holtek Semiconductor, Inc. ## 键盘
Bus 001 Device 002: ID 1532:005f Razer USA, Ltd ## 鼠标
Bus 001 Device 009: ID 05a3:9230 ARC International ## 摄像头
Bus 001 Device 010: ID 05a3:9230 ARC International ## 摄像头
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub

二、相关介绍

2.1 符号和符号表

程序的链接(三):符号和符号表

符号就是其实程序中的变量名、函数名。

2.1.1 符号的定义和引用

注意:局部变量temp分配在栈中,不会在函数外部被引用,因此不是符号定义。
在这里插入图片描述

2.2 .o .a .so文件

Linux的so文件到底是干嘛的?浅析Linux的动态链接库

2.2.1 编译与链接

链接其实就是把其他第三方库自己源代码生成的二进制目标文件融合在一起的过程。经过链接之后,那些第三方库中定义的函数就能被调用执行了。早期的一些操作系统一般使用静态链接的方式,现在基本上都在使用动态链接的方式。

2.2.2 静态链接与动态链接

虽然静态链接和动态链接都能生成可执行文件,但两者的代价差异很大。下面这张图可以很形象地演示了动态链接和静态链接的区别:
在这里插入图片描述

左侧右侧
动态链接静态链接
精简后的内容带在自己身上,需要什么,运行的时候再去拿所有依赖的第三方库函数都打包到了一起

左侧的人就像是一个动态链接的可执行文件,右侧的海象是一个静态链接的可执行文件。比起人,海象臃肿得多,那是因为静态链接在链接的时候,就把所依赖的第三方库函数都打包到了一起,导致最终的可执行文件非常大。而动态链接在链接的时候并不将那些库文件直接拿过来,而是在运行时,发现用到某些库中的某些函数时,再从这些第三方库中读取自己所需的方法。

2.2.3 SONAME命名规则

so文件后面往往跟着很多数字,这表示了不同的版本。so文件命名规则被称为SONAME(简单共享名,Short for shared object name):

libname.so.x.y.z

lib是前缀,这是一个约定俗成的规则。x为主版本号(Major Version),y为次版本号(Minor Version),z为发布版本号(Release Version)。

  • 主版本号(Major Version)表示重大升级,不同Major Version之间的库是不兼容的。Major Version升级后,或者依赖旧Major Version的程序需要更新代码,重新编译,才可以在新的Major Version上运行;或者操作系统保留旧Major Version,使得老程序依然能运行。
  • 次版本号(Minor Version)表示增量更新,一般是增加了一些新接口,原来的接口不变。所以,在Major Version相同的情况下,Minor Version从高到低是兼容的。
  • 发布版本号(Release Version)表示库的一些bug修复性能改进等,不添加任何新的接口,不改变原来的接口。

编译生成.so版本

.so文件的版本号分两部分,一部分叫soname,一部分是真正的版本号,soname在编译时生成,这个并不是一个简单的软链接,而是在生成动态链接库的时候编译生成的。

例如,gcc hello.c -fPIC -shared -Wl,-soname,libhello.so.0 -o libhello.so.0.0.1,参数是 -Wl,-soname,libhello.so.0(中间没有空格),-Wl选项告诉编译器将后面的参数传递给链接器,-soname则指定了动态库的soname,soname为libhello.so.0,真正的版本号是libhello.so.0.0.1。再运行 ldconfig -n .时,会根据编译时候的选项把soname和真正的库名关联起来。

将来升级时再次编译gcc hello.c -fPIC -shared -Wl,-soname,libhello.so.0 -o libhello.so.0.0.2,再运行 ldconfig -n . 就把 libhello.so.0 指向了libhello.so.0.0.2,达到了升级运态链接库的目的,而可执行文件不需要重新编译。

创建软链接

$ ls -l /lib/x86_64-linux-gnu/libpcre.so.3
/lib/x86_64-linux-gnu/libpcre.so.3 -> libpcre.so.3.13.2

因为不同的Major Version之间不兼容,而Minor Version和Release Version都是向下兼容的,软连接会指向Major Version相同,Minor Version和Release Version最高的.so文件上。

2.2.4 .o .a .so文件功能

工程里很多函数只是有声明,找不到实现的代码。因为那些实现代码已经编译成库所以看不见,我所看见的全是一堆头文件。.o、.a、.so文件都是Linux下的程序函数库,即编译好的可以供其他程序使用的代码和数据。

优点:程序模块化,容易重新编译,方便升级。

功能对应windows
.a文件静态函数库
.so文件共享目标文件(Shared Object)动态链接库(Dynamic Link Library,.dll)文件
.o文件目标文件.obj文件

2.2.5 .so文件路径

Linux的动态链接库绝大多数都在/usr/local/lib/usr/lib/lib下,操作系统也会默认去这三个路径下搜索动态链接库。

.so文件可执行程序/命令
文件路径/usr/local/lib
/usr/lib
/lib
/usr/local/bin

2.2.6 .so文件配置

/etc/ld.so.conf配置文件

.so配置文件所在路径:/etc/ld.so.conf,该配置文件会告诉操作系统去哪些路径下搜索动态链接库。
在这里插入图片描述

root@ls:~# ll /etc/ld
ldap/         ld.so.cache   ld.so.conf    ld.so.conf.d/
root@ls:~# ll /etc/ld
ldap/         ld.so.cache   ld.so.conf    ld.so.conf.d/
root@ls:~# ll /etc/ld.so.c
ld.so.cache   ld.so.conf    ld.so.conf.d/
root@ls:~# cat /etc/ld.so.conf
include /etc/ld.so.conf.d/*.conf

root@ls:~# ll /etc/ld.so.conf.d/
total 16
drwxr-xr-x  2 root root 4096 Mar 17  2022 ./
drwxr-xr-x 86 root root 4096 Aug 29 15:23 ../
-rw-r--r--  1 root root   44 Apr 15  2020 libc.conf
-rw-r--r--  1 root root  100 Apr 15  2020 x86_64-linux-gnu.conf
root@ls:~# cat /etc/ld.so.conf.d/libc.conf
# libc default configuration
/usr/local/lib
root@ls:~# cat /etc/ld.so.conf.d/x86_64-linux-gnu.conf
# Multiarch support
/usr/local/lib/x86_64-linux-gnu
/lib/x86_64-linux-gnu
/usr/lib/x86_64-linux-gnu

/etc/ld.so.cache缓存

这些位置的动态链接库很多,如果链接器每次都去这些路径遍历一遍,非常耗时,Linux提供了ldconfig工具,这个工具会对这些路径的动态链接库按照SONAME规则创建软连接,同时也会生成一个缓存Cache到/etc/ld.so.cache文件里,链接器根据缓存可以更快地查找到各个.so文件。每次在/lib/usr/lib这些路径下安装了新的库,或者更改了/etc/ld.so.conf文件,都需要调用ldconfig命令来做一次更新,重新生成软连接和Cache。但是/etc/ld.so.conf文件和ldconfig命令最好使用root账户操作。非root用户可以在某个路径下安装库文件,并将这个路径添加到/etc/ld.so.conf文件下,再由root用户调用一下ldconfig

对于非root用户,另一种方法是使用LD_LIBRARY_PATH环境变量。LD_LIBRARY_PATH存放着若干路径。链接器会去这些路径下查找库。非root可以将某个库安装在了一个非root权限的路径下,再将其添加到环境变量中。

动态链接库的查找先后顺序

  • LD_LIBRARY_PATH环境变量中的路径
  • /etc/ld.so.cache缓存文件
  • /usr/lib/lib

例如,我们把CUDA安装到/opt下面,我们可以使用下面的命令将CUDA添加到环境变量里。

export LD_LIBRARY_PATH=/opt/cuda/cuda-toolkit/lib64:$LD_LIBRARY_PATH

.so多版本共存

如果在执行某个具体程序前先执行上面的命令,那么这个程序将使用这个路径下的CUDA;如果将这行添加到了.bashrc文件,那么该用户一登录就会执行这行命令,因此该用户的所有程序也都将使用这个路径下的CUDA。当同一个动态链接库有多个不同版本的.so文件时,可以将他们安装到不同的路径下面,然后使用LD_LIBRARY_PATH环境变量来控制使用哪个库。这种比较适合在多人共享的服务器上使用不同版本的库,比如CUDA这种版本变化较快,且深度学习程序又高度依赖的库。

2.2.7 GCC编译选项

GCC选项含义用法
-l动态链接库文件名-lname myfile.c
-L动态链接库文件所在路径-L/path/to/library

使用GCC编译链接时,有两个参数需要注意,一个是-l(小写的L),一个是-L(大写的L)。我们前面曾提到,Linux有个约定速成的规则,假如库名是name,那么动态链接库文件名就是libname.so。在使用GCC编译链接时,-lname来告诉GCC使用哪个库。链接时,GCC的链接器ld就会前往LD_LIBRARY_PATH环境变量、/etc/ld.so.cache缓存文件和/usr/local/lib/usr/lib/lib目录下去查找libname.so。我们也可以用-L/path/to/library的方式,让链接器ld/path/to/library路径下去找库文件。

如果动态链接库文件在/path/to/library,库名叫name,编译链接的方式如下:

gcc -L/path/to/library -lname myfile.c

2.3 nm指令

nm指令用于查询.o .a .so文件的符号信息。

nm 列出.o .a .so中的符号信息

2.4 编译指令

参数Value解释说明
-Iarm64,arm32,x86_64硬件架构
-j32设定编译时所用的线程数
-Aon、off编译AAR包(包含arm32和arm64)
-d设置该参数,则编译Debug版本,否则编译Release版本
-i设置该参数,则进行增量编译,否则进行全量编译

2.5 CMakefile文件

###########################################################
#	File:	netPrnctl.so Makefile
#	Author: Neko
###########################################################

CC = gcc
CFLAGS = -Wall -g -fPIC

INCLUDE = -I./inc -I../ -I../cups -I../backend -I/usr/include/libusb-1.0
TARGET = netPrnctl.so
LIBVAR = -lusb-1.0 -lcups
LIBPATH = -L/usr/lib64/x86_64-linux-gnu

vpath % .h ./inc

OBJS	= public.o prnctlAvision.o prnctlNetwork.o prnctl.o
SRCS	= ./src/public.c ./src/prnctlAvision.c ./src/prnctlNetwork.c ./src/prnctl.c

$(OBJS):$(SRCS)
	$(CC) $(CFLAGS) $(INCLUDE) -c $^

all:$(OBJS)
	$(CC) -shared -fPIC -o $(TARGET) $(OBJS) $(LIBPATH) $(LIBVAR) -pthread
	cp $(TARGET) /usr/bin
	cp $(TARGET) /usr/lib64

clean:
	rm -f *.o
	rm -f netPrnctl.so

参数解释

参数Value解释说明
OBJSpublic.o prnctlAvision.o prnctlNetwork.o prnctl.o编译生成的目标文件
SRCS./src/public.c ./src/prnctlAvision.c ./src/prnctlNetwork.c ./src/prnctl.c
LIBVAR-lusb-1.0 -lcups
LIBPATH-L/usr/lib64/x86_64-linux-gnu
TARGETnetPrnctl.so

2.3 Just-In-Time (JIT) Compiler

即时编译(JIT)

问题引入

Java 编译器生成字节代码(与体系结构无关)后,执行将由 JVM(在 Java 中)处理。 字节码将由加载程序加载到 JVM 中,然后解释每个字节指令。当我们需要多次调用一个方法时,我们需要多次解释相同的代码,这可能比需要花费更多的时间。 因此,我们有了 JIT(即时)编译器。 将字节加载到 JVM(运行时)后,整个代码将被编译而不是解释,从而节省了时间。

JIT 编译器仅在运行时起作用,因此我们没有任何二进制输出。

JIT编译器

JIT编译器,中文翻译为“即实编译器”,也称为 动态翻译运行时编译,是一种执行计算机代码的方法。JIT 代表即时(Just-in-Time),这意味着代码将在需要时(而不是在运行时之前)进行编译。JIT编译器可以将字节码编译成机器码(动态编译),而不是解释字节码。代码可以在即将执行时进行编译(因此称为“即时”),然后缓存并在以后重用,无需重新编译。

JIT 编译器示例

  1. 在 Java 中,JIT 在 JVM(Java 虚拟机)中
  2. 在 C#中,它在 CLR(公共语言运行时)中
  3. 在 Android 中,它是 DVM(Dalvik 虚拟机)或更高版本的 ART(Android 运行时)。

Java中的JIT编译器

在Java编程语言和环境中,JIT编译器是一个把Java的字节码转换成可以直接发送给处理器的指令的程序,其中,字节码包括需要被解释的指令的程序。当你写好一个Java程序后,源语言的语句将由Java编译器编译成字节码,而不是编译成与某个特定的处理器硬件平台对应的指令代码(比如,Intel的Pentium微处理器或IBM的System/390处理器)。字节码是可以发送给任何平台并且能在那个平台上运行的独立于平台的代码

字节码

在字节码编译的系统中,源代码被转换为称为字节码的 中间表示形式。把源代码编译成字节码(虚拟机代码),称为字节码编译字节码不是任何特定计算机的机器代码,可以在计算机体系结构之间移植,然后可以在虚拟机上解释或运行字节码。

静态编译与动态编译

静态编译的代码或本地代码在 部署之前 编译,动态编译环境是在 执行期间 可以使用编译器的环境。

三、undefined symbol问题

问题待验证

3.1 问题引入

undefined symbol问题的查找、定位与解决方法

Linux 动态库 undefined symbol 原因定位与解决方法

程序编译通过,但是运行失败。

XXXX:[symbol](https://so.csdn.net/so/search?q=symbol&spm=1001.2101.3001.7020) lookup error:/home/....../libpdfium.so:undefined symbol:CRYPT_MD5Generate
./network: symbol lookup error: /usr/lib64/netPrnctl.so: undefined symbol: cupsGetDests

3.2 常用指令

# 查找so动态库的依赖库
ldd -r xxx.so

# 读取库被导出的符号
nm -gDC xxx.so | grep -i "xxx"

# 读取 ELF(Executable and Linkable Format)文件的相关信息
readelf -d xxx.so | grep rpath

# 获取符号的原始名
c++filt __ZN13SXVideoEngine6Public7License10SetLicenseEPKc

3.3 可能的原因

  1. 找不到依赖库
    这是最常见的原因,一般是没有指定查找目录,或者没有安装到系统查找目录里。
  2. 链接的依赖库不一致
    编译的时候使用了高版本,然后不同机器使用时链接的却是低版本,低版本可能缺失某些 api。
  3. 符号被隐藏
    如果动态库编译时被默认隐藏,外部代码使用了某个被隐藏的符号。
  4. c++ abi 版本不一致
    最典型的例子就是 gcc 4.x 到 gcc 5.x 版本之间的问题,在 4.x 编辑的动态库,不能在 5.x 中链接使用。

3.4 找不到依赖库

  • 使用 ldd -r , 确定系统库中是否存在所依赖的库。
  • 执行 ldconfig 命令更新 ld 缓存。
  • 执行 ldconfig -p | grep {SO_NAME} 查看是否能找到对应的库。
  • 检查 LD_LIBRATY_PATH 是否设置了有效的路径。

3.5 链接的依赖库不一致

如果系统中之前有安装过相同的库,或者存在多个库,就需要确定链接的具体是哪个库。

有一个特殊场景需要注意下,.so 文件中有个默认 rpath 路径,用于搜索被依赖的库,这个路径优先于系统目录和LD_LIBRARY_PATH。假如 rpath 存在相同名字的 .so 文件,会优先加载这个路径的文件。

查找rpapth

readelf -d libSXVideoEngineJni.so | grep rpath

3.6 符号被隐藏

第三方已经编译好的库,在引入了对应的头文件,使用了其中的某个方法,最终链接的时候出现 undefined symbol,这种情况有可能是库的开发者并没有导出这个方法的符号。

# 使用 nm 命令查看导出的函数符号, 这里查看 License 相关的函数
$ nm -gDC libSXVideoEngineJni.so | grep -i license
0000000000008110 T __ZN13SXVideoEngine6Public7License10SetLicenseEPKc
0000000000008130 T __ZN13SXVideoEngine6Public7License13LicenseStatusEv
0000000000008190 T __ZN13SXVideoEngine6Public7License19IsVideoCutSupportedEv
0000000000008170 T __ZN13SXVideoEngine6Public7License26IsDynamicTemplateSupportedEv
0000000000008150 T __ZN13SXVideoEngine6Public7License26IsStadardTemplateSupportedEv

# nm 返回的并不是原始函数名,通过 c++filt 获取原始名称
$ c++filt __ZN13SXVideoEngine6Public7License10SetLicenseEPKc
SXVideoEngine::Public::License::SetLicense(char const*)

3.7 c++ Abi 版本不一致

Gcc 对 c++ 的新特性是一步一步的增加的,如果实现了新的特性,就可能会修改 c++ 的 abi,并且会升级 glibc 的版本。

Abi 链接最常见的错误是 std::string 和 std::list 的在gcc 4.x 和 gcc 5.x 的不同实现引起的。在gcc 4.x 时,gcc 对标准 string 的实现就放在 std 命名空间下,编译时展开为 std::basic_string 。但是 gcc 5.x 开始,对 string 的实现就放在了 std::__cxx11空间里,编译后展开为 std::__cxx11::basic_string 。这就会导致在 gcc 4.x 编译的动态库,假如有的函数使用了 string 作为参数或者返回值,这时导出的函数参数为 std::basic_string 类型。 无法在 gcc 5.x 下编译连接使用。
错误类似:

undefined symbol:  "std::__cxx11 ***"

这种情况有一个折中办法就是在gcc 5.x 或以上 编译时,增加 -D_GLIBCXX_USE_CXX11_ABI=0 禁用 c++11 abi。
当然最好的做法就是保证编译器大版本基本一致。在新开发的程序如果用到了 c++ 的新特性,升级 gcc 版本和 glibc 是十分必要的。

3.8 其他情况

查看.so库文件的架构

使用file 命令查看 so库的架构,看看是否与平台一致

file xxx.so
$ file libpathplan.so 
libpathplan.so: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, 
BuildID[sha1]=32ae641e73c547376df20ca94746fbf5507de415, not stripped

查看so库链接状态和错误信息

ldd命令,可以查看对应的可执行文件或库文件依赖哪些库,但可执行文件或库文件要求与操作系统的编译器类型相同,即电脑是X86的GCC编译器,那么无法通过ldd命令查看ARM交叉编译器编译出来的可执行文件或库文件。

ldd -r xxx.so

如果想在Ubuntu等Linux宿主机上查看ARM交叉编译好的可执行程序和库文件的相关依赖关系,可以通过以下命令:

readelf -d xxx.so | grep NEEDED
$ ldd -r libpathplan.so
	linux-vdso.so.1 =>  (0x00007ffec1bd8000)
	libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f186cc0a000)
	libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f186c901000)
	libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f186c6eb000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f186c321000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f186d27a000)
undefined symbol: pthread_create	(./libpathplan.so)
undefined symbol: _ZN12ninebot_algo10AprAlgoLog9instance_E	(./libpathplan.so)
undefined symbol: _ZN2cv3maxERKNS_3MatES2_	(./libpathplan.so)
undefined symbol: _ZN12ninebot_algo10AprAlgoLog8WriteLogE10LEVEL_TYPEPKcS3_z	(./libpathplan.so)
undefined symbol: _ZN2cv6dilateERKNS_11_InputArrayERKNS_12_OutputArrayES2_NS_6Point_IiEEiiRKNS_7Scalar_IdEE	(./libpathplan.so)
undefined symbol: _ZN2cvgtERKNS_3MatEd	(./libpathplan.so)
undefined symbol: _ZN2cv8fastFreeEPv	(./libpathplan.so)
undefined symbol: _ZN2cv3Mat5setToERKNS_11_InputArrayES3_	(./libpathplan.so)
undefined symbol: _ZN12ninebot_algo10AprAlgoLog9instance_E	(./libpathplan.so)

可以看到有好多 undefined symbol ,其中就有提到的 _ZN12ninebot_algo10AprAlgoLog9instance_E 错误

使用 c++filt symbol 定位错误在那个C++文件中

从上面的undefined symbol中,通过c++filt ,可以定位到大多是opencv的问题。

$ c++filt _ZN2cv7waitKeyEi
cv::waitKey(int)

$ c++filt _ZN2cv3maxERKNS_3MatES2_
cv::max(cv::Mat const&, cv::Mat const&)

3.9 样例(经验版)

undefined symbol 问题解决记录

出现问题

./network: symbol lookup error: /usr/lib64/netPrnctl.so: undefined symbol: cupsGetDests

查看so依赖库

ldd -r /usr/lib64/netPrnctl.so
neko@neko:~/ZCPrinterDevice/NetworkSetupTool/filter$ ldd -r /usr/lib64/netPrnctl.so
	linux-vdso.so.1 (0x00007ffeef44c000)
	libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f9607add000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f96076ec000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f9607f0f000)
undefined symbol: libusb_open	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsArrayNew	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsDirOpen	(/usr/lib64/netPrnctl.so)
undefined symbol: ppdFindAttr	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsParseOptions	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsGetPPD	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_set_interface_alt_setting	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_release_interface	(/usr/lib64/netPrnctl.so)
undefined symbol: ppdClose	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsGetOption	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_detach_kernel_driver	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_close	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsFreeOptions	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsDirClose	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_get_string_descriptor_ascii	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_free_config_descriptor	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_get_config_descriptor	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsBackChannelWrite	(/usr/lib64/netPrnctl.so)
undefined symbol: _cupsGet1284Values	(/usr/lib64/netPrnctl.so)
undefined symbol: _ppdNormalizeMakeAndModel	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_attach_kernel_driver	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_get_device_list	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_kernel_driver_active	(/usr/lib64/netPrnctl.so)
undefined symbol: _cups_strcasecmp	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_bulk_transfer	(/usr/lib64/netPrnctl.so)
undefined symbol: _cupsLangPrintFilter	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsSideChannelRead	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsArrayCount	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsFileClose	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsDirRead	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_set_configuration	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsFreeDests	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_open_device_with_vid_pid	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_reset_device	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_exit	(/usr/lib64/netPrnctl.so)
undefined symbol: ppdOpenFile	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_init	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_get_device_descriptor	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsBackendReport	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_free_device_list	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_claim_interface	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsFileOpen	(/usr/lib64/netPrnctl.so)
undefined symbol: libusb_control_transfer	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsFileGets	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsArrayAdd	(/usr/lib64/netPrnctl.so)
undefined symbol: backendGetMakeModel	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsArrayFind	(/usr/lib64/netPrnctl.so)
undefined symbol: _cups_strlcpy	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsGetDests	(/usr/lib64/netPrnctl.so)
undefined symbol: cupsSideChannelWrite	(/usr/lib64/netPrnctl.so)
undefined symbol: httpAssembleURIf	(/usr/lib64/netPrnctl.so)

猜测问题原因:发现netPrnctl.so动态库没有链接libusb 与 libcups 两个库,导致运行可执行程序时netPrnctl.so依赖的libusb 与 libcups 两个库没有链接上。

查找libusb和libcups

libusb和libcups库文件一般在gcc路径下。

ls /usr/lib/x86_64-linux-gnu/ | grep libcups
ls /usr/lib/x86_64-linux-gnu/ | grep libusb
neko@neko:~/ZCPrinterDevice/NetworkSetupTool/filter$ ls /usr/lib/x86_64-linux-gnu/ | grep libcups
libcupscgi.so.1
libcupsfilters.so.1
libcupsfilters.so.1.0.0
libcupsimage.so.2
libcupsmime.so.1
libcupsppdc.so.1
libcups.so.2
neko@neko:~/ZCPrinterDevice/NetworkSetupTool/filter$ ls /usr/lib/x86_64-linux-gnu/ | grep libusb
libusb-1.0.a
libusb-1.0.so
libusbmuxd.so.4
libusbmuxd.so.4.0.0

修改Makefile文件

增加libusb、libcups链接库

LIBVAR = -lusb-1.0 -lcups
LIBPATH = -L/usr/lib64/x86_64-linux-gnu

重新编译

编译通过。

(可选)查看so依赖库

重新编译之后,显示so依赖库全部链接上了。

neko@neko:~/ZCPrinterDevice/NetworkSetupTool/filter$ ldd -r /usr/lib64/netPrnctl.so
	linux-vdso.so.1 (0x00007fff43f66000)
	libusb-1.0.so.0 => /lib/x86_64-linux-gnu/libusb-1.0.so.0 (0x00007f4087f39000)
	libcups.so.2 => /usr/lib/x86_64-linux-gnu/libcups.so.2 (0x00007f4087cad000)
	libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f4087a8e000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f408769d000)
	libudev.so.1 => /lib/x86_64-linux-gnu/libudev.so.1 (0x00007f408747f000)
	libgssapi_krb5.so.2 => /usr/lib/x86_64-linux-gnu/libgssapi_krb5.so.2 (0x00007f4087234000)
	libgnutls.so.30 => /usr/lib/x86_64-linux-gnu/libgnutls.so.30 (0x00007f4086ecf000)
	libavahi-common.so.3 => /usr/lib/x86_64-linux-gnu/libavahi-common.so.3 (0x00007f4086cc3000)
	libavahi-client.so.3 => /usr/lib/x86_64-linux-gnu/libavahi-client.so.3 (0x00007f4086ab2000)
	libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f4086895000)
	libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f40864f7000)
	/lib64/ld-linux-x86-64.so.2 (0x00007f4088364000)
	librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f40862ef000)
	libkrb5.so.3 => /usr/lib/x86_64-linux-gnu/libkrb5.so.3 (0x00007f4086019000)
	libk5crypto.so.3 => /usr/lib/x86_64-linux-gnu/libk5crypto.so.3 (0x00007f4085de7000)
	libcom_err.so.2 => /lib/x86_64-linux-gnu/libcom_err.so.2 (0x00007f4085be3000)
	libkrb5support.so.0 => /usr/lib/x86_64-linux-gnu/libkrb5support.so.0 (0x00007f40859d8000)
	libp11-kit.so.0 => /usr/lib/x86_64-linux-gnu/libp11-kit.so.0 (0x00007f40856a9000)
	libidn2.so.0 => /usr/lib/x86_64-linux-gnu/libidn2.so.0 (0x00007f408548c000)
	libunistring.so.2 => /usr/lib/x86_64-linux-gnu/libunistring.so.2 (0x00007f408510e000)
	libtasn1.so.6 => /usr/lib/x86_64-linux-gnu/libtasn1.so.6 (0x00007f4084efb000)
	libnettle.so.6 => /usr/lib/x86_64-linux-gnu/libnettle.so.6 (0x00007f4084cc5000)
	libhogweed.so.4 => /usr/lib/x86_64-linux-gnu/libhogweed.so.4 (0x00007f4084a91000)
	libgmp.so.10 => /usr/lib/x86_64-linux-gnu/libgmp.so.10 (0x00007f4084810000)
	libdbus-1.so.3 => /lib/x86_64-linux-gnu/libdbus-1.so.3 (0x00007f40845c3000)
	libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f40843bf000)
	libkeyutils.so.1 => /lib/x86_64-linux-gnu/libkeyutils.so.1 (0x00007f40841bb000)
	libresolv.so.2 => /lib/x86_64-linux-gnu/libresolv.so.2 (0x00007f4083fa0000)
	libffi.so.6 => /usr/lib/x86_64-linux-gnu/libffi.so.6 (0x00007f4083d98000)
	libsystemd.so.0 => /lib/x86_64-linux-gnu/libsystemd.so.0 (0x00007f4083b14000)
	liblzma.so.5 => /lib/x86_64-linux-gnu/liblzma.so.5 (0x00007f40838ee000)
	liblz4.so.1 => /usr/lib/x86_64-linux-gnu/liblz4.so.1 (0x00007f40836d2000)
	libgcrypt.so.20 => /lib/x86_64-linux-gnu/libgcrypt.so.20 (0x00007f40833b7000)
	libgpg-error.so.0 => /lib/x86_64-linux-gnu/libgpg-error.so.0 (0x00007f40831a2000)

重新运行

运行成功。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

花花少年

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值