一、 问题描述
最近工作中遇到了一个问题:项目需要合入其他部门的模块,但是其中的一个共用共享库被更新了。因为项目很大,如果直接在我们的环境中替换更新这个库,很有可能会影响到其他模块。祖传的代码流传了差不多20年,涉及的模块也十分之多,贸然升级的风险很难评估。但是不替换这个库第三方模块又跑不起来,一度头痛。
刚开始是想到了以下几个方法:
- 设置LD\_LIBRARY\_PATH环境变量,修改查找路径的优先级。
- 修改so库名
对于第一种方法是有效的:在程序目录下加个lib目录,然后 export LD_LIBRARY_PATH=
pwd/lib:$LD_LIBRARY_PATH
把当前路径放到第一搜索顺序,能解决这个问题。但是,环境变量时当前所有程序共享的,其他程序的搜索目录也是这里第一,所以这里的结果就和直接升级库没有区别。同样,修改/etc/ld.so.conf的原理也是一样。
第二种方法本来是认为比较靠谱的,但是测试发现共享库并不是根据文件名来的,修改库名无效。so库内部还有个真实的名字,可以通过 readelf -d lib*.so
来查看:
> readelf /usr/lib/libau.so -d
Dynamic section at offset 0x2c88 contains 29 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libdl.so.2]
0x0000000000000001 (NEEDED) Shared library: [libpthread.so.0]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000000e (SONAME) Library soname: [libau.so.2]
所以上面两种方法就被派出了,后面查了很久也没有找到合适的办法,崩溃。。。
最后去请教大佬就被告知了可以使用 -Wl,-rpath
选项来解决这个问题,试了一下确实可以。
二、-Wl,-rpath和-Wl,-rpath-link选项
2.1 -Wl,-rpath
加上 -Wl,-rpath
选项的的作用就是指定“程序运行时”的库搜索目录,是一个链接选项,生效于设置环境变量之前。
我们已经知道,共享库的查找顺序为:
LD_LIBRARY_PATH
环境变量的目录ld.so.conf
高速缓冲文件中的目录- 系统的默认库目录如
/lib, /lib64
等
而 -Wl,-rpath
可以让程序在第一步搜索之前先搜索它所指定的目录,通过一个例子来说明:
// add.h
int add(int i, int j);
// add.c
#include "add.h"
int add(int i, int j) {
return i + j;
}
// main.c
#include <stdio.h>
#include "add.h"
int main() {
printf("1 + 2 = %d\n", add(1, 2));
return 0;
}
add.h和add.c用于生成一个so库,实现了一个简单的加法,main.c中引用共享库计算1 + 2:
# 编译共享库
gcc add.c -fPIC -shared -o libadd.so
# 编译主程序
gcc main.o -L. -ladd -o app
编好后运行依赖库:
> ldd app
linux-vdso.so.1 (0x00007ffeb23ab000)
libadd.so => not found
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007febb7dd0000)
/lib64/ld-linux-x86-64.so.2 (0x00007febb83d0000
> ./app
./app: error while loading shared libraries: libadd.so: cannot open shared object file: No such file or directory
可以看到,libadd.so这个库是没有找到的,程序也无法运行,要运行它必须要把当前目录加到环境变量或者库搜索路径中去。
但是如果在链接的时候加上 -Wl,-rpath
选项之后:
> gcc -Wl,-rpath=`pwd` main.o -L. -ladd -o app
> ldd app
linux-vdso.so.1 (0x00007fff8f4e3000)
libadd.so => /data/code/c/1-sys/solib/libadd.so (0x00007faef8428000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007faef8030000)
/lib64/ld-linux-x86-64.so.2 (0x00007faef8838000)
> ./app
1 + 2 = 3
依赖库的查找路径就找到了,程序能正常运行。
2.2 -Wl,rpath-link
-Wl,rpath-link
是设置编译链接时候的顺序,例如app运行依赖libadd.so,但是libadd.so又依赖libadd\_ex.so,rpath-link
就是指定libadd\_ex.so的路径。和 -Wl,rpath
相比工作的时间不同,一个在链接期间,一个在运行期间。
此处评论已关闭