GCC 4.5与4.4链接依赖项的区别

10

我注意到在尝试在GCC 4.4和GCC 4.5上进行相同操作时存在差异。由于我正在处理的代码是专有的,因此无法提供它,但我通过这个简单的测试案例观察到了类似的失败情况。

基本上,我的目标是让一个共享库(libb)依赖于另一个共享库(liba)。当加载libb时,我假设也应该加载liba,即使libb不一定使用liba中的符号。

我所观察到的是,当我使用GCC 4.4编译时,我会发现会加载liba,但如果我使用GCC 4.5编译,则不会加载libb。

我有一个小的测试用例,包括两个文件a.c和b.c。它们的内容如下:

//a.c
int a(){ 
    return 0; 
}

//b.c
int b(){
    return 0;
}
//c.c
#include <stdio.h>
int a();
int b();

int main()
{
    printf("%d\n", a()+b());
    return 0;
}
//test.sh    
$CC -o liba.so a.c -shared
$CC -o libb.so b.c -shared -L. -la -Wl,-rpath-link .
$CC c.c -L. -lb -Wl,-rpath-link .
LD_LIBRARY_PATH=. ./a.out

这是我使用不同版本的GCC编译得到的输出结果

$ CC=gcc-4.4 ./test.sh
1
$ CC=gcc-4.5 ./test.sh
/tmp/cceJhAqy.o: In function `main':
c.c:(.text+0xf): undefined reference to `a'
collect2: ld returned 1 exit status
./test.sh: line 4: ./a.out: No such file or directory
$ CC=gcc-4.6 ./test.sh
/tmp/ccoovR0x.o: In function `main':
c.c:(.text+0xf): undefined reference to `a'
collect2: ld returned 1 exit status
./test.sh: line 4: ./a.out: No such file or directory
$ 

有人能解释一下发生了什么吗?另外一个额外的信息是,对于GCC 4.4上的libb.so的ldd确实显示了liba.so,但对于GCC 4.5却没有。

编辑

我将test.sh更改为以下内容:

$CC -shared -o liba.so a.c
$CC -L. -Wl,--no-as-needed -Wl,--copy-dt-needed-entries -la -shared -o libb.so b.c -Wl,-rpath-link . 
$CC -L. c.c -lb -Wl,-rpath-link . 
LD_LIBRARY_PATH=. ./a.out

使用GCC 4.5,这将产生以下输出:

/usr/bin/ld: /tmp/cc5IJ8Ks.o: undefined reference to symbol 'a'
/usr/bin/ld: note: 'a' is defined in DSO ./liba.so so try adding it to the linker command line
./liba.so: could not read symbols: Invalid operation
collect2: ld returned 1 exit status
./test.sh: line 4: ./a.out: No such file or directory

我的编译环境是Ubuntu 11.10 x86-64。 - Arne Bergene Fossaa
在Debian 6.0.3上,打包的GCC 4.4可以轻松处理该示例。打包的GCC 4.3会抱怨缺少“-fPIC”标志。 - Fred Foo
1个回答

12

在链接过程中,ld 处理 DT_NEEDED 库的方式似乎发生了变化。以下是当前 man ld 的相关部分:

使用 --copy-dt-needed-entries 选项,命令行中提到的动态库将被递归搜索,按照它们所需的标签 DT_NEEDED 搜索其他库,以解决输出二进制文件需要的符号。但是,默认设置下,随后的动态库搜索将停止于该动态库本身。不会遍历 DT_NEEDED 链来解决符号。

(属于 --copy-dt-needed-entries 部分)。

在 GCC 4.4 和 GCC 4.5 之间的某个时候(显然是这样,参见这里 - 找不到真正权威的资料),默认值从递归搜索更改为不递归搜索(正如您在新版 GCC 中看到的那样)。

无论如何,在最后的链接步骤中,您都可以(也应该)通过指定 liba 来修复它:

$CC c.c -L. -lb -la -Wl,-rpath-link .

通过使用您的新编译器和以下命令行,您可以检查此链接器设置确实(至少部分)是问题所在:

$CC c.c -L. -Wl,--copy-dt-needed-entries -lb -Wl,--no-copy-dt-needed-entries \
         -Wl,-rpath-link .

那是一个非常好的解释,但添加标志并不会改变结果。我尝试在构建libb.so和c.c时都添加它们。 - Arne Bergene Fossaa
参考我的版本是GNU ld(Ubuntu的GNU Binutils)2.21.53.20110810 - Arne Bergene Fossaa
1
这真的不是一个选项,因为c.c可能是客户代码,他们只会看到libb。我真正想要的是一种方法,在加载libb时强制加载liba。 - Arne Bergene Fossaa
抱歉,如果继续深入讨论此问题将会暴露过多的内部信息。问题在于,在链接时某些不包含libb链接的liba版本可用,而在运行时另一个包含libb链接的liba版本可用。可以将liba视为“shim”库,它从libb加载所有符号。 - Arne Bergene Fossaa
1
这个答案无价,解决了我的问题,而且还解决了我在stackoverflow上的问题。非常感谢。 - Philippe
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接