C++的链接分为两部分,一个是编译时,一个是运行时。但运行时的行为也收到编译参数的影响。
1、编译时链接
基本就两个参数,-l
和-L
:
-l
编译时要链接的库(包括动态链接库.so
和静态链接库.a
),注意库的顺序,被依赖的放在后面。-L
查询链接库的位置,编译器将依次查找。(/usr/lib
之类的系统位置不用写)。
一个例子:
g++ ... -l folly -l boost_system -L /opt/lib
一个特殊的例子是,如果要链接的依赖库,是携带了版本号的,比如/opt/lib/libfoo.so.1
。此时直接用 -l foo
,会提示找不到 libfoo.so ,导致链接错误。此时有两种方法:
- 一种方法是写全路径:
-l /opt/lib/libfoo.so.1
。 - 另一种方法是建立文件软链接,在
/opt/lib
目录下执行:ln -s libfoo.so.1 libfoo.so
。对于正常编译安装的外部库,一般都自动做了这种软链接。
2、运行时链接
编译时,静态链接库.a
文件将直接被合并,因此运行时链接只涉及动态链接库.so
文件。
执行ldd your_file
可以查看指定文件运行时所需要链接的文件以及是否链接到指定位置。如果出现Not found
或者链接到不正确位置,需要考察以下两个设置对象。
2.1、系统环境变量LD_LIBRARY_PATH
最后链接程序会考虑系统的环境变量LD_LIBRARY_PATH
,从这里面设置的目录列表依次查找所需要的库文件。
下面命令可以查看当前设置的(用:
隔开的)目录列表:
echo $LD_LIBRARY_PATH
下面命令可以设置该列表:
export LD_LIBRARY_PATH=~/lib:$LD_LIBRARY_PATH
注意将自己的目录放在最前面,多个目录用:
隔开,并且包含原有的$LD_LIBRARY_PATH
,以免破坏其它程序的设置。
该命令设置只会对当前窗口有效,新开窗口需要重新设置。如果需要总是有效,可考虑下面方法:
- 如果想对所有用户生效,可放在
/etc/environment
文件里(需 ROOT 权限)。 - 如果想对当前用户生效,可放在
~/.bashrc
或者~/.zshrc
等启动配置文件。
2.2、程序内置参数RUNPATH
链接程序首先会参考程序内置参数RUNPATH
。下面命令可以查看该设置:
readelf -d your_file | grep RUNPATH
这个参数的设置在编译环节,即常见的-Wl,rpath
:
g++ ... -Wl,rpath=dir1 -Wl,rpath=dir2
RUNPATH 的设置会在 LD_LIBRARY_PATH 之后,这是为了让用户更灵活地配置。
2.3、程序内置参数DT_RPATH
内置参数DT_RPATH
是很老的-Wl,rpath
行为,现在已经改成了RUNPATH
。在已设置RUNPATH
情况下,该参数就会无效。DT_RPATH
的区别在于它的优先级还在系统设置LD_LIBRARY_PATH
之前。下面命令可查看该设置
readelf -d your_file | grep DT_RPATH
设置方法为-Wl,rpath
配上-Wl,disable-new-dtags
选项:
g++ ... -Wl,rpath=dir1 -Wl,rpath=dir2 -Wl,disable-new-tags
有时候会显示路径为$(ORIGIN)
,这个表示库所在的当前路径。
2.4、依赖库的依赖库
比如我的程序使用了依赖库/opt/3rd/libaaa.so
,但/opt/3rd/libaaa.so
又依赖了/opt/3rd/libbbb.so
。这时候,即使将/opt/3rd
添加到RUNPATH
,程序依旧可能会报错找不到 libbbb.so 文件。这是因为程序的 RUNPATH 只对 libaaa.so 的查找起作用,libbbb.so
的查找依赖于libaaa.so
的 RUNPATH 设置。
这时候要么只能设置LD_LIBRARY_PATH
,它是全局有效的,要么在编译的时候将libbbb.so
也添加到依赖项(注意 bbb 要放在 aaa 的后面):
g++ ... -l aaa -l bbb -L /opt/3rd -Wl,rpath=/opt/3rd
2.5、还是找不到库?
有时候,通过上面方法设置了合适的路径和参数,查看路径和参数也都正常,但还是提示找不到,比如报「libboost_thread.so.xxx.xxx.xxx: cannot open shared object file: No such file or directory」错误。
我碰到的一次,解决方法是执行:
sudo ldconfig /opt/lib/
其中/opt/lib
换成你的 boost 的安装路径。本质是通过ldconfig
刷新缓存。
Q. E. D.