开发C++程序, 路径设置一直是一个容易出错的关键点。很多人入门C++很久,对于程序开发和运行相关路径还是云里雾里。这里做一个简单的梳理。
因为现在C++开发一般都是CMake了,所以本文都以CMake举例。
C++开发中需要关注的路径
- 头文件路径: 头文件路径是编译器(如: cc, cc1)需要使用的路径。
- 依赖库文件路径: 依赖库文件路径是链接器(如: ld)需要使用的路径。
- 运行时路径: 程序运行时通常需要加载一些动态库,仅依赖当前系统环境,与开发设置无关,所以需要在系统环境中设置运行时路径。
GCC链接选项-L,-rpath-link,-rpath
-L
: 指定通过-l
连接的动态库的搜索目录,所有的-L
对所有的-l
有效。-rpath
: 设置运行时库搜索路径,会写入ELF可执行文件中。-rpath-link
: 指定链接时间接依赖库搜索路径, 运行时无效。
LD_LIBRARY_PATH
在程序链接或运行期间,如果设置了LD_LIBRARY_PATH, 链接器/运行时链接器都会搜索该路径查找动态库。
有趣的两点:
- LD_LIBRARY_PATH 中可以配置相对路径,比如.和..
- LD_LIBRARY_PATH 中若存在空路径,则相当于包含当前工作路径 (所以LD_LIBRARY_PATH中存在一个多余的冒号与否,有较大区别)
可执行程序运行时搜索库路径的几个点:
- 默认路径: /lib, /usr/lib
- 环境变量: LD_LIBRARY_PATH
- ELF中的RPATH
- ELF中的RUNPATH
其优先级关系如下:
|ELF中的RPATH|ELF中的RUNPATH|LD_LIBRARY_PATH环境变量|尝试加载目录的数序|
|:=:|:=:|:=:|:=:|
|未设置|未设置|未设置|/lib==>/usr/lib|
|未设置|未设置|设置|${LD_LIBRARY_PATH}==>/lib==>/usr/lib|
|设置|未设置|未设置|${RPATH}==>/lib==>/usr/lib|
|设置|未设置|设置|${RPATH}==>${LD_LIBRARY_PATH}==>/lib==>/usr/lib|
|设置或未设置|设置|设置|${LD_LIBRARY_PATH}==>${RUN_PATH}==>/lib==>/usr/lib|
|设置或未设置|设置|未设置|${RUN_PATH}==>/lib==>/usr/lib|
pkg-config和PKG_CONFIG_PATH
许多程序和第三方库发布包提供一个*.pc的配置文件,里面也包含了头文件和依赖库等信息,用户通过设置PKG_CONFIG_PATH,就可以利用pkg-config工具解析并输出编译选项,将其用于gcc等编译器的编译链接过程中。如:
$gcc main.c pkg-config --cflags --libs opencv
-o main
CMake中设置头文件,依赖库路径和运行时路径
1 | include_directories: 设置当前Project全局头文件路径 |
可能遇到的问题
- 设置了rpath, 但是运行时找不到so.
1
2
3
4
5# 检查elf FLAGS:
$readelf -d myelf | grep FLAGS
0x000000000000001e (FLAGS) ORIGIN
0x000000006ffffffb (FLAGS_1) Flags: ORIGIN
#如果看到FLAGS不是上面的ORIGIN,可能需要通过`-z origin`链接选项开启。