通俗理解-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 查找动态库的顺序是:

  1. rpath 指定的目录;
  2. 环境变量 LD_LIBRARY_PATH 指定的目录;
  3. runpath 指定的目录;
  4. /etc/ld.so.cache 缓存文件,通常包含 /etc/ld.so.conf 文件编译出的二进制俩别哦(比如 CentOS 上,该文件会使用 include 从而使用 ld.so.conf.d 目录下面所有的 *.conf 文件,这些都会缓存在 ld.so.cache 中)
  5. 系统默认路径,比如 /lib/usr/lib

在编译时若使用 -z nodefaultlib 选项编译,则会跳过 4 和 5。至于 runpath,和 rpath 类似,都是二进制(ELF)文件的动态 section 属性(分别为 DT_RUNPATHDT_RPATH),唯一区别就是是否优先于 LD_LIBRARY_PATH 来查找。

rpath vs. runpathrpathrunpath 是嵌入在可执行文件或共享库中的路径列表,用于指定运行时查找共享库的位置。rpath 是旧标准,runpath 是新标准,功能类似但优先级不同。

-Wl参数

gcc的-Wl,xxx选项将逗号分隔的标记列表(flags)作为空格分隔的参数列表传递给链接器,即:

bash 复制代码
gcc -Wl,aaa,bbb,ccc

最终变成了linker的用法:

bash 复制代码
ld aaa bbb ccc

如果是想把ld -rpath通过-Wl传递给gcc,可以是-Wl,-rpath,xxx,也可以指定-Wl的重复实例:

bash 复制代码
gcc -Wl,aaa -Wl,bbb -Wl,ccc

类似的参数还有:

bash 复制代码
-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 路径。

bash 复制代码
# 设置交叉编译工具链的环境变量
export PATH=/home/yoyo/360Downloads/toolchains/arm-linux-gnueabihf/bin:$PATH

# 查看交叉编译工具链的 sysroot 路径
arm-linux-gnueabihf-gcc -print-sysroot

输出示例:

bash 复制代码
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 中。可以在编译时设置 RPATHRUNPATH$ORIGIN/../lib,这样程序运行时会在 /app/lib 中查找依赖库。

bash 复制代码
/app/
    myprogram.c
    bin/
    	app
    lib/
        libdependency.so

在链接 app 时,可以使用:

bash 复制代码
gcc -Wl,-rpath,'$ORIGIN/../x264/lib' -Wl,rpath,'$ORIGIN/../x265/lib' -o app myprogram.c
-Wl,-rpath,'$ORIGIN'

如果需要设置多个路径,可以将它们用空格分隔,并确保每个路径前都有 -Wl,-rpath,

bash 复制代码
-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

通过使用 rpathrpath-link,可以确保程序在运行时正确找到依赖的库。这对于处理不在系统默认路径下的库非常有用。在编译和链接时正确使用这两个选项可以确保你的程序在不同环境中都能正常运行。

引言

现代连接器在处理动态库时将链接时路径(Link-time path) 和 运行时路径(Run-time path) 分开,用户可以通过 -L指定连接时库的路径,通过-R(或-rpath)指定程序运行时库的路径,大大提高了库应用的灵活性。

-rpath-rpath-link之间的区别:-rpath选项指定的目录被包含在可执行文件中并在运行时使用,而-rpath-link选项仅在链接时有效。

通过测试发现,-Wl,-rpath下面这三种写法都是可以的:

bash 复制代码
-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 的错误。

bash 复制代码
-L/mylib -lmylib

-Wl,-rpath选项(运行时路径)

功能:-Wl,-rpath 用于指定动态库的搜索路径(在运行阶段),该路径会被记录在elf可执行文件中。

-rpath 的作用相当于在程序运行时设置了 LD_LIBRARY_PATH 环境变量,因为 -rpath指定的路径会被记录在生成的可执行程序中,用于运行时查找需要加载的动态库 。因此,在开发板中无需设置环境变量即可找到相关的动态库。通常情况下,推荐使用 -Wl,-rpath 选项。

bash 复制代码
# 单个路径
-L/mylib -lmylib -Wl,-rpath=dir

# 多个路径
-L/mylib -lmylib -Wl,-rpath,dir1:dir2:...:dirN

在cmake中使用:

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'"
)

或者:

cmake 复制代码
# 设置第三方库路径
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:

bash 复制代码
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文件中。

bash 复制代码
-L/mylib -lmylib -Wl,-rpath-link=<library_path>

在cmake中使用:

bash 复制代码
# 设置第三方库路径
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'"
)

或者:

cmake 复制代码
# 设置第三方库路径
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

bash 复制代码
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

bash 复制代码
objdump -x path/to/executable | grep RPATH
readelf -d path/to/executable | head -20
chrpath -l path/to/executable
相关推荐
零涂毕业设计20 分钟前
毕业设计模块开发-OLED显示屏(IIC协议0.96寸)STM32 ESP32 FPGA Linux驱动免费分享
linux·stm32·单片机·嵌入式·esp32·fpga·oled
tedcloud12331 分钟前
OmniRoute怎么部署?开源AI模型路由平台Linux部署教程
linux·服务器·人工智能·开源·音视频
SakitamaX1 小时前
LVS(Linux Virtual Server)概念详解
linux·运维·lvs
小张同学a.1 小时前
Linux系统管理
linux·运维
阿龙的工作记录1 小时前
深入理解Linux设备驱动模型
linux·嵌入式硬件·工作记录
代码村新手2 小时前
Linux的基本指令
linux·运维·服务器
撩得Android一次心动4 小时前
Linux编程笔记3【个人用】
linux·笔记
OpenPomeloxCommunity4 小时前
3. Linux 同步机制之 spinlock 设计与实现(一):通用层
linux
念恒123065 小时前
网络基础
linux·网络·c++
皮卡狮5 小时前
Linux静态库和动态库详解
linux