Qt .pro配置gcc相关命令(三):-W1、-L、-rpath和-rpath-link
目录
1.Linux 动态库相关知识
1.1.动态库查找路径
1.2.查看程序依赖的动态库
1.3.修改动态库查找路径的方法
1.4.动态链接器缓存管理
2.-Wl参数
3.-L选项(编译时路径)
4.-rpath参数(运行时路径)
5.-rpath-link 参数
6.常见问题与解决方案
7.总结
在 Linux 系统中编译和链接动态库时,-L
、-rpath
和-rpath-link
是三个非常重要的参数,它们分别解决了动态库编译、链接和运行时的不同问题。
1.Linux 动态库相关知识
1.1.动态库查找路径
一个典型的 C/C++ 程序的构建流程是:预处理,汇编,编译,链接。而执行链接的程序其实是 ld
,通常编译器比如 GCC 都会自动调用 ld
去进行链接,用户不必关注其中的细节。而 ld
查找动态库的顺序是:
1.程序本身指定的路径, 即是rpath
或 runpath
指定的目录;
RPATH
:硬编码在 ELF 文件中,优先级最高。RUNPATH
:与RPATH
类似,但优先级低于LD_LIBRARY_PATH
。
2.环境变量 LD_LIBRARY_PATH
指定的目录;
- 用户自定义的临时路径,多个路径用冒号
:
分隔。 - 例如:
export LD_LIBRARY_PATH=/path/to/lib:$LD_LIBRARY_PATH
。
3.runpath
指定的目录;
4./etc/ld.so.cache
缓存文件,通常包含 /etc/ld.so.conf
文件编译出的二进制(比如 CentOS 上,该文件会使用 include 从而使用 ld.so.conf.d
目录下面所有的 *.conf
文件,这些都会缓存在 ld.so.cache 中)
- 这些文件中指定的路径会被动态链接器缓存(通过
ldconfig
命令更新)。
5.系统默认路径,比如 /lib
,/usr/lib
。
/lib
:系统核心库(如libc.so
)。/usr/lib
:用户级库。/usr/local/lib
:本地安装的库(如自行编译的软件)。
在编译时若使用 -z nodefaultlib
选项编译,则会跳过 4 和 5。至于 runpath,和 rpath 类似,都是二进制(ELF)文件的动态 section 属性(分别为 DT_RUNPATH
和 DT_RPATH
),唯一区别就是是否优先于 LD_LIBRARY_PATH
来查找。
rpath
vs.runpath
:rpath 和 runpath 是嵌入在可执行文件或共享库中的路径列表,用于指定运行时查找共享库的位置。rpath 是旧标准,runpath 是新标准,功能类似但优先级不同。
1.2.查看程序依赖的动态库
1.ldd
命令
ldd /path/to/program # 显示程序依赖的所有动态库及其路径
- 若某库显示为
not found
,表示系统未找到该库。
2.readelf
命令
readelf -d /path/to/program | grep -E '(RPATH|RUNPATH|NEEDED)'
输出示例:
0x000000000000000f (RPATH) Library rpath: [/path/to/lib]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
1.3.修改动态库查找路径的方法
1.临时修改:通过 LD_LIBRARY_PATH
环境变量
export LD_LIBRARY_PATH=/path/to/lib:$LD_LIBRARY_PATH # 当前终端生效
# 或添加到 ~/.bashrc 中使其永久生效
echo 'export LD_LIBRARY_PATH=/path/to/lib:$LD_LIBRARY_PATH' >> ~/.bashrc
source ~/.bashrc
2.永久修改:编辑 /etc/ld.so.conf
或添加新配置
# 方法1:直接编辑主配置文件
sudo vim /etc/ld.so.conf
# 添加一行:/path/to/lib
# 保存后更新缓存
sudo ldconfig# 方法2:在 /etc/ld.so.conf.d/ 目录下创建新配置文件
sudo touch /etc/ld.so.conf.d/myapp.conf
sudo echo '/path/to/lib' > /etc/ld.so.conf.d/myapp.conf
sudo ldconfig # 更新缓存
3.编译时指定:通过 -Wl,-rpath
选项
在编译程序时,使用 -Wl,-rpath
将路径硬编码到程序中:
gcc -o myapp myapp.c -L/path/to/lib -lmylib -Wl,-rpath,/path/to/lib
- 优点:无需修改系统配置或环境变量。
- 缺点:路径被固定,若库位置改变需重新编译。
1.4.动态链接器缓存管理
1.更新缓存
当添加新库或修改 /etc/ld.so.conf
后,需执行:
sudo ldconfig # 重建动态链接器缓存
2.查看缓存内容
ldconfig -p # 显示当前缓存的所有库及其路径
2.-Wl参数
在 Linux 系统中,-Wl
是 GCC 编译器的一个重要参数,用于向链接器(如 ld
)传递额外选项。由于 GCC 是编译和链接的前端工具,真正处理链接过程的是链接器,因此需要通过 -Wl
将参数传递给链接器。
基本语法
gcc [编译选项] -Wl,链接器选项1[,链接器选项2,...] [源文件]
将逗号分隔的选项传递给链接器。如:
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
3.-L
选项(编译时路径)
- 编译时:指定链接器搜索库文件的路径。
- 仅影响链接过程,不影响程序运行时的库查找。
假设你有一个自定义库 libmath.so
位于 /opt/mylibs
目录下:
g++ main.cpp -L/opt/mylibs -lmath -o myapp
-L/opt/mylibs
:告诉链接器在/opt/mylibs
目录下搜索libmath.so
。-lmath
:链接名为libmath.so
的库。
-L一般用于链接非标准位置的第三方库(如 /usr/local/lib
以外的路径)。
4.-rpath参数(运行时路径)
运行时搜索路径,将动态库搜索路径硬编码到可执行文件中(运行时生效)。通过修改可执行文件的 .dynamic
段,将路径硬编码到二进制文件中。
-rpath
的作用相当于在程序运行时设置了 LD_LIBRARY_PATH
环境变量,因为 -rpath
指定的路径会被记录在生成的可执行程序中,用于运行时查找需要加载的动态库 。因此,在开发板中无需设置环境变量即可找到相关的动态库。通常情况下,推荐使用 -Wl,-rpath
选项。
# 单个路径
-L/mylib -lmylib -Wl,-rpath=dir# 多个路径
-L/mylib -lmylib -Wl,-rpath,dir1:dir2:...:dirN
示例:
g++ main.cpp -L/opt/mylibs -lmath -Wl,-rpath=/opt/mylibs -o myapp
-Wl,-rpath=/opt/mylibs
:将/opt/mylibs
添加到程序运行时的库搜索路径。
等价写法
# 直接使用链接器参数
g++ main.cpp -L/opt/mylibs -lmath -Wl,-rpath=/opt/mylibs -o myapp# 或使用环境变量
export LDFLAGS="-Wl,-rpath=/opt/mylibs"
g++ main.cpp -L/opt/mylibs -lmath $LDFLAGS -o myapp
特殊变量
$ORIGIN
:表示可执行文件所在目录,常用于相对路径。
# 搜索与可执行文件同级的 lib 目录
-Wl,-rpath='$ORIGIN/../lib'
注意:在 Makefile 或 shell 脚本中使用时,需用双引号防止变量提前展开:
-Wl,-rpath="\$$ORIGIN/../lib"
在cmake中使用:
# 设置第三方库路径
set(3RDPARTY_DIR "${PARENT_DIR}/third_party")# 设置目标属性,将运行时库搜索路径添加到目标
set_target_properties(dla_detectPROPERTIESLINK_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'")
5.-rpath-link
参数
用于指定动态库的搜索路径(在链接阶段),该选项只在链接阶段起作用,不会被写入elf文件中。辅助链接器解析库之间的依赖关系,但不影响程序运行时的库搜索路径。适用于复杂的库依赖链(如 A 依赖 B,B 依赖 C)。
示例:假设:
libapp.so
依赖libmath.so
。libmath.so
依赖libutils.so
(位于/opt/thirdparty
)。
# 链接 libapp.so 时指定 libmath.so 的依赖路径
g++ -shared -fPIC app.cpp -L/opt/mylibs -lmath -Wl,-rpath-link=/opt/thirdparty -o libapp.so
-Wl,-rpath-link=/opt/thirdparty
:告诉链接器,libmath.so
依赖的libutils.so
在/opt/thirdparty
目录下。- 但程序运行时,仍需通过
-rpath
或环境变量指定/opt/thirdparty
。
在cmake中使用:
# 设置第三方库路径
set(3RDPARTY_DIR "${PARENT_DIR}/third_party")# 设置目标属性,将运行时库搜索路径添加到目标
set_target_properties(dla_detectPROPERTIESLINK_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
的区别
-rpath
:同时影响链接和运行时的库搜索。-rpath-link
:仅在链接时生效,不修改可执行文件的运行时路径。
三者对比
参数 | 生效阶段 | 是否影响运行时 | 作用 |
---|---|---|---|
-L | 链接时 | 否 | 指定链接器搜索库的路径 |
-rpath | 运行时 | 是 | 修改程序运行时的库搜索路径(硬编码到二进制文件) |
-rpath-link | 链接时 | 否 | 辅助链接器解析库依赖关系,但不影响程序运行时的搜索路径 |
6.常见问题与解决方案
1.错误:libxxx.so: cannot open shared object file: No such file or directory
- 可能原因:
- 库未安装。
- 库路径未包含在查找路径中。
- 解决方案
# 1. 确认库是否存在
find / -name "libxxx.so*" 2>/dev/null# 2. 若存在,将路径添加到 LD_LIBRARY_PATH
export LD_LIBRARY_PATH=/path/to/lib:$LD_LIBRARY_PATH# 3. 或添加到系统配置
echo '/path/to/lib' | sudo tee -a /etc/ld.so.conf
sudo ldconfig
2.区分 RPATH
和 RUNPATH
- RPATH:在
RUNPATH
和LD_LIBRARY_PATH
之前被搜索。 - RUNPATH:在
LD_LIBRARY_PATH
之后被搜索。
修改方法:
# 编译时指定 RPATH
gcc -Wl,-rpath,/path/to/lib ...# 编译时指定 RUNPATH(需显式使用 -rpath-link)
gcc -Wl,-rpath-link,/path/to/lib -Wl,-rpath,'$ORIGIN/../lib' ...
$ORIGIN
表示程序所在目录,用于相对路径。
3.链接时提示找不到库的依赖
/usr/bin/ld: libmath.so: undefined reference to `function_in_libutils'
原因:
libmath.so
依赖libutils.so
,但链接器找不到libutils.so
。
解决方案:
# 临时指定依赖路径
g++ -shared -fPIC app.cpp -L/opt/mylibs -lmath -Wl,-rpath-link=/opt/thirdparty -o libapp.so
7.总结
- 优先使用
-rpath
:减少对环境变量的依赖,提高程序可移植性。 - 使用
$ORIGIN
:让库路径相对于可执行文件,避免硬编码绝对路径。 - 谨慎使用
-rpath-link
:仅在链接复杂依赖的库时使用,避免污染运行时路径。 - 测试部署:确保程序在目标环境中能正常找到库(如使用
ldd myapp
检查)。
通过合理组合 -L
、-rpath
和 -rpath-link
,可以灵活控制动态库的编译、链接和运行时行为,解决复杂的依赖问题。