文章目录
-
- 一、先复现问题
- 二、编译期找库和运行期找库不是一回事
-
- 1. 编译链接阶段
- 2. 程序运行阶段
- 三、用 ldd 查看动态依赖
- 四、动态链接器默认去哪里找库
- 五、解决方案一:拷贝到系统库路径
- 六、解决方案二:在系统路径建立软链接
- 七、解决方案三:设置 LD_LIBRARY_PATH
- 八、解决方案四:配置 ld.so.conf.d 并执行 ldconfig
- 九、解决方案五:编译时写入 rpath
- 十、五种方案对比
- 十一、为什么静态库没有这个问题
- 十二、几个常用排查命令
-
- 1. ldd
- 2. file
- 3. readelf -d
- 4. ldconfig -p
- 十三、一次完整排查流程
- 十四、总结
上一篇我们已经能制作并链接动态库了。很多同学第一次做动态库时,会遇到一个非常典型的问题:
gcc main.c -I./include -L./lib -lmyc -o main
编译链接明明成功了,但一运行:
./main
却报错:
error while loading shared libraries: libmyc.so: cannot open shared object file: No such file or directory
或者用 ldd 查看时看到:
libmyc.so => not found
这个问题非常关键,因为它说明动态库有两个阶段都要“找库”:编译链接阶段要找,程序运行阶段还要再找。本文就专门把这件事讲透。 
一、先复现问题
假设我们的目录结构如下:
shared_lib_demo/
├── include/
│ └── my_math.h
├── lib/
│ └── libmyc.so
└── main.c
main.c 调用了 libmyc.so 中的函数。
编译命令:
gcc main.c -I./include -L./lib -lmyc -o main
这条命令可能成功生成 main。
但是执行:
./main
可能报错:
./main: error while loading shared libraries: libmyc.so: cannot open shared object file: No such file or directory
这时候很多人会困惑:
编译的时候不是已经用 -L./lib 告诉 gcc 库在哪里了吗?
为什么运行的时候还说找不到?
答案是:
-L 只影响编译链接阶段,不负责程序运行阶段。
二、编译期找库和运行期找库不是一回事
动态库有两个重要阶段。
1. 编译链接阶段
命令:
gcc main.c -I./include -L./lib -lmyc -o main
这时参与工作的主要是编译器和链接器。
参数含义:
-I./include 告诉编译器去哪里找头文件
-L./lib 告诉链接器去哪里找库文件
-lmyc 告诉链接器链接 libmyc.so 或 libmyc.a
只要链接器在 ./lib 下找到了 libmyc.so,它就可以生成可执行程序。
2. 程序运行阶段
当你执行:
./main
此时不再是 gcc 在工作,而是操作系统加载程序,并由动态链接器负责加载依赖的 .so。
Linux 下常见动态链接器是:
/lib64/ld-linux-x86-64.so.2
或者 Ubuntu/Debian 上类似:
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
动态链接器并不会自动记住你编译时写过的 -L./lib。所以如果它的搜索路径里没有 ./lib,运行时就找不到 libmyc.so。
这就是问题本质。
三、用 ldd 查看动态依赖
ldd 可以查看一个可执行程序依赖哪些动态库:
ldd ./main
如果库找不到,会看到:
linux-vdso.so.1
libmyc.so => not found
libc.so.6 => /lib64/libc.so.6
/lib64/ld-linux-x86-64.so.2
其中:
libmyc.so => not found
说明可执行程序确实记录了对 libmyc.so 的依赖,但动态链接器在运行搜索路径中没有找到它。
如果库能找到,则可能显示:
libmyc.so => ./lib/libmyc.so
或:
libmyc.so => /usr/local/lib/libmyc.so
所以排查动态库运行问题时,第一步通常就是:
ldd ./main
四、动态链接器默认去哪里找库
动态链接器运行时查找 .so,常见来源包括:
不同系统、不同链接选项下的精确顺序会涉及一些细节。初学阶段先抓住核心:
运行时找库由动态链接器负责,和 gcc 编译时的 -L 不是一套机制。
五、解决方案一:拷贝到系统库路径
最直接的方法是把 .so 拷贝到系统默认库路径,例如:
sudo cp libmyc.so /usr/local/lib/
sudo ldconfig
或者某些系统中使用:
sudo cp libmyc.so /lib64/
sudo ldconfig
这样动态链接器就能从系统路径找到它。
但这个方案不适合学习实验中随便乱用,因为:
所以学习阶段更推荐使用 LD_LIBRARY_PATH 或 rpath。
六、解决方案二:在系统路径建立软链接
如果真实库在某个自定义目录,可以在系统库路径中建立软链接:
sudo ln -s /home/user/mylib/libmyc.so /usr/local/lib/libmyc.so
sudo ldconfig
这样系统路径下看起来有 libmyc.so,实际文件仍在原位置。
软链接方案比直接拷贝更方便维护,但依然需要 root 权限,也需要注意不要污染系统目录。
七、解决方案三:设置 LD_LIBRARY_PATH
学习和临时测试最常用的是:
export LD_LIBRARY_PATH=$PWD/lib:$LD_LIBRARY_PATH
./main
如果库就在当前目录:
export LD_LIBRARY_PATH=$PWD:$LD_LIBRARY_PATH
./main
LD_LIBRARY_PATH 是环境变量,用来告诉动态链接器额外搜索哪些路径。
优点:
缺点:
比如你可以这样验证:
ldd ./main
export LD_LIBRARY_PATH=$PWD/lib:$LD_LIBRARY_PATH
ldd ./main
前后对比 ldd 输出,会看到 libmyc.so 从 not found 变成具体路径。
八、解决方案四:配置 ld.so.conf.d 并执行 ldconfig
更系统化的方式是把库路径加入动态链接器配置。
例如你的库在:
/home/user/mylib/lib
可以创建配置文件:
sudo sh -c 'echo /home/user/mylib/lib > /etc/ld.so.conf.d/myc.conf'
然后执行:
sudo ldconfig
ldconfig 会更新 /etc/ld.so.cache,动态链接器后续可以从缓存中找到这些库。
这种方式适合系统级安装库。
优点:
缺点:
九、解决方案五:编译时写入 rpath
还有一种方式:在生成可执行文件时,把动态库搜索路径写进可执行文件。
例如:
gcc main.c -I./include -L./lib -lmyc -Wl,-rpath=./lib -o main
这里的:
-Wl,-rpath=./lib
意思是把 -rpath=./lib 传给链接器。
这样运行时,动态链接器会根据可执行文件中记录的路径去找 libmyc.so。
可以使用 readelf -d 查看动态段信息:
readelf -d ./main | grep -E "RPATH|RUNPATH|NEEDED"
可能看到:
NEEDED Shared library: [libmyc.so]
RUNPATH Library runpath: [./lib]
这种方案适合把库和程序放在相对固定目录结构中的项目。
十、五种方案对比
| 拷贝到系统库路径 | 持久 | 需要 | 系统安装 | 污染系统目录 |
| 系统路径建软链接 | 持久 | 需要 | 系统安装/版本管理 | 链接错误会影响系统 |
| LD_LIBRARY_PATH | 临时 | 不需要 | 学习、测试、临时运行 | 容易加载错库 |
| ld.so.conf.d + ldconfig | 持久 | 需要 | 正规系统级安装 | 配置影响全局 |
| -Wl,-rpath | 随程序 | 不需要 root | 工程发布、相对路径固定 | 路径写死需谨慎 |
学习阶段建议:
优先 LD_LIBRARY_PATH 或 rpath;
不要随便把实验库丢进系统目录。
十一、为什么静态库没有这个问题
静态库 .a 在链接阶段已经把相关代码合并进可执行程序。
所以链接完成后:
rm libmyc.a
./main
程序仍然能运行。
动态库不同。可执行程序里没有完整库代码,只记录了对 libmyc.so 的依赖。运行时必须能找到 .so。
所以:
静态库问题主要发生在编译链接阶段;
动态库问题既可能发生在编译链接阶段,也可能发生在运行加载阶段。
十二、几个常用排查命令
1. ldd
查看动态依赖:
ldd ./main
重点看有没有:
not found
2. file
查看文件类型:
file ./main
file ./lib/libmyc.so
可以判断是不是 ELF、是不是 shared object。
3. readelf -d
查看动态段:
readelf -d ./main
重点看:
NEEDED
RPATH
RUNPATH
4. ldconfig -p
查看缓存中有哪些库:
ldconfig -p | grep myc
如果你已经配置了 ld.so.conf.d 并执行了 ldconfig,但这里查不到库,就说明配置没有生效。
十三、一次完整排查流程
遇到动态库运行失败时,可以按这个顺序排查:
1. ldd ./main,看是不是 libxxx.so => not found
2. 确认 libxxx.so 是否真实存在
3. 确认编译时 -L 指向的目录是否正确
4. 确认运行时动态链接器能否找到该目录
5. 临时设置 LD_LIBRARY_PATH 验证库本身是否可用
6. 再决定使用 rpath、ldconfig 还是系统安装方案
不要一上来就复制库到 /lib64。先定位问题发生在哪个阶段。
十四、总结
动态库最容易让人困惑的点是:
编译期找库和运行期找库不是一回事。
-L 只告诉链接器编译时去哪找库;程序运行时由动态链接器负责加载 .so,它有自己的搜索路径规则。
常见解决方式包括:
LD_LIBRARY_PATH
ldconfig
-Wl,-rpath
系统库路径
软链接
下一篇开始,我们进入更底层的世界:.o、.so、可执行文件为什么都属于 ELF?链接器到底在这些文件里处理什么?
网硕互联帮助中心



评论前必须登录!
注册