知识点

现代连接器在处理动态库时将链接时路径(Link-time path)和运行时路径(Run-time path)分开,用户可以通过-L指定连接时库的路径,通过-R(或-rpath)指定程序运行时库的路径,大大提高了库应用的灵活性。
链接器ld的选项有 -L,-rpath 和 -rpath-link,看了下 man ld,大致是这个意思:

-L: “链接”的时候,去找的目录,也就是所有的 -l 选项里的库,都会先从 -L 指定的目录去找,然后是默认的地方。编译时的-L选项并不影响环境变量LD_LIBRARY_PATH,-L只是指定了程序编译连接时库的路径,并不影响程序执行时库的路径,系统还是会到默认路径下查找该程序所需要的库,如果找不到,还是会报错,类似cannot open shared object file。

-rpath-link:这个也是用于“链接”的时候的,例如你显示指定的需要 FOO.so,但是 FOO.so 本身是需要 BAR.so 的,后者你并没有指定,而是 FOO.so 引用到它,这个时候,会先从 -rpath-link 给的路径里找。

-rpath: “运行”的时候,去找的目录。运行的时候,要找 .so 文件,会从这个选项里指定的地方去找。对于交叉编译,交叉编译链接器需已经配置 –with-sysroot 选项才能起作用。也就是说,-rpath指定的路径会被记录在生成的可执行程序中,用于运行时查找需要加载的动态库。

知识点2

-Wa,Pass comma-separated on to the assembler
-Wp,Pass comma-separated on to the preprocessor
-Wl,Pass comma-separated on to the linker

the-rpathoption encodes the path in the binary, either asDT_RPATHorDR_RUNPATH

通常,您不需要它,事实上,最好不要在可执行文件中对库搜索路径进行编码(使用-rpath选项将路径编码为二进制,可以是DT_rpath或DR_RUNPATH)

注:我自己的一般方法是在可执行文件位于构建树中,并依赖于构建树中的其他库时,将其与–rpath选项链接,以便于调试,但在安装(make install,building packages)时,需要在不使用–rpath选项的情况下重新链接,并将查找共享库的任务留给目标平台的适当动态链接器配置,例如ld.so.conf。

知识点3

binutils在2.22版本以后,默认把–no-copy-dt-needed-entries这个选项打开了。当打开了这个选项的时候,编译器在链接的时候是不会递归的去获取依赖动态库的依赖项的,于是就会出现上述的问题。

–copy-dt-needed-entries
–no-copy-dt-needed-entries
This option affects the treatment of dynamic libraries referred to by DT_NEEDED tags inside ELF dynamic libraries mentioned on the command line. Normally the linker won’t add a DT_NEEDED
tag to the output binary for each library mentioned in a DT_NEEDED tag in an input dynamic library. With –copy-dt-needed-entries specified on the command line however any dynamic
libraries that follow it will have their DT_NEEDED entries added. The default behaviour can be restored with –no-copy-dt-needed-entries.

This option also has an effect on the resolution of symbols in dynamic libraries. With –copy-dt-needed-entries dynamic libraries mentioned on the command line will be recursively
searched, following their DT_NEEDED tags to other libraries, in order to resolve symbols required by the output binary. With the default setting however the searching of dynamic
libraries that follow it will stop with the dynamic library itself. No DT_NEEDED links will be traversed to resolve symbols.
跟在–no-copy-dt-needed-entries它后面的库都不会遍历其依赖项,使用–copy-dt-needed-entries则相反。即可以遍历所有依赖项目,保证编译成功。

实例

//a.cc == main.cc#include #include "b.h"int main(){bbb();std::cout << "Hello world" << std::endl;return 0;}//b.h-----#pragma oncevoid bbb();//b.cc-----#include #include "ccc.h"void bbb(){ccc();std::cout << "Hello world" << std::endl;}// ccc.h-----#pragma oncevoid ccc();// ccc.cc-----#include void ccc(){std::cout << "Hello world" << std::endl;}

编译过程1

$ g++ -fPIC -shared ccc.cc -o libccc.so$ g++ -fPIC -shared b.cc -o libbbb.so -L. -lccc# g++ a.cc -L. -lb 是不行的,具体看https://blog.csdn.net/zrq293/article/details/105969423g++ a.cc -L. -lb -Wl,--copy-dt-needed-entries/usr/bin/ld: warning: libccc.so, needed by ./libb.so, not found (try using -rpath or -rpath-link)$ g++ a.cc -L. -lb -Wl,--copy-dt-needed-entries,-rpath-link=.$ ./a.out./a.out: error while loading shared libraries: libb.so: cannot open shared object file: No such file or directory$ g++ a.cc -L. -lb -Wl,--copy-dt-needed-entries,-rpath-link=.,-rpath=.tiantian@DESKTOP-UVN3KRD:~/code$ ./a.out./a.out: error while loading shared libraries: libccc.so: cannot open shared object file: No such file or directory$ ldd a.outlinux-vdso.so.1 (0x00007ffff5585000)libb.so => ./libb.so (0x00007f44961f0000)libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f4495fc0000)libc.so => ./libc.so (0x00007f4495fb0000)libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f4495d80000)libccc.so => not foundlibm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f4495c80000)/lib64/ld-linux-x86-64.so.2 (0x00007f4496215000)libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f4495c60000)$ export LD_LIBRARY_PATH=.$ ./a.outHello worldHello worldHello world

cmake的扩展

默认cmake设置

set(CMAKE_SKIP_BUILD_RPATH FALSE) # 编译时加上RPATH
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) # 编译时RPATH不使用安装的RPATH
set(CMAKE_INSTALL_RPATH “”)# 安装RPATH为空
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE) # 安装的执行文件不加上RPATH
设置rpath

SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
SET(CMAKE_INSTALL_RPATH “\${ORIGIN}/../lib”)

  1. 默认情况下,这个PATH在构建产生的中间文件,是不增加的;而是只对安装的目标有效。如果你想针对构建的中间文件也有效果,可以加上
  2. 手动指定添加的RPATH