64位ubuntu下链接器的BUG

最近在维护一个多平台的库,其中有一个64位linux的版本,在维护中发现了一个奇怪的问题。


假设该库的工程由一个静态库libS.a和一个动态库libD.so构成,其中libS.a引用了系统的libdl.so(使用了dlopen函数),而libD.so用到了libS.a(但没有直接使用dlopen)。

由于libD.so使用了libS.a,因此在链接libD.so需要加上-lS,另外libS.a使用了libdl.so库,所以还要加上-ldl,整个libD.so的链接参数类似如下:

> gcc -L. -shared -ldl -lS -o libD.so D.o

另假设有一可执行文件工程A.out需要使用libD.so,那么其链接参数:

> gcc -L. -lD -o A.out A.o

然后报错:

> undefined reference to 'dlopen'

用ldd -r libD.so命令查看libD.so,会看到libD.so并没有链上libdl.so,而符号dlopen显示为未解决。


但如果你在libD.so的某个.c中用到了dlopen则没有这个问题……


一阵google发现有一个gcc的命令行参数可以处理这个问题:-rdynamic(等价于-Wl,-E),于是使用如下的链接参数:

> gcc -L. -shared -rdynamic -ldl -lS -o libD.so D.o

再链接A.out:

> gcc -L. -lD -o A.out A.o

奇怪的事情发生了,32位linux下链接通过而64位下链接失败(undefined reference to 'dlopen')。

继续用ldd查看各自的libD.so,发现32位下正确的链接到了libdl.so而64位的没有。


于是我花了将近一天的时间去排查这个问题,发现如果不使用gcc,而是直接用ld:

ld -L. -shared -E -ldl -lS -o libD.so D.o

是可以链接上libdl.so的,这令我很纳闷,难道gcc -rdynamic或者 gcc -Wl,-E不等同于ld -E么?

我没有找到直接的证据,但从现象上来看它们确实有区别。

于是我打算直接用ld链接,但是不幸的是libdl.so是链上了,但仍然时不时的有符号找不到(有些是自己定义的符号),因为毫无规律可言所以我也无法在此举例。

注意32位下一切正常,上述问题都是仅限于64位环境才出现的。


于是我又奋斗了半天,终于发现只要这么写命令行就一切ok:

> gcc -L. -shared -rdynamic -lS -ldl -o libD.so D.o

没看出区别吧?只是调换了-lS和-ldl的位置……


没有找到权威的解释,但个人推测如下:

1.64位Linux的链接器在链接时总是按照命令行指定的顺序链接

2.如果前面的链接目标中存在未定义符号而能在后面的链接目标中找到,则符号可以成功链接上

3.但反之,如果后面的链接目标中存在未定义符号但只能在前面的链接目标中找到时,符号链接会失败,出现undefined reference to xxx

4.上述问题都只在64位系统下出现,32位不管你顺序怎么摆似乎都是ok的,因此推测为64位下gnu工具链的bug


所以在写链接参数时,尽可能地遵循一个原则就是把越底层的东西越往后放来避免这个问题。

但是如果有环形引用的情况怎么办我也不知道了,也许只能写个空调用了……


如果谁知道更详细的信息请告诉我啊,否则以后工程里只敢用静态库了……


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值