Linux 编译软件常见问题解析
在Linux下编译软件经常碰到各种问题。问题就那么集中。没有动态链接库或者头文件、找不到动态链接库或者头文件或者依赖冲突。下面我介绍一下如何解决此类问题
预备知识
常见命令
ldconfig
主要是在默认搜寻目录/lib和/usr/lib以及动态库配置文件/etc/ld.so.conf内所列的目录下,搜索出可共享的动态链接库(格式如lib.so),进而创建出动态装入程序(ld.so)所需的连接和缓存文件,缓存文件默认为/etc/ld.so.cache,此文件保存已排好序的动态链接库名字列表。linux下的共享库机制采用了类似高速缓存机制,将库信息保存在/etc/ld.so.cache,程序连接的时候首先从这个文件里查找,然后再到ld.so.conf的路径中查找。为了让动态链接库为系统所共享,需运行动态链接库的管理命令ldconfig,此执行程序存放在/sbin目录下
1
-v或–verbose:ldconfig将显示正在扫描的目录及搜索到的动态链接库,还有它所创建的连接的名字
2
-f CONF:此选项指定动态链接库的配置文件为CONF,系统默认为/etc/ld.so.conf
3
-p或–print-cache:此选项指示ldconfig打印出当前缓存文件所保存的所有共享库的名字。
4
-V:此选项打印出ldconfig的版本信息,而后退出。
最常用的命令是 ldconfig
重建缓存文件
ldconfig -p|grep xxx
查找依赖库
ldd
ldd是list, dynamic, dependencies的缩写, 意思是列出动态库依赖关系
动态链接库的选择顺序
编译程序时用到动态库,该从那些地方查找,按照怎么样的顺序查找?
gcc 编译时参数-L指定的路径
环境变量 LIBRARY_PATH
系统默认库位置 /lib /usr/lib
运行程序时需要动态库,该从那些地方查找,按照怎么样的顺序查找?
gcc 编译时指定的运行时库路径 -Wl,-rpath
环境变量 LD_LIBRARY_PATH
ldconfig 缓存 /etc/ld.so.cache
4 系统默认库位置 /lib /usr/lib
LIBRARY_PATH和LD_LIBRARY_PATH环境变量的区别
LIBRARY_PATH和LD_LIBRARY_PATH是Linux下的两个环境变量,二者的含义和作用分别如下:
LIBRARY_PATH环境变量用于在程序编译期间查找动态链接库时指定查找共享库的路径,例如,指定gcc编译需要用到的动态链接库的目录
1 | export LIBRARY_PATH=LIBDIR1:LIBDIR2:$LIBRARY_PATH |
LD_LIBRARY_PATH环境变量用于在程序加载运行期间查找动态链接库时指定除了系统默认路径之外的其他路径,注意,LD_LIBRARY_PATH中指定的路径会在系统默认路径之前进行查找。设置方法如下(其中,LIBDIR1和LIBDIR2为两个库目录)
1 | export LD_LIBRARY_PATH=LIBDIR1:LIBDIR2:$LD_LIBRARY_PATH |
开发时,设置LIBRARY_PATH,以便gcc能够找到编译时需要的动态链接库。
发布时,设置LD_LIBRARY_PATH,以便程序加载运行时能够自动找到需要的动态链接库。
长期使用LD_LIBRARY_PATH
在~/目录下打开.bash_profile文件,设置环境变量如下
1
LD_LIBRARY_PATH=dir:$LD_LIBRARY_PATH
2
export LD_LIBRARY_PATH
/etc/ld.so.conf
链接选项-I,-l,-L,-Wl:rpath
-I,添加包含路径
-I 在编译时用,告诉编译器去哪个路径下找文件
如:-I /home/hello/include
表示将/home/hello/include目录作为第一个寻找头文件的目录。
编译器的寻找顺序是:/home/hello/include–>/usr/include–>/usr/local/include。如果在/home/hello/include中有个文件hello.h,则在程序中用#include<hello.h>就能引用到这个文件。
可以加多个包含路径,编译器的寻找顺序为添加的顺序。
-l,添加引用链接库
-l 在链接时用到,它的作用是告诉链接器,要用到哪个库。
如:-l pthread
告诉链接器(linker),程序需要链接pthread这个库,这里的pthread是库名不是文件名,具体来说文件句是libpthread.so。
-L,添加链接库路径
-L 后跟路径,告诉链接器从哪找库(.so文件),只有在链接时会用到。
如:-L /home/hello/lib
表示将/home/hello/lib目录作为第一个寻找库文件的目录,寻找顺序是:/home/hello/lib–>/usr/lib–>/usr/local/lib。
可以加多个包含路径,链接器的寻找顺序为添加的顺序。
-Wl:rpath,添加运行时库路径
-Wl:rpath 后面也是路径,运行的时候用。这条编译指令会在编译时记录到target文件中,所以编译之后的target文件在执行时会按这里给出的路径去找库文件。
如:-Wl:rpath=/home/hello/lib
表示将/home/hello/lib目录作为程序运行时第一个寻找库文件的目录,程序寻找顺序是:/home/hello/lib–>/usr/lib–>/usr/local/lib。
链接选项和路径
现代连接器在处理动态库时将链接时路径(Link-time path)和运行时路径(Run-time path)分开,用户可以通过-L指定连接时库的路径,通过-R(或-rpath)指定程序运行时库的路径,大大提高了库应用的灵活性。比如我们做嵌入式移植时#arm-linux-gcc $(CFLAGS) –o target –L/work/lib/zlib/ -llibz-1.2.3 (work/lib/zlib下是交叉编译好的zlib库),将target编译好后我们只要把zlib库拷贝到开发板的系统默认路径下即可。或者通过-rpath(或-R )、LD_LIBRARY_PATH指定查找路径。
链接器ld的选项有 -L,-rpath 和 -rpath-link,看了下 man ld,大致是这个意思:
-L: “链接”的时候,去找的目录,也就是所有的 -lFOO 选项里的库,都会先从 -L 指定的目录去找,然后是默认的地方。编译时的-L选项并不影响环境变量LD_LIBRARY_PATH,-L只是指定了程序编译连接时库的路径,并不影响程序执行时库的路径,系统还是会到默认路径下查找该程序所需要的库,如果找不到,还是会报错,类似cannot open shared object file。
-rpath-link:这个也是用于“链接”的时候的,例如你显示指定的需要 FOO.so,但是 FOO.so 本身是需要 BAR.so 的,后者你并没有指定,而是 FOO.so 引用到它,这个时候,会先从 -rpath-link 给的路径里找。
-rpath: “运行”的时候,去找的目录。运行的时候,要找 .so 文件,会从这个选项里指定的地方去找。对于交叉编译,交叉编译链接器需已经配置 –with-sysroot 选项才能起作用。也就是说,-rpath指定的路径会被记录在生成的可执行程序中,用于运行时查找需要加载的动态库。-rpath-link 则只用于链接时查找。
gcc和链接选项的使用
-Wl选项告诉编译器将后面的参数传递给链接器
在gcc中使用ld链接选项时,需要在选项前面加上前缀-Wl(是字母l,不是1),以区别不是编译器的选项。
参考链接
https://github.com/Becivells/linux-compile
https://blog.csdn.net/byxdaz/article/details/89405588