C++动态链接库总结

编译时查找动态链接库的顺序

编译时会在以下路径查找:
通过编译选项-L 指定的目录
通过链接选项–rpath=指定的目录
通过环境变量LIBRARY_PATH指定的目录(:分割)
通过环境变量LD_LIBRARY_PATH指定的目录(:分割)
/lib
/usr/lib
/etc/ld.so.cache中缓存的路径,通过/etc/ld.so.conf指定,通过ldconfig命令更新缓存。
至于查找的顺序其实不重要,因为只要找到符号通过链接就可以了,而运行时又会按照运行时查找的顺序进行查找,甚至运行时都不在相同环境,链接到的动态库也可能和编译时不是同一个。

指定编译时的动态链接库路径

通过gcc/g++-L编译选项
通过-Wl,--rpath=传递链接选项
设置LIBRARY_PATHLD_LIBRARY_PATH环境变量
CMakeLists.txt中使用link_directories(${LIB_DIRS})来指定,LIB_DIRS是用一个或多个空白字符分割的多个路径
CMakeLists.txt中使用指定rpath,方式如下

1
2
3
4
target_link_libraries(${PROJECT_NAME}
${LIB_LIST}
-Wl,-rpath,'$ORIGIN'/libs,-rpath,'$ORIGIN'/../libs
)

其中'$ORIGIN'是一个特殊的路径,实际上这里是在提前指定运行时的查找路径,'$ORIGIN'表示可执行文件所在目录,另外要注意的是如果指定多个rpath这里不能通过:分割,而是使用多次-rpath选项.
BTW: 我并不清楚为什么要使用'$ORIGIN'而不是.,因为我测试下来效果是一样的。

运行时查找动态链接库的顺序

通过rpath连接选项指定的目录
通过环境变量LD_RUN_PATH指定的目录(:分割)
通过环境变量LD_LIBRARY_PATH指定的目录(:分割)
编译时实际的链接路径(不是查找过的路径,而是找到了这个so的路径)
/lib
/usr/lib

另外可以通过ldd <可执行程序>来查看运行将使用链接库,也可以看到缺少的库。
还可以通过chrpath -l <可执行程序>查看程序被设置的rpath。

指定运行时的动态链接库路径

设置环境变量LD_RUN_PATH(:分割)
设置环境变量LD_LIBRARY_PATH(:分割)
还可以通过chrpath -r <库路径> <可执行程序>修改程序的rpath,但实际上rpath被写在可执行文件中并没有为修改它预留空间,所以只能修改成相同长度或更短的路径,所以显然这不是一个常规操作。
另外rpath只影响这个可执行程序本身而不能传递给其加载的so,如果我从程序a加载了动态库b,而动态b需要加载动态库c,即使我把b、c放在相同路径,a通过rpath的方式找到了b,而b还是找不到c,因为b的rpath里并没有这个路径。
所以总得来说最靠谱的方式还是指定LD_LIBRARY_PATH,在linux上可以通过下面的方式在启动程序同时设置环境变量

1
LD_LIBRARY_PATH=<动态库目录>:$LD_LIBRARY_PATH <可执行文件>

设置LD_LIBRARY_PATH同时保留了之前的值放在后面,这样设置的环境变量只影响可执行文件启动的程序,包括间接加载的动态链接库,