如何修改动态链接优先级
关键词: RUN_PATH, R_PATH, LD_LIBRARY_PATH
understand 是一个代码阅读器,偶然在知乎上看到有好几个人推荐,据说比 Vim+Ctag
还要好。想尝尝看,结果就安装了一个下午。
version xxx
not found
从官网中下载后,解压后是一个已经编译好的文件夹,里面有一些 libxxx.so
文件和最终运行的 understand 程序。
understand 已经动态链接到这些 libxxx.so
了,程序还依赖这里面没有的库文件,如 libc.so
,这些程序是动态链接到 /usr/lib/libyyy.so
中。understand 运行出了如下错误。
./understand: /home/allen/understand/bin/linux64/libQt5Core.so.5: version `Qt_5.15' not found (required by /usr/lib/libQt5Test.so.5)
./understand: /home/allen/understand/bin/linux64/libQt5Core.so.5: version `Qt_5.15' not found (required by /usr/lib/libQt5QuickWidgets.so.5)
这个以前遇过,当时是 openssl 有问题,解决方法是下载旧版本,然后用 patchelf
链接到那个旧版本。也就是上述的 /usr/lib
中的库文件比 understand
中的库文件要新.
patchelf 失效原因
问题好像很容易, 但是上面的错误中, 引发问题的 libQt5Test
这个文件明明在 understand
文件夹中, 但动态链接却在 /usr/lib
中, 非常奇怪.
尝试 patchelf --replace-needed libQt5Test.so.5 xxx/libQt5Test.so.5 understand
,但是发现这条语句没有效果!
这是为什么??这里就是【第一个知识点】: 【ldd 和 readelf 区别】。
假设程序需要
A.so
,而加载A.so
需要B.so
。
ldd
输出的是这个程序【所有需要】的动态链接文件,例子中为A.so
和B.so
。
readelf -d xxx | grep NEED
输出的是这个程序【直接需要】的动态链接文件,例子中为A.so
。
所以这里不行的原因是 libQt5Test.so
不是直接需要的动态链接文件。patchelf
本质上修改的是 .dynsym 表,所以它查找发现没有 libQt5Test
,就没有任何操作。
利用 R_PATH 和 RUN_PATH 指定程序查找的动态链接路径
所以现在情况是需要让程序动态链接到 understand
的 libQt5Test
, 而不是 /usr/lib
中. 由于上述原因, 直接利用 patchelf
修改 understand
的库文件路径是无效的, 所以需要找到 understand
动态链接中所有需要的库文件, 都去把他们的路径修改.
但是这个问题是, 每一个库文件都要找到那些依赖的文件, 然后再 patchelf
修改. 能否直接修改他们的【动态链接路径】呢? 也就是查找动态链接库时, 让 ./understand
文件夹比 /usr/lib
文件夹更高.
这里就是【第二个知识点】: R_PATH
和 RUN_PATH
的作用, 以及 patchelf
修改它们的注意点
R_PATH
和RUN_PATH
就是指明动态链接文件夹的位置,前者比后者优先级更高,即前者如果存在就不用找后者了。
patchelf
修改两者有细微区别。
# 查看 R_PATH 和 RUN_PATH
readelf -d understand | grep 'R*PATH'
# 设置 RUN_PATH
patchelf --set-rpath xxxx/lib understand
# 设置 R_PATH
patchelf --force-rpath --set-rpath xxxx/lib understand
但还是修改 understand
的 R_PATH
还是不行, 为什么? 实际上还是之前那个原因: 虽然 understand
需要 libA.so
时确实从我设置的 R_PATH
中找, 但是如果 libA.so
还需要 libB.so
时, 此时是从 libA.so
的 R_PATH
中去找了, 所以失败。
因此强制所有涉及到的动态库文件的 RPATH
都改变, 这样就 OK 了, 具体实现看下面, 不过还有更好的做法.
# 解压后的文件夹有些 libxxx.so 是不需要使用的
mkdir libQt && mv libQt*.so libQt
cd libQt && patchelf --force-rpath --set-rpath xxxx/libQt *
cd .. && patchelf --force-rpath --set-rpath xxxx/libQt understand
利用 LD_LIBRARY_PATH 指定程序查找的动态链接路径
实际上根本不要这么麻烦, Linux 中已经有了 LD_LIBRARY_PATH
来负责这个问题.【第三个知识点】
LD_LIBRARY_PATH
这个变量是由多个路径组成, 表示动态链接去哪个文件夹找
LD_LIBRARY_PATH
,RPATH
,RUNPATH
优先级是什么呢?【非常重要】
- 如果RUNPATH
为空,那么为RPATH
,LD_LIBRARY_PATH
- 如果RUNPATH
不空,那么为LD_LIBRARY_PATH
,RUNPATH
,此时RPATH
相当于没有用了
所以解决方法就很清楚,只要让 LD_LIBRARY_PATH
中 understand
文件夹在 /usr/lib
文件夹前面就行
# 为了清晰一点,我把 understand 中的 libxxx.so 放到 libQt 文件夹中
mkdir libQt && mv libQt*.so libQt
LD_LIBRARY_PATH=./libQt:$LD_LIBRARY_PATH ./understand
[1] https://blog.csdn.net/zhanghm1995/article/details/106474505