通俗理解-L、-rpath和-rpath-link编译链接动态库
一、参考资料
链接选项 rpath 的应用和原理 | BewareMyPower的博客
使用 rpath 和 rpath-link 确保 samba-util 库正确链接-CSDN博客
编译参数-Wl和rpath的理解_-wl,-rpath-CSDN博客
Using LD, the GNU linker - Options
Directory Options (Using the GNU Compiler Collection (GCC))
交叉编译时–sysroot,-rpath,-rpath-link,-L之间的关系与注意点_交叉编译 sysroot-CSDN博客
二、相关介绍
Linux 动态库查找路径
一个典型的 C/C++ 程序的构建流程是:预处理,汇编,编译,链接。而执行链接的程序其实是 ld
,通常编译器比如 GCC 都会自动调用 ld
去进行链接,用户不必关注其中的细节。而 ld
查找动态库的顺序是:
rpath
指定的目录;- 环境变量
LD_LIBRARY_PATH
指定的目录; runpath
指定的目录;/etc/ld.so.cache
缓存文件,通常包含/etc/ld.so.conf
文件编译出的二进制俩别哦(比如 CentOS 上,该文件会使用 include 从而使用ld.so.conf.d
目录下面所有的*.conf
文件,这些都会缓存在 ld.so.cache 中)- 系统默认路径,比如
/lib
,/usr/lib
。
在编译时若使用 -z nodefaultlib
选项编译,则会跳过 4 和 5。至于 runpath,和 rpath 类似,都是二进制(ELF)文件的动态 section 属性(分别为 DT_RUNPATH
和 DT_RPATH
),唯一区别就是是否优先于 LD_LIBRARY_PATH
来查找。
rpath
vs.runpath
:rpath 和 runpath 是嵌入在可执行文件或共享库中的路径列表,用于指定运行时查找共享库的位置。rpath 是旧标准,runpath 是新标准,功能类似但优先级不同。
-Wl
参数
gcc的-Wl,xxx
选项将逗号分隔的标记列表(flags)作为空格分隔的参数列表传递给链接器,即:
gcc -Wl,aaa,bbb,ccc
最终变成了linker的用法:
ld aaa bbb ccc
如果是想把ld -rpath
通过-Wl
传递给gcc,可以是-Wl,-rpath,xxx
,也可以指定-Wl的重复实例:
gcc -Wl,aaa -Wl,bbb -Wl,ccc
类似的参数还有:
-Wa,<options> Pass comma-separated <options> on to the assembler
-Wp,<options> Pass comma-separated <options> on to the preprocessor
-Wl,<options> Pass comma-separated <options> on to the linker
--sysroot
选项
--sysroot = dir
将dir作为逻辑root目录,用于搜索头文件和依赖库文件,例如 --sysroot=/home/build
,那么如果之前默认去 /usr/lib
下面去搜索依赖库,则在sysroot的作用下会定位到 /home/build/usr/lib
目录下进行搜索。这个参数在交叉编译的时候会影响到rpath,如果没有设置这个sysroot,则rpath在编译阶段是不起作用的。
在交叉编译工具中,有默认的 sysroot 路径。
# 设置交叉编译工具链的环境变量
export PATH=/home/yoyo/360Downloads/toolchains/arm-linux-gnueabihf/bin:$PATH
# 查看交叉编译工具链的 sysroot 路径
arm-linux-gnueabihf-gcc -print-sysroot
输出示例:
yoyo@yoyo:~$ arm-linux-gnueabihf-gcc -print-sysroot
/home/yoyo/360Downloads/toolchains/arm-linux-gnueabihf/bin/../arm-linux-gnueabihf/libc
$ORIGIN
变量
关于Linux上的$ORIGIN解说
关于Linux上的$ORIGIN解说
许多现代C / C ++项目都利用 Autotools
创建GNU构建系统,例如根据平台生成make文件。 可执行文件(二进制文件)在生成/编译过程中生成,并且可以在执行编译的计算机上本地执行。 但是,如果将同一可执行文件移动到另一台计算机上,或者只是移到同一台计算机上的其他文件夹,则在运行该可执行文件时可能会遇到“找不到库”错误。因此,引入一个特殊的环境变量 $ORIGIN
。
在Linux系统中,$ORIGIN
是一个特殊的环境变量,表示可执行文件或共享库所在的目录。使用 $ORIGIN
可以指定相对于可执行文件或共享库的路径,确保程序在不同目录中运行时能正确找到依赖库。
使用 $ORIGIN
的好处在于,它允许可移植性更高的应用程序部署,因为这意味着应用程序和它的依赖库可以被放置在文件系统中的任意位置,并且在运行时动态链接器仍然能正确找到它们,只要维持相对结构不变即可。
简单示例:假设有一个可执行文件 /app/bin/myprogram
,依赖库在 /app/lib
中。可以在编译时设置 RPATH
或 RUNPATH
为 $ORIGIN/../lib
,这样程序运行时会在 /app/lib
中查找依赖库。
/app/
myprogram.c
bin/
app
lib/
libdependency.so
在链接 app
时,可以使用:
gcc -Wl,-rpath,'$ORIGIN/../x264/lib' -Wl,rpath,'$ORIGIN/../x265/lib' -o app myprogram.c
-Wl,-rpath,'$ORIGIN'
如果需要设置多个路径,可以将它们用空格分隔,并确保每个路径前都有 -Wl,-rpath,
。
-Wl,-rpath,'$ORIGIN/../third_party/arm_opencv/3rdparty/zlib/lib' \
-Wl,-rpath,'$ORIGIN/../third_party/arm_opencv/3rdparty/szlib/lib'
这样,不管 /usr/appdir/
复制到文件系统的哪个地方,执行 app
时,libdependency.so
都会从执行文件所在目录下的 lib
目录中被找到和加载。
在处理诸如需要将应用打包到一个单一文件夹以实现便携式部署的情况下,$ORIGIN
变量异常有用。使用 $ORIGIN
,开发者便无需担心程序部署后动态链接库的路径问题,从而大大提高了软件的移植性和灵活性。
注意事项:
$ORIGIN
必须用引号括起来,防止被 shell 解释。- 使用
$ORIGIN
时,确保路径设置正确,避免运行时找不到库。 - 在
Makefile
或其他文件中直接使用时,只用一个$
会展开成变量值,通常需要写作$$ORIGIN
来避免变量扩展。
三、-L、-rpath-link和-rpath
通过使用 rpath
和 rpath-link
,可以确保程序在运行时正确找到依赖的库。这对于处理不在系统默认路径下的库非常有用。在编译和链接时正确使用这两个选项可以确保你的程序在不同环境中都能正常运行。
引言
现代连接器在处理动态库时将链接时路径(Link-time path) 和 运行时路径(Run-time path) 分开,用户可以通过 -L
指定连接时库的路径,通过-R
(或-rpath
)指定程序运行时库的路径,大大提高了库应用的灵活性。
-rpath
和-rpath-link
之间的区别:-rpath
选项指定的目录被包含在可执行文件中并在运行时使用,而-rpath-link
选项仅在链接时有效。
通过测试发现,-Wl,-rpath
下面这三种写法都是可以的:
-Wl,-rpath -Wl,/usr/lib/gstreamer-1.0
-Wl,-rpath,/usr/lib/gstreamer-1.0
-Wl,-rpath=/usr/lib/gstreamer-1.0
-l
选项
功能:添加需要链接的库文件,如果没有后缀指明动态库还是静态库,则优先使用动态库。
-L
选项(编译时路径)
功能:
-L
指定的是编译时链接的动态库搜索路径。
编译时-L
选项并不影响环境变量 LD_LIBRARY_PATH
,只是指定了程序编译连接时库的路径,并不影响程序执行时库的路径。当程序运行时,系统还是会到默认路径下查找该程序所需要的库,如果找不到,会出现类似 cannot open shared object file
的错误。
-L/mylib -lmylib
-Wl,-rpath
选项(运行时路径)
功能:
-Wl,-rpath
用于指定动态库的搜索路径(在运行阶段),该路径会被记录在elf可执行文件中。
-rpath
的作用相当于在程序运行时设置了 LD_LIBRARY_PATH
环境变量,因为 -rpath
指定的路径会被记录在生成的可执行程序中,用于运行时查找需要加载的动态库 。因此,在开发板中无需设置环境变量即可找到相关的动态库。通常情况下,推荐使用 -Wl,-rpath
选项。
# 单个路径
-L/mylib -lmylib -Wl,-rpath=dir
# 多个路径
-L/mylib -lmylib -Wl,-rpath,dir1:dir2:...:dirN
在cmake中使用:
# 设置第三方库路径
set(3RDPARTY_DIR "${PARENT_DIR}/third_party")
# 设置目标属性,将运行时库搜索路径添加到目标
set_target_properties(
dla_detect
PROPERTIES
LINK_FLAGS
"-L${3RDPARTY_DIR}/zlib/lib -Wl,-rpath,'$ORIGIN/../../third_party/zlib/lib' \
-L${3RDPARTY_DIR}/szlib/lib -Wl,-rpath,'$ORIGIN/../../third_party/szlib/lib' \
-L${3RDPARTY_DIR}/hdf5/lib -Wl,-rpath,'$ORIGIN/../../third_party/hdf5/lib' \
-L${3RDPARTY_DIR}/x264/lib -Wl,-rpath,'$ORIGIN/../../third_party/x264/lib' \
-L${3RDPARTY_DIR}/x265/lib -Wl,-rpath,'$ORIGIN/../../third_party/x265/lib' \
-L${3RDPARTY_DIR}/ffmpeg/lib -Wl,-rpath,'$ORIGIN/../../third_party/ffmpeg/lib' \
-L${3RDPARTY_DIR}/sigmastar/lib -Wl,-rpath,'$ORIGIN}/../../third_party/sigmastar/lib' \
-L${3RDPARTY_DIR}/arm_opencv/lib -Wl,-rpath,'$ORIGIN/../../third_party/arm_opencv/lib' \
-L${PARENT_DIR}/build -Wl,-rpath,'$ORIGIN/../../build'"
)
或者:
# 设置第三方库路径
set(3RDPARTY_DIR "${PARENT_DIR}/third_party")
# 设置rpath
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} \
-L${3RDPARTY_DIR}/zlib/lib -Wl,-rpath,'$ORIGIN/../../third_party/zlib/lib' \
-L${3RDPARTY_DIR}/szlib/lib -Wl,-rpath,'$ORIGIN/../../third_party/szlib/lib' \
-L${3RDPARTY_DIR}/hdf5/lib -Wl,-rpath,'$ORIGIN/../../third_party/hdf5/lib' \
-L${3RDPARTY_DIR}/x264/lib -Wl,-rpath,'$ORIGIN/../../third_party/x264/lib' \
-L${3RDPARTY_DIR}/x265/lib -Wl,-rpath,'$ORIGIN/../../third_party/x265/lib' \
-L${3RDPARTY_DIR}/ffmpeg/lib -Wl,-rpath,'$ORIGIN/../../third_party/ffmpeg/lib' \
-L${3RDPARTY_DIR}/sigmastar/lib -Wl,-rpath,'$ORIGIN/../../third_party/sigmastar/lib' \
-L${3RDPARTY_DIR}/arm_opencv/lib -Wl,-rpath,'$ORIGIN/../../third_party/arm_opencv/lib' \
-L${PARENT_DIR}/build -lsigmastar_vVehicle_det -Wl,-rpath,'$ORIGIN/../../build'")
编译之后,查看rpath:
yoyo@yoyo:~/share/driver/dla_detect$ readelf -d test/build/dla_detect
...
0x0000000f (RPATH) Library rpath: [$ORIGIN/../../third_party/zlib/lib:$ORIGIN/../../third_party/szlib/lib:$ORIGIN/../../third_party/hdf5/lib:$ORIGIN/../../third_party/x264/lib:$ORIGIN/../../third_party/x265/lib:$ORIGIN/../../third_party/ffmpeg/lib:$ORIGIN/../../third_party/sigmastar/lib:$ORIGIN/../../third_party/arm_opencv/lib:$ORIGIN/../../build:/home/yoyo/share/driver/dla_detect/third_party/arm_opencv/share/opencv4/../../lib]
...
-Wl,-rpath-link
选项(运行时路径)
功能:
-Wl,-rpath-link
用于指定动态库的搜索路径(在链接阶段),该选项只在链接阶段起作用,不会被写入elf文件中。
-L/mylib -lmylib -Wl,-rpath-link=<library_path>
在cmake中使用:
# 设置第三方库路径
set(3RDPARTY_DIR "${PARENT_DIR}/third_party")
# 设置目标属性,将运行时库搜索路径添加到目标
set_target_properties(
dla_detect
PROPERTIES
LINK_FLAGS
"-L${3RDPARTY_DIR}/zlib/lib -Wl,-rpath-link,'$ORIGIN/../../third_party/zlib/lib' \
-L${3RDPARTY_DIR}/szlib/lib -Wl,-rpath-link,'$ORIGIN/../../third_party/szlib/lib' \
-L${3RDPARTY_DIR}/hdf5/lib -Wl,-rpath-link,'$ORIGIN/../../third_party/hdf5/lib' \
-L${3RDPARTY_DIR}/x264/lib -Wl,-rpath-link,'$ORIGIN/../../third_party/x264/lib' \
-L${3RDPARTY_DIR}/x265/lib -Wl,-rpath-link,'$ORIGIN/../../third_party/x265/lib' \
-L${3RDPARTY_DIR}/ffmpeg/lib -Wl,-rpath-link,'$ORIGIN/../../third_party/ffmpeg/lib' \
-L${3RDPARTY_DIR}/sigmastar/lib -Wl,-rpath-link,'$ORIGIN}/../../third_party/sigmastar/lib' \
-L${3RDPARTY_DIR}/arm_opencv/lib -Wl,-rpath,'$ORIGIN/../../third_party/arm_opencv/lib' \
-L${PARENT_DIR}/build -Wl,-rpath-link,'$ORIGIN/../../build'"
)
或者:
# 设置第三方库路径
set(3RDPARTY_DIR "${PARENT_DIR}/third_party")
# 设置rpath
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} \
-L${3RDPARTY_DIR}/zlib/lib -Wl,-rpath-link,'$ORIGIN/../../third_party/zlib/lib' \
-L${3RDPARTY_DIR}/szlib/lib -Wl,-rpath-link,'$ORIGIN/../../third_party/szlib/lib' \
-L${3RDPARTY_DIR}/hdf5/lib -Wl,-rpath-link,'$ORIGIN/../../third_party/hdf5/lib' \
-L${3RDPARTY_DIR}/x264/lib -Wl,-rpath-link,'$ORIGIN/../../third_party/x264/lib' \
-L${3RDPARTY_DIR}/x265/lib -Wl,-rpath-link,'$ORIGIN/../../third_party/x265/lib' \
-L${3RDPARTY_DIR}/ffmpeg/lib -Wl,-rpath-link,'$ORIGIN/../../third_party/ffmpeg/lib' \
-L${3RDPARTY_DIR}/sigmastar/lib -Wl,-rpath-link,'$ORIGIN/../../third_party/sigmastar/lib' \
-L${3RDPARTY_DIR}/arm_opencv/lib -Wl,-rpath-link,'$ORIGIN/../../third_party/arm_opencv/lib' \
-L${PARENT_DIR}/build -lsigmastar_vVehicle_det -Wl,-rpath-link,'$ORIGIN/../../build'")
编译之后,查看二进制的 rpath
:
yoyo@yoyo:~/share/driver/dla_detect$ readelf -d test/build/dla_detect
...
0x0000000f (RPATH) Library rpath: [/home/yoyo/share/driver/dla_detect/third_party/arm_opencv/share/opencv4/../../lib]
...
查看 rpath
:
objdump -x path/to/executable | grep RPATH
readelf -d path/to/executable | head -20
chrpath -l path/to/executable