在使用 c 或 c++ 开发应用时,在启动程序时,有时会遇到这个错误,找不到动态库。这个时候,我们使用 ldd 来查看,发现可执行文件依赖的动态库显示为 not found。
1 实验代码
使用如下 3 个文件做实验。
hello.h 中声明了函数 say_hello()。
hello.c 中实现了函数 say_hallo(), 在函数中打印 hello,hello.c 编译出一个动态库。
main.c 中调用 hello.c 中的函数 say_hello(),main.c 编译出可执行文件。
hello.h:
void say_hello();
hello.c
#include <stdio.h>
#include "hello.h"void say_hello() {printf("hello\n");
}
main.c
#include "hello.h"int main() {say_hello();return 0;
}
使用 gcc -fPIC -shared hello.c -o libhello.so 编译出动态库,使用 gcc main.c libhello.so 编译可执行文件,编译之后的文件目录如下,libhello.so 是动态库,a.out 是可执行文件。
这个时候直接执行 a.out 却无法执行,打印错误信息是找不到动态库。
除了使用 ldd 查看文件依赖的动态库之外,还可以使用 objdump 或 readelf 来查看可执行文件依赖的动态库。
objdump -x a.out |grep NEED
readelf -d a.out |grep NEED
2 动态库路径配置
有几种方式可以配置动态库的路径,这几种方式都是可行的,其中 LD_LIBRARY_PATH 是工作中最常使用的。
2.1 LD_LIBRARY_PATH
工作中,我们常用的是设置一个环境变量 LD_LIBRARY_PATH,将依赖的动态库路径追加到这个环境变量中,可执行文件就可以执行了。在执行可执行文件时,linux 加载器会从这个环境变量所包含的路径中找动态库。
如下图所示,将 libhello.so 追加到环境变量,使用 ldd 查看 a.out 依赖的库,也不会显示 not found 了,执行 a.out 也可以正确执行。
2.2 /etc/ld.so.conf.d/
先看看这个目录下,默认有什么文件。可以看到,有 3 个 .conf 文件,文件中的内容分别是 /usr/local/lib,/lib/x86_64-linux-gnu、/usr/lib/x86_64-linux-gnu,/usr/lib/x86_64-linux-gnu/libfakeroot。这个目录下配置的路径下的动态库会被查找到。
在该目录下配置的路径下的动态库,可以通过 ldconfig -p 查看到。
如果要把本文中的 libhello.so 也加入到这个目录下,操作如下:
(1)首先在目录下增加一个 .conf 文件,文件名没有要求,只要不重复就可以,文件里写入 libhello.so 所在的目录
(2)执行 ldconfig,添加配置之后执行 ldconfig 才会生效
执行完两个步骤之后,使用 ldconfig -p 就可以看到 libhello.so 出现在缓存里了。
2.3 编译时添加路径
在编译的时候使用 -Wl,rpath 来添加动态库路径。其中 rpath 是 run path 的意思,使用 rpath 指定的路径,在加载时会到这个路径下查找动态库。
gcc main.c -lhello -L. -Wl,-rpath /home/wyl/test/libtest/
使用上边的命令编译之后,使用 ldd 查看 a.out 依赖的动态库,能够找到 libhello.so。
使用 objdump 可以看到,这个路径被直接写到了 a.out 文件中。
3 不同配置下的动态库查找顺序
LD_DEBUG 可以用来查看链接和加载过程的信息,用来调试。LD_DEBUG 可以赋不同的值,不同的值可以查看不同的调试信息。
我们使用 libs 来显示库的加载信息。在实验之前,做如下工作:
(1)编译时使用 rpath 选项
这样的话,就会将 /home/wyl/test/libtest/ 写到编译的目标文件中
gcc main.c -lhello -L. -Wl,-rpath /home/wyl/test/libtest/
(2)配置 LD_LIBRARY_PATH
export LD_LIBRARY_PATH=/home/wyl/test/libtest/lib1:$LD_LIBRARY_PATH
(3)配置 /etc/ld.so.conf.d/hello.conf
这样的话,3 种方式都做了配置,但是 3 个方式配置的路径是不一样的。同时我们只让 libhello.so 存在于 /home/wyl/test/libtest/lib2 中,另外两个目录下不放 libhello.so。
使用 LD_DEBUG 的打印信息如下,从打印信息可以看出来:
(1)首先查找的路径是 LD_LIBRARY_PATH 中的路径
(2)LD_LIBRARY_PATH 路径找不到,从 rpath 选项中的路径查找
(3)rpath 路径找不到,则从 /etc/ld.so.cache 中查找,该文件中保存的就是配置在 /etc/ld.so.conf.d/ 中配置的目录下的动态库