C++ 的链接问题

作者: , 共 2037 字 , 共阅读 0

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.

类似文章:
编程 » C++, 编译链接
最近升级系统,出现好多莫名其妙的问题。其中一个便是 G++编译后,发现其中一个动态链接库引用了绝对地址。正常情况下运行ldd bin/auto应该是下面的结果:
编程 » C++, GCC, 编译链接
LD 在链接生成目标文件时,会从左到有扫描输入的依赖库,当依赖库之间也有依赖关系时,必须将「依赖别人的库」放在「被别人依赖的库」的前面。否则会链接失败!失败的症状有:
编程 » C++, 编译
一个典型的 GCC C++编译过程为:
相似度: 0.148
boost是除std外最常用的 C++库,覆盖很多常用操作。目前最新的版本是1.85.0http://boost.org/上可以查看最新版本号,将下面的 59 换成最新的即可)。
相似度: 0.098
编程 » pytorch, C++
目前 pytorch 已经升级到了 1.7.0 ,但在 ubuntu 20.04 下有一个非常诡异的 bug。为此,我们只能自己编译。
相似度: 0.092
armadillo是一个线性代数 C++库,封装了blaslapack,提供更直观的接口。
编程 » C++, log
先编译 libfmt.a :
最近老遇到一个奇怪的问题。在 VS 2013 编译时,爆出很多警告:
IT » Linux, grep
最简单的方式是使用grep-L参数,查找当前目录下不包含指定字符串的文件:
编程 » C++, assert, 异常处理
1)在函数开始处检验传入参数的合法性
爬升接近 400 米,路线长度接近 8 公里。有很长一段在山脊上行走,视野非常开阔,一般要 2000 米以上的山才有的景色。秋天可观赏红叶。