编译器可以通过-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 依然依赖 A 1, A 2, A 3. 为了让你的程序(假设叫 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
...即便 libA 1.so, libA 2.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 foundlibfxcm.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 里没有上面的目录。可以看到那一大串都是别人机器上的目录,所以对本地环境没啥用。还可以看到,他的第一个路径是当前目录!所以拷到同目录或者同目录创建软链接就能搜到了。
小技巧
- 可通过
readelf -d myapp | grep -iE 'rpath|runpath'查看 elf 文件的 rpath. - 可通过
LD_DEBUG=libs ldd myapp 2>&1 | less查看详细的搜索路径。
查找示例
$ LD_DEBUG=libs ldd libfxcm.so 2>&1|less ...... 877765: find library=libForexConnect.so [0]; searching 877765: search path=./tls/haswell/x86_64:./tls/haswell:./tls/x86_64:./tls:./haswell/x86_64:./haswell:./x86_64:.:./bin/tls/haswell/x86_64:./bin/tls/haswell:./bin/tls/x86_64:./bin/tls:./bin/haswe ll/x86_64:./bin/haswell:./bin/x86_64:./bin:/usr/local/lib/tls/haswell/x86_64:/usr/local/lib/tls/haswell:/usr/local/lib/tls/x86_64:/usr/local/lib/tls:/usr/local/lib/haswell/x86_64:/usr/local/lib/haswell:/usr/local/lib/x86_64:/usr/local/lib:/home/yychi/code/mdl_src/3rd/libs/forexconnect/tls/haswell/x86_64:/home/yychi/code/mdl_src/3rd/libs/forexconnect/tls/haswell:/home/yychi/code/mdl_src/3rd/libs/forexconnect/tls/x86_64:/home/yychi/code/mdl_src/3rd/libs/forexconnect/tls:/home/yychi/code/mdl_src/3rd/libs/forexconnect/haswell/x86_64:/home/yychi/code/mdl_src/3rd/libs/forexconnect/haswell:/home/yychi/code/mdl_src/3rd/libs/forexconnect/x86_64:/home/yychi/code/mdl_src/3rd/libs/forexconnect (RUNPATH from file ./libfxcm.so) 877765: trying file=./tls/haswell/x86_64/libForexConnect.so 877765: trying file=./tls/haswell/libForexConnect.so 877765: trying file=./tls/x86_64/libForexConnect.so 877765: trying file=./tls/libForexConnect.so 877765: trying file=./haswell/x86_64/libForexConnect.so 877765: trying file=./haswell/libForexConnect.so 877765: trying file=./x86_64/libForexConnect.so 877765: trying file=./libForexConnect.so 877765: trying file=./bin/tls/haswell/x86_64/libForexConnect.so 877765: trying file=./bin/tls/haswell/libForexConnect.so 877765: trying file=./bin/tls/x86_64/libForexConnect.so 877765: trying file=./bin/tls/libForexConnect.so 877765: trying file=./bin/haswell/x86_64/libForexConnect.so 877765: trying file=./bin/haswell/libForexConnect.so 877765: trying file=./bin/x86_64/libForexConnect.so 877765: trying file=./bin/libForexConnect.so 877765: trying file=/usr/local/lib/tls/haswell/x86_64/libForexConnect.so 877765: trying file=/usr/local/lib/tls/haswell/libForexConnect.so 877765: trying file=/usr/local/lib/tls/x86_64/libForexConnect.so 877765: trying file=/usr/local/lib/tls/libForexConnect.so 877765: trying file=/usr/local/lib/haswell/x86_64/libForexConnect.so 877765: trying file=/usr/local/lib/haswell/libForexConnect.so 877765: trying file=/usr/local/lib/x86_64/libForexConnect.so 877765: trying file=/usr/local/lib/libForexConnect.so 877765: trying file=/home/yychi/code/mdl_src/3rd/libs/forexconnect/tls/haswell/x86_64/libForexConnect.so 877765: trying file=/home/yychi/code/mdl_src/3rd/libs/forexconnect/tls/haswell/libForexConnect.so 877765: trying file=/home/yychi/code/mdl_src/3rd/libs/forexconnect/tls/x86_64/libForexConnect.so 877765: trying file=/home/yychi/code/mdl_src/3rd/libs/forexconnect/tls/libForexConnect.so 877765: trying file=/home/yychi/code/mdl_src/3rd/libs/forexconnect/haswell/x86_64/libForexConnect.so 877765: trying file=/home/yychi/code/mdl_src/3rd/libs/forexconnect/haswell/libForexConnect.so 877765: trying file=/home/yychi/code/mdl_src/3rd/libs/forexconnect/x86_64/libForexConnect.so 877765: trying file=/home/yychi/code/mdl_src/3rd/libs/forexconnect/libForexConnect.so 877765: ...... 877765: find library=libfxmsg.so [0]; searching 877765: search path=./tls/haswell/x86_64:./tls/haswell:./tls/x86_64:./tls:./haswell/x86_64:./haswell:./x86_64:. (RUNPATH from file ./libfxcm.so) 877765: trying file=./tls/haswell/x86_64/libfxmsg.so 877765: trying file=./tls/haswell/libfxmsg.so 877765: trying file=./tls/x86_64/libfxmsg.so 877765: trying file=./tls/libfxmsg.so 877765: trying file=./haswell/x86_64/libfxmsg.so 877765: trying file=./haswell/libfxmsg.so 877765: trying file=./x86_64/libfxmsg.so 877765: trying file=./libfxmsg.so 877765: search path= (RPATH from file /home/yychi/code/mdl_src/3rd/libs/forexconnect/libForexConnect.so) 877765: search cache=/etc/ld.so.cache 877765: search path=/lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu:/lib:/usr/lib (system search path) 877765: trying file=/lib/x86_64-linux-gnu/libfxmsg.so 877765: trying file=/usr/lib/x86_64-linux-gnu/libfxmsg.so 877765: trying file=/lib/libfxmsg.so 877765: trying file=/usr/lib/libfxmsg.so
可见,查找 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
...当然,一般还是在当前目录创建软链接更方便,更通用。
Update(2026/11/09)
今天又遇到了同样的问题,但是借助 AI 又学到了一点。简单说就是,
Tip
自己在编动态库时,一定要加上
.和$ORIGIN的 rpath.
我们已经知道,当加载动态库的时候,A 依赖 B,B 依赖 C,A 的 runpath 中有 B 和 C,B 的 runpath 中却没有 C,这样还是会出现 not found 的情况。具体查找步骤如下,
- 检测到 A 依赖 B,读取 A 的 runpath
- 去 A 的 runpath 中搜索 B,找到了
- 检测到 B 依赖 C(但 A 并不直接依赖 C)
- 读取 B 的 runpath,去 B 的 runpath 中依次搜索 C(注意:此时并不会再看 A 的 runpath),没找到
- C not found
最后造成,
$ ldd A
B => /path/to/B.so
C => not found也就是说“我依赖的依赖,不是我的依赖,所以不会按照我给的依赖路径去找”。每个动态库只负责表明自己的直接依赖去哪找!
此时,如果 B 的 runpath 中能找到 C,自然是万事大吉。一般 B 的 runpath 都会带上 . (当前目录),所以 B 和 C 放在同目录时,通常是能找到的。从这里看到,动态库的搜索路径最好包含当前目录(即该库所在的目录)。
那 $ORIGIN 是干嘛的呢?其实它比 . 更灵活,会根据库文件所在的目录灵活展开。例如 libB.so 在目录 /path/to/lib 下,那么当读取 libB.so 的 runpath 时,会自动加一条 /path/to/lib. 假如换一台机器,或这把库文件移动到其他目录,那么 runpath 也会根据 libB.so 所在目录的真实路径展开,是不是非常方便。
cmake 中设置 $ORIGIN 还值得一提,
target_link_options(my_share_lib PRIVATE -Wl,-rpath=.)
target_link_options(my_share_lib PRIVATE "SHELL:-Wl,-rpath=\$ORIGIN")因为 CMake 对 target_link_options 的普通参数做了去重和重排,会破坏链接器选项的语义。而 SHELL: 前缀告诉 CMake:“这整行是一个 shell 命令片段,别拆,别排,原样传递。”
Footnotes
-
其他二级依赖如 libfxmsg.so 也都在这个目录下。 ↩