rpath使用说明
大约 3 分钟misclinuxMacOS
rpath是运行时链接器查找动态库的搜索路径。
一般什么时候你会注意到这东西呢?当你的项目依赖了非系统安装的第三方库的时候。
最简单的结构类似:
all: main liba.so
main: main.o liba.so
$(CC) -o $@ $^ -L. -la
liba.so: a.o
$(CC) -o $@ -shared $^
a.o: a.c
$(CC) -c $^ -fPIC
clean:
rm *.o *.so main
编译链接都正常结束,但是当运行的时候就会发现:
./main: error while loading shared libraries: liba.so: cannot open shared object file: No such file or directory
使用ldd查看一下依赖库的情况:
$ ldd main
linux-vdso.so.1 (0x00007ffc73dd7000)
liba.so => not found
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f899a2ce000)
/lib64/ld-linux-x86-64.so.2 (0x00007f899a4a0000)
链接器找不到这个东西,因为它不在系统的搜索路径里。一个办法是使用LD_LIBRARY_PATH,链接器除了默认的系统路径外还会查找找这个路径:
$ LD_LIBRARY_PATH=. ldd ./main
linux-vdso.so.1 (0x00007ffc29db7000)
liba.so => ./liba.so (0x00007f068d9b5000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f068d7ea000)
/lib64/ld-linux-x86-64.so.2 (0x00007f068d9c1000)
但这有点麻烦,每次运行时都要在前面加个LD_LIBRARY_PATH。
好一点的做法是在编译链接时把搜索路径写入目标文件里,运行时链接器就知道到哪去找了,这就是rpath:
main: main.o liba.so
$(CC) -o $@ $^ -L. -la -Wl,-rpath=.
在没加这个选项之前:
$ chrpath -l main
main: no rpath or runpath tag found.
加了这个选项之后:
$ chrpath -l main
main: RUNPATH=.
CMake
在CMake下,情况有点不一样,因为CMake暗地里帮你做了一些事情。
cmake_minimum_required(VERSION 3.1)
project(main)
add_library(a SHARED a.c)
add_executable(main main.c)
target_link_libraries(main a)
# set rpath to install_dir/lib
# set_target_properties(main PROPERTIES INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")
install(TARGETS main a
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
INCLUDES DESTINATION include)
cmake构建出来,会发现main已经带上rpath了,把make的详细指令输出,就能看见cmake把rpath选项设置好了:
$ make VERBOSE=1
# ...
[100%] Linking C executable main
/usr/local/bin/cmake -E cmake_link_script CMakeFiles/main.dir/link.txt --verbose=1
/usr/bin/cc -rdynamic CMakeFiles/main.dir/main.c.o -o main -Wl,-rpath,/home/agan/programming/c/cmake_test/build liba.so
但是,在install的时候又不行了:
$ make install
[ 50%] Built target a
[100%] Built target main
Install the project...
-- Install configuration: ""
-- Installing: /home/agan/programming/c/cmake_test/test/bin/main
-- Set runtime path of "/home/agan/programming/c/cmake_test/test/bin/main" to ""
-- Installing: /home/agan/programming/c/cmake_test/test/lib/liba.so
可以看到,install时把runtime path设为了"",自然又找不到liba.so了。
我找到的一个解决方法是设置CMAKE_INSTALL_RPATH,即把上面set_target_properities的注释打开,再install,会发现这下rpath就设置成功了:
$ make install
[ 50%] Built target a
[100%] Built target main
Install the project...
-- Install configuration: ""
-- Installing: /home/agan/programming/c/cmake_test/test/bin/main
-- Set runtime path of "/home/agan/programming/c/cmake_test/test/bin/main" to "/home/agan/programming/c/cmake_test/test/lib"
-- Installing: /home/agan/programming/c/cmake_test/test/lib/liba.so
macOS
macOS对rpath的概念是一样的,只不过查看的工具以及表示的形式有些区别而已。但是,它多了一个install_name的概念。
每个动态库都有自己的install_name,表示自己安装后的名称。当可执行文件与动态库链接时,会把这个install_name写到自己身上,也就知道这个动态库的位置在哪了。
# install_name of libevent_core
➜ lib otool -D libevent_core.dylib
libevent_core.dylib:
@rpath/libevent_core-2.1.7.dylib
# game link with libevent_core, and log its install_name
➜ bin git:(main) ✗ otool -L game
game:
/usr/local/lib/libpython3.11.dylib (compatibility version 3.11.0, current version 3.11.0)
@rpath/libevent_core-2.1.7.dylib (compatibility version 8.0.0, current version 8.1.0)
@rpath/libevent_extra-2.1.7.dylib (compatibility version 8.0.0, current version 8.1.0)
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 1500.65.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1319.100.3)
# rpath of game
➜ bin git:(main) ✗ otool -l game |grep path
name @rpath/libevent_core-2.1.7.dylib (offset 24)
name @rpath/libevent_extra-2.1.7.dylib (offset 24)
path /Users/agan/programming/github/ZeroServer/test/lib (offset 12)
其中,@rpath就是文件本身记录的rpath的值。