# 编译
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
程序的链接(三):符号和符号表
符号就是其实程序中的变量名、函数名。
注意:局部变量temp分配在栈中,不会在函数外部被引用,因此不是符号定义。
.o .a .so
文件Linux的so文件到底是干嘛的?浅析Linux的动态链接库
链接其实就是把其他第三方库和自己源代码生成的二进制目标文件融合在一起的过程。经过链接之后,那些第三方库中定义的函数就能被调用执行了。早期的一些操作系统一般使用静态链接的方式,现在基本上都在使用动态链接的方式。
虽然静态链接和动态链接都能生成可执行文件,但两者的代价差异很大。下面这张图可以很形象地演示了动态链接和静态链接的区别:
左侧 | 右侧 |
---|---|
动态链接 | 静态链接 |
把精简后的内容带在自己身上,需要什么,运行的时候再去拿 | 把所有依赖的第三方库函数都打包到了一起 |
左侧的人就像是一个动态链接的可执行文件,右侧的海象是一个静态链接的可执行文件。比起人,海象臃肿得多,那是因为静态链接在链接的时候,就把所依赖的第三方库函数都打包到了一起,导致最终的可执行文件非常大。而动态链接在链接的时候并不将那些库文件直接拿过来,而是在运行时,发现用到某些库中的某些函数时,再从这些第三方库中读取自己所需的方法。
so
文件后面往往跟着很多数字,这表示了不同的版本。so
文件命名规则被称为SONAME(简单共享名,Short for shared object name):
libname.so.x.y.z
lib是前缀,这是一个约定俗成的规则。x为主版本号(Major Version),y为次版本号(Minor Version),z为发布版本号(Release Version)。
编译生成.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
文件上。
.o .a .so
文件功能工程里很多函数只是有声明,找不到实现的代码。因为那些实现代码已经编译成库所以看不见,我所看见的全是一堆头文件。.o、.a、.so文件都是Linux下的程序函数库,即编译好的可以供其他程序使用的代码和数据。
优点:程序模块化,容易重新编译,方便升级。
功能 | 对应windows | |
---|---|---|
.a文件 | 静态函数库 | |
.so文件 | 共享目标文件(Shared Object) | 动态链接库(Dynamic Link Library,.dll)文件 |
.o文件 | 目标文件 | .obj文件 |
Linux的动态链接库绝大多数都在/usr/local/lib
,/usr/lib
和/lib
下,操作系统也会默认去这三个路径下搜索动态链接库。
.so文件 | 可执行程序/命令 | |
---|---|---|
文件路径 | /usr/local/lib /usr/lib /lib |
/usr/local/bin |
/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这种版本变化较快,且深度学习程序又高度依赖的库。
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
nm指令用于查询.o .a .so文件的符号信息。
nm 列出.o .a .so中的符号信息
参数 | Value | 解释说明 |
---|---|---|
-I | arm64,arm32,x86_64 | 硬件架构 |
-j | 32 | 设定编译时所用的线程数 |
-A | on、off | 编译AAR包(包含arm32和arm64) |
-d | 无 | 设置该参数,则编译Debug版本,否则编译Release版本 |
-i | 无 | 设置该参数,则进行增量编译,否则进行全量编译 |
###########################################################
# 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 | 解释说明 |
---|---|---|
OBJS | public.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 | |
TARGET | netPrnctl.so |
即时编译(JIT)
Java 编译器生成字节代码(与体系结构无关)后,执行将由 JVM(在 Java 中)处理。 字节码将由加载程序加载到 JVM 中,然后解释每个字节指令。当我们需要多次调用一个方法时,我们需要多次解释相同的代码,这可能比需要花费更多的时间。 因此,我们有了 JIT(即时)编译器。 将字节加载到 JVM(运行时)后,整个代码将被编译而不是解释,从而节省了时间。
JIT 编译器仅在运行时起作用,因此我们没有任何二进制输出。
JIT编译器,中文翻译为“即实编译器”,也称为 动态翻译 或 运行时编译,是一种执行计算机代码的方法。JIT 代表即时(Just-in-Time),这意味着代码将在需要时(而不是在运行时之前)进行编译。JIT编译器可以将字节码编译成机器码(动态编译),而不是解释字节码。代码可以在即将执行时进行编译(因此称为“即时”),然后缓存并在以后重用,无需重新编译。
在Java编程语言和环境中,JIT编译器是一个把Java的字节码转换成可以直接发送给处理器的指令的程序,其中,字节码包括需要被解释的指令的程序。当你写好一个Java程序后,源语言的语句将由Java编译器编译成字节码,而不是编译成与某个特定的处理器硬件平台对应的指令代码(比如,Intel的Pentium微处理器或IBM的System/390处理器)。字节码是可以发送给任何平台并且能在那个平台上运行的独立于平台的代码。
在字节码编译的系统中,源代码被转换为称为字节码的 中间表示形式。把源代码编译成字节码(虚拟机代码),称为字节码编译。字节码不是任何特定计算机的机器代码,可以在计算机体系结构之间移植,然后可以在虚拟机上解释或运行字节码。
静态编译的代码或本地代码在 部署之前 编译,动态编译环境是在 执行期间 可以使用编译器的环境。
问题待验证。
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
# 查找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
如果系统中之前有安装过相同的库,或者存在多个库,就需要确定链接的具体是哪个库。
有一个特殊场景需要注意下,.so 文件中有个默认 rpath 路径,用于搜索被依赖的库,这个路径优先于系统目录和LD_LIBRARY_PATH。假如 rpath 存在相同名字的 .so 文件,会优先加载这个路径的文件。
查找rpapth
readelf -d libSXVideoEngineJni.so | grep rpath
第三方已经编译好的库,在引入了对应的头文件,使用了其中的某个方法,最终链接的时候出现 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*)
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 是十分必要的。
使用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
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 错误
从上面的undefined symbol中,通过c++filt ,可以定位到大多是opencv的问题。
$ c++filt _ZN2cv7waitKeyEi
cv::waitKey(int)
$ c++filt _ZN2cv3maxERKNS_3MatES2_
cv::max(cv::Mat const&, cv::Mat const&)
undefined symbol 问题解决记录
./network: symbol lookup error: /usr/lib64/netPrnctl.so: undefined symbol: cupsGetDests
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库文件一般在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
增加libusb、libcups链接库
LIBVAR = -lusb-1.0 -lcups
LIBPATH = -L/usr/lib64/x86_64-linux-gnu
编译通过。
重新编译之后,显示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)
运行成功。