编译器可以通过-Wl将选项传递给链接器ld

例如指定动态库搜索路径,see also ^0d5d11

gcc -Wl,-rpath=./
 
# 等价于
gcc -Wl,rpath,./
 
# 传多个path
gcc -Wl,-rpath=/path1,-rpath=/path2
gcc -Wl,-rpath=/path1 -Wl,-rpath=/path2

上面的命令其实会将这些路径写到elf文件的rpath/runpath字段下。然后运行时会读取这个路径去检索其以来的动态库。

今天(26/7/10)又踩了一个坑。如果你的代码要连接一个外部库A,但是libA.so依然依赖A1, A2, A3. 为了让你的程序(假设叫myapp)正确链接A,你已经知道要在编译选项里面加上

gcc -Wl,-rpath=/path/to/libA

但是对于动态库 libA.so 本身的依赖,诸如 libA1.so, libA2.so 等等,运行时不会再根据 myapp 的 rpath 去搜索它们。表现出来的结果就是,

$ ldd myapp
libA.so => /path/to/libA.so
libA1.so => not found
libA2.so => not found
...

即便libA1.so, libA2.so就和libA.so在同一目录下也不行!这就是动态库的依赖链,顶层程序的 rpath 用于搜索它的一级依赖,一级依赖本身的依赖,会用一级依赖本身的 rpath(而非 myapp 的 rpath)去搜索。而一级依赖本身的 rpath 通常是别人提供的,其目录于你本地目录迥然不同,所以通常是找不到的。这种情况下,只好把所有依赖库全部拷到当前目录(因为别人提供rpath通常带当前目录)。或者设置 LD_LIBRARY_PATH 环境变量,

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/path/to/libA
./myapp

举个例子,

$ ldd libfxcm.so
        linux-vdso.so.1 (0x00007fffa44f3000)
        libForexConnect.so => /home/yychi/code/mdl_src/3rd/libs/forexconnect/libForexConnect.so (0x00007f712be3c000)
        libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f712bc46000)
        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f712bc2b000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f712ba39000)
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f712ba14000)
        libgsexpat.so => not found
        libfxtp.so => not found
        libfxmsg.so => not found
        libgstool3.so => not found
        liblog4cplus.so => not found

libfxcm.so是我自己编出来的,我指定了 rpath 为 /home/yychi/code/mdl_src/3rd/libs/forexconnect. 所以我的直接依赖 libForexconnect.so 可以被找到。但是 libForexconnect.so 本身的那些依赖都是 not found. 可以通过下面的命令查看rpath/runpath,

$ readelf -d libfxcm.so|grep -iE 'rpath|runpath'
 0x000000000000001d (RUNPATH)            Library runpath: [./:./bin:/usr/local/lib:/yychi/code/mdl_src/3rd/libs/forexconnect]
 
$ readelf -d ../../3rd/libs/forexconnect/libForexConnect.so | grep -i rpath
 0x000000000000000f (RPATH)              Library rpath: [.:/usr/local/gehtsoft/jenkins_service/workspace/FXAPI_build_Linux64/CandleWorks/WindowsTradingApplications/Order2Go2/cpp/../../../../3rd_party/expat/lib:/usr/local/gehtsoft/jenkins_service/workspace/FXAPI_build_Linux64/CandleWorks/WindowsTradingApplications/Order2Go2/cpp/../../../../3rd_party/expat:/usr/local/gehtsoft/jenkins_service/workspace/FXAPI_build_Linux64/CandleWorks/WindowsTradingApplications/Order2Go2/cpp/../../../../3rd_party/log4cplus:/usr/local/gehtsoft/jenkins_service/workspace/FXAPI_build_Linux64/CandleWorks/WindowsTradingApplications/Order2Go2/cpp/../../../../3rd_party/zlib:/usr/local/gehtsoft/jenkins_service/workspace/FXAPI_build_Linux64/CandleWorks/WindowsTradingApplications/Order2Go2/cpp/../../../../3rd_party/gehtsoft/gstool3/lib:/usr/local/gehtsoft/jenkins_service/workspace/FXAPI_build_Linux64/CandleWorks/WindowsTradingApplications/Order2Go2/cpp/../../../../FXCM_SRC/fxtc:/usr/local/gehtsoft/jenkins_service/workspace/FXAPI_build_Linux64/CandleWorks/WindowsTradingApplications/Order2Go2/cpp/../../../../FXCM_SRC/Tiramisu/messaging/cpp/fxmsg/lib:/usr/local/gehtsoft/jenkins_service/workspace/FXAPI_build_Linux64/CandleWorks/WindowsTradingApplications/Order2Go2/cpp/../../../../FXCM_SRC/Tiramisu/messaging/cpp/fxtp/lib:/usr/local/gehtsoft/jenkins_service/workspace/FXAPI_build_Linux64/CandleWorks/WindowsTradingApplications/Order2Go2/cpp/../../../../3rd_party/CppEvents/lib:/usr/local/gehtsoft/jenkins_service/workspace/FXAPI_build_Linux64/FXCM_SRC/Tiramisu/messaging/cpp/fxtp/lib:/usr/local/gehtsoft/jenkins_service/workspace/FXAPI_build_Linux64/3rd_party/gehtsoft/gstool3/lib]

可以看到 libfxcm.so 的依赖搜索路径里面已经指定了 /home/yychi/code/mdl_src/3rd/libs/forexconnect,而 libForexconnect.so 在这个目录下1,所以可以被找到。但间接依赖就 not found 了。因为 libForexconnect.so 的 rpath 里没有上面的目录。可以看到那一大串都是别人机器上的目录,所以对本地环境没啥用。还可以看到,他的第一个路径是当前目录!所以拷到同目录或者同目录创建软链接就能搜到了。

小技巧

  1. 可通过 readelf -d myapp | grep -iE 'rpath|runpath' 查看 elf 文件的 rpath.
  2. 可通过 LD_DEBUG=libs ldd myapp 2>&1 | less 查看详细的搜索路径。

可见,查找 libForexconnect.so 时,按照我指定的目录去找了。而找 libfxmsg.so 时,只找了系统目录。如果任选一个 libForexconnect.so 搜索目录,在本机创建之,然后在那个目录下创建软链接,立马就能找到间接依赖。

$ mkdir -p /usr/local/gehtsoft/jenkins_service/workspace/FXAPI_build_Linux64/3rd_party/gehtsoft/gstool3/lib
$ cd /usr/local/gehtsoft/jenkins_service/workspace/FXAPI_build_Linux64/3rd_party/gehtsoft/gstool3/lib
$ ln -s /home/bingbing.hu/code/mdl_src/3rd/libs/forexconnect/libfxmsg.so
$ ll
total 12
drwxr-xr-x 2 root root 4096 Jul 10 15:22 ./
drwxr-xr-x 3 root root 4096 Jul 10 15:22 ../
lrwxrwxrwx 1 root root   64 Jul 10 15:22 libfxmsg.so -> /home/yychi/code/mdl_src/3rd/libs/forexconnect/libfxmsg.so
 
$ ldd libfxcm.so
        linux-vdso.so.1 (0x00007ffe61bfd000)
        libForexConnect.so => /home/yychi/code/mdl_src/3rd/libs/forexconnect/libForexConnect.so (0x00007fc888bc7000)
        libstdc++.so.6 => /lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fc8889d1000)
        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fc8889b6000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fc8887c4000)
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fc88879f000)
        libgsexpat.so => not found
        libfxtp.so => not found
        libfxmsg.so => /usr/local/gehtsoft/jenkins_service/workspace/FXAPI_build_Linux64/3rd_party/gehtsoft/gstool3/lib/libfxmsg.so (0x00007fc8882fc000)
        libgstool3.so => not found
        liblog4cplus.so => not found
        ...

当然,一般还是在当前目录创建软链接更方便,更通用。

Footnotes

  1. 其他二级依赖如 libfxmsg.so 也都在这个目录下。