动态库链接的优先级以及如何修改动态库链接
最近工作中遇到了一个问题,启动nginx
时,链接动态库的路径与预想的路径不同,应该链接同目录下的动态库,但是却链接了系统中同名但不同版本的库,导致无法启动,在经过查询与多方案尝试后,最终解决了该问题,将解决过程与大家分享一下
首先,先了解一下动态库的链接优先级,在运行可执行文件时,可能会链接一些动态库,系统在链接这些动态库时会有一个顺序,下面按优先级顺序说一下各链接方式
一、动态库链接的优先级
rpath
全称是run-time search path
。Linux下所有elf格式的文件都包含它,特别是可执行文件。它规定了可执行文件在寻找.so
文件时的第一优先位置
rpath
一般可以在编译时指定,在Linux下,执行./configure
时就有-Wl,-rpath,
选项,可以将rpath
的路径指定到所需动态库的路径下
-wl
这是个gcc
的参数,表示编译器会将后面的参数传递给链接器ld
rpath
只能是绝对路径,不能是相对路径。使用相对路径是没有意义的,因为相对路径将相对于运行程序时的当前工作目录,而不是相对于找到二进制文件/库的目录。 因此,在几乎任何情况下,它对于$PATH
或库中的可执行文件都根本不起作用
比如你的可执行文件在
/usr/bin/exe
,动态库在/usr/lib/
路径下,rpath
被指定为../lib
,那么只有进入/usr/bin/
路径下执行exe
时,才能找到正确的动态库,在其他目录下调用该程序就无法定位到正确的动态库路径下
修改环境变量的方式
#修改环境变量是第二优先级,在没有指定rpath的情况下,则会寻找该路径
export LD_LIBRARY_PATH=LDpath
#以下命令可以查看目前的环境变量路径是什么
echo $LD_LIBRARY_PATH
- 修改环境变量不是永久性的。只在执行
export LD_LIBRARY_PATH=LDpath
命令的当前bash
有效,在其他的bash
中则不起效果
由于该原因,
export
命令在脚本中执行是无效的,因为在执行shell
脚本时,是先创建一个子进程shell
,然后运行脚本程序,执行结束后子进程退出,返回父进程shell,因此子shell中对环境变量的改变并不影响父进程看到的环境变量。
如果脚本中包含export LD_LIBRARY_PATH
,那么执行脚本时应该使用source
,比如source test.sh
- 如果想永久性修改环境变量,可以选择以下方案
#需要在root下执行
vi + /etc/profile
将export LD_LIBRARY_PATH=LDpath语句追加到打开的profile文件中
保存后重启虚拟机,这样的修改方式为永久修改
#需要在root下执行
vi /etc/ld.so.conf
将需要添加的动态库路径追加到该文件中,保存后退出,执行以下命令让修改生效
/sbin/ldconfig
优先级最低的是系统默认的路径,一般为/lib
,/usr/lib
,/usr/local/lib
等系统库的路径
二、如何修改动态库链接
#查看程序的动态库链接
ldd application
#查看rpath
readelf -d XXX
在修改动态库链接路径时,应该按照上文的优先级顺序进行修改,第一方案就是修改可执行文件的rpath
,有以下几种方案供大家参考
- 下载源码重新编译该程序,在编译时执行
./configure
时-Wl,-rpath=LDpath,
可以设置多个rpath
路径,多个路径之间用冒号:
隔开,优先级为路径书写顺序 - 使用
chrpath
工具
##### 安装 #####
apt-get install chrpath
#删除编译时的rpath
chrpath -d application
#修改rpath为新路径
########### 注意,该工具只能修改为比原来路径短或者长度一样的rpath #############
chrpath -r "/usr/local/lib/" application
-r <path> | --replace <path>
Replace current rpath or runpath setting with the path given.
The new path must be shorter or the same length as the current path.
#可使用man chrpath查看各参数作用
这个工具一般都能在线安装成功,如果安装失败,在这里提供离线包供大家使用chrpath离线包,解压后进入目录,./configure && make
即可在当前目录生成chrpath
可执行文件
- 使用
patchelf
工具
##### 安装 #####
apt-get install patchelf
# 或者下面的安装方式
wget http://nixos.org/releases/patchelf/patchelf-0.8/patchelf-0.8.tar.bz2
tar -zxf patchelf-0.8.tar.bz2 && cd patchelf-0.8
./configure --prefix=/usr
make && make install
##### 使用 #####
#修改RUNPATH
patchelf --set-rpath /usr/local/lib/ application
--set-rpath RPATH
Change the RPATH of the executable or library to RPATH.
#可使用man patchelf查看各参数作用
如果wget
下载失败,可以在这边下载patchelf工具
- 该工具可以修改更长的路径,也可以为没有
rpath
的程序直接添加上RUNPATH
patchelf
工具修改的可执行文件的RUNPATH
,我没太搞懂rpath
和RUNPATH
的区别,查看patchelf
工具参数作用时,描述的是修改RPATH
;但是使用readelf -d XXX
命令查看修改后的可执行文件时,显示的为RUNPATH
,并且chrpath
工具中有一个-c
参数的描述为
-c | --convert Convert the rpath setting into a runpath setting
所以两者应该还是略有区别,下面贴上我搜索的两者区别的链接,大家可以参考一下
- 接下来的方案就不用具体说了,上面优先级中已经有描述,可以修改环境变量,或者修改
ldconfig
来修改动态库的链接路径
全文为自己查询并尝试过的方案以及自己的理解,所以会有一些理解不全或者错误的地方,大家参考即可,如发现有误请指出,谢谢;如觉得有用,在标明出处后可随意转载