Skip to content

如何修改动态链接优先级

关键词: 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.soB.so

readelf -d xxx | grep NEED 输出的是这个程序【直接需要】的动态链接文件,例子中为 A.so

所以这里不行的原因是 libQt5Test.so 不是直接需要的动态链接文件。patchelf 本质上修改的是 .dynsym 表,所以它查找发现没有 libQt5Test,就没有任何操作。

利用 R_PATH 和 RUN_PATH 指定程序查找的动态链接路径

所以现在情况是需要让程序动态链接到 understandlibQt5Test, 而不是 /usr/lib 中. 由于上述原因, 直接利用 patchelf 修改 understand 的库文件路径是无效的, 所以需要找到 understand 动态链接中所有需要的库文件, 都去把他们的路径修改.

但是这个问题是, 每一个库文件都要找到那些依赖的文件, 然后再 patchelf 修改. 能否直接修改他们的【动态链接路径】呢? 也就是查找动态链接库时, 让 ./understand 文件夹比 /usr/lib 文件夹更高.

这里就是【第二个知识点】: R_PATHRUN_PATH 的作用, 以及 patchelf 修改它们的注意点

R_PATHRUN_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

但还是修改 understandR_PATH 还是不行, 为什么? 实际上还是之前那个原因: 虽然 understand 需要 libA.so 时确实从我设置的 R_PATH 中找, 但是如果 libA.so 还需要 libB.so 时, 此时是从 libA.soR_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_PATHunderstand 文件夹在 /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

Comments