Qt .pro配置gcc相关命令(三):-W1、-L、-rpath和-rpath-link

目录

[1.Linux 动态库相关知识](#1.Linux 动态库相关知识)

1.1.动态库查找路径

1.2.查看程序依赖的动态库

1.3.修改动态库查找路径的方法

1.4.动态链接器缓存管理

2.-Wl参数

3.-L选项(编译时路径)

4.-rpath参数(运行时路径)

[5.-rpath-link 参数](#5.-rpath-link 参数)

6.常见问题与解决方案

7.总结


在 Linux 系统中编译和链接动态库时,-L-rpath-rpath-link是三个非常重要的参数,它们分别解决了动态库编译、链接和运行时的不同问题。

1.Linux 动态库相关知识

1.1.动态库查找路径

一个典型的 C/C++ 程序的构建流程是:预处理,汇编,编译,链接。而执行链接的程序其实是 ld,通常编译器比如 GCC 都会自动调用 ld 去进行链接,用户不必关注其中的细节。而 ld 查找动态库的顺序是:

1.程序本身指定的路径, 即是rpathrunpath指定的目录;

  • 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_RUNPATHDT_RPATH),唯一区别就是是否优先于 LD_LIBRARY_PATH 来查找。

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

1.2.查看程序依赖的动态库

1.ldd 命令

cpp 复制代码
ldd /path/to/program  # 显示程序依赖的所有动态库及其路径
  • 若某库显示为 not found,表示系统未找到该库。

2.readelf 命令

cpp 复制代码
readelf -d /path/to/program | grep -E '(RPATH|RUNPATH|NEEDED)'

输出示例:

cpp 复制代码
0x000000000000000f (RPATH)              Library rpath: [/path/to/lib]
0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]

1.3.修改动态库查找路径的方法

1.临时修改:通过 LD_LIBRARY_PATH 环境变量

cpp 复制代码
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 或添加新配置

cpp 复制代码
# 方法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 将路径硬编码到程序中:

cpp 复制代码
gcc -o myapp myapp.c -L/path/to/lib -lmylib -Wl,-rpath,/path/to/lib
  • 优点:无需修改系统配置或环境变量。
  • 缺点:路径被固定,若库位置改变需重新编译。

1.4.动态链接器缓存管理

1.更新缓存

当添加新库或修改 /etc/ld.so.conf 后,需执行:

cpp 复制代码
sudo ldconfig  # 重建动态链接器缓存

2.查看缓存内容

cpp 复制代码
ldconfig -p  # 显示当前缓存的所有库及其路径

2.-Wl参数

在 Linux 系统中,-Wl 是 GCC 编译器的一个重要参数,用于向链接器(如 ld)传递额外选项 。由于 GCC 是编译和链接的前端工具,真正处理链接过程的是链接器,因此需要通过 -Wl 将参数传递给链接器。

基本语法

cpp 复制代码
gcc [编译选项] -Wl,链接器选项1[,链接器选项2,...] [源文件]

将逗号分隔的选项传递给链接器。如:

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

最终变成了linker的用法:

cpp 复制代码
ld aaa bbb ccc

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

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

类似的参数还有:

cpp 复制代码
  -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 目录下:

cpp 复制代码
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 选项。

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

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

示例:

cpp 复制代码
g++ main.cpp -L/opt/mylibs -lmath -Wl,-rpath=/opt/mylibs -o myapp
  • -Wl,-rpath=/opt/mylibs:将 /opt/mylibs 添加到程序运行时的库搜索路径。

等价写法

cpp 复制代码
# 直接使用链接器参数
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:表示可执行文件所在目录,常用于相对路径。

cpp 复制代码
# 搜索与可执行文件同级的 lib 目录
-Wl,-rpath='$ORIGIN/../lib'

注意:在 Makefile 或 shell 脚本中使用时,需用双引号防止变量提前展开:

cpp 复制代码
-Wl,-rpath="\$$ORIGIN/../lib"

cmake中使用:

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

或者:

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

用于指定动态库的搜索路径(在链接阶段),该选项只在链接阶段起作用,不会被写入elf文件中。辅助链接器解析库之间的依赖关系,但不影响程序运行时的库搜索路径。适用于复杂的库依赖链(如 A 依赖 B,B 依赖 C)。

示例:假设:

  • libapp.so 依赖 libmath.so
  • libmath.so 依赖 libutils.so(位于 /opt/thirdparty)。
cpp 复制代码
# 链接 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中使用:

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

或者

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

  • 可能原因
    • 库未安装。
    • 库路径未包含在查找路径中。
  • 解决方案
cpp 复制代码
# 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.区分 RPATHRUNPATH

  • RPATH :在 RUNPATHLD_LIBRARY_PATH 之前被搜索。
  • RUNPATH :在 LD_LIBRARY_PATH 之后被搜索。

修改方法

cpp 复制代码
# 编译时指定 RPATH
gcc -Wl,-rpath,/path/to/lib ...

# 编译时指定 RUNPATH(需显式使用 -rpath-link)
gcc -Wl,-rpath-link,/path/to/lib -Wl,-rpath,'$ORIGIN/../lib' ...
  • $ORIGIN 表示程序所在目录,用于相对路径。

3.链接时提示找不到库的依赖

cpp 复制代码
/usr/bin/ld: libmath.so: undefined reference to `function_in_libutils'

原因

  • libmath.so 依赖 libutils.so,但链接器找不到 libutils.so

解决方案

cpp 复制代码
# 临时指定依赖路径
g++ -shared -fPIC app.cpp -L/opt/mylibs -lmath -Wl,-rpath-link=/opt/thirdparty -o libapp.so

7.总结

  1. 优先使用 -rpath:减少对环境变量的依赖,提高程序可移植性。
  2. 使用 $ORIGIN:让库路径相对于可执行文件,避免硬编码绝对路径。
  3. 谨慎使用 -rpath-link:仅在链接复杂依赖的库时使用,避免污染运行时路径。
  4. 测试部署 :确保程序在目标环境中能正常找到库(如使用 ldd myapp 检查)。

通过合理组合 -L-rpath-rpath-link,可以灵活控制动态库的编译、链接和运行时行为,解决复杂的依赖问题。

相关推荐
_wyt0014 小时前
洛谷 B3930 [GESP202312 五级] 烹饪问题 题解
c++·gesp
大树886 小时前
金刚石散热越强,管路越先见顶
大数据·运维·服务器·人工智能·ai
摇滚侠6 小时前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
霸道流氓气质6 小时前
领域驱动设计(DDD)在 Spring Boot 微服务中的实践指南
运维·spring boot·微服务
bush46 小时前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行5207 小时前
Linux 11 动态监控指令top
linux
Inhand陈工7 小时前
基于台达PLC与映翰通IG502的智慧水产养殖精准投喂与远程运维解决方案
运维·人工智能·物联网·阿里云·信息与通信
玖玥拾7 小时前
C/C++ 数据结构(七)栈、容器适配器
c语言·数据结构·c++··容器适配器
酣大智8 小时前
ARP代理--工作原理
运维·网络·arp·arp代理
不会C语言的男孩8 小时前
Linux 系统编程 · 第 8 章:进程基础
linux·c语言