ld链接器在创建共享文件时从静态库中删除目标文件

时间:2012-12-30 14:48:31

标签: c linux linker ld

我有许多静态库,我将它们链接到一个共享库中。其中一个,比如说libUsefulFunc.a包含一个带有函数的有用函数的对象文件usefulFunc.o,它只用于另一个静态库,比如说usingFunc(),驻留在libUsingFunc.a中托管的usingFunc.c

问题是链接器丢弃有用的.Func.o并且我得到错误“未定义的引用”。我尝试了两种连接顺序。

我使用我能想到的最简单的文件重新创建了这种情况:

A.H

extern int foo();

交流转换器

#include "a.h"
int foo()
{
    return 13;
}

b.c

#include "a.h"

extern int b()
{
  return print("a = %d\n", foo());
}

建立一切:

gcc -c a.c -o a.o
gcc -c b.c -o b.o
ar q b.a b.o
ar q a.a a.o
ld -shared -o test.so ./b.a ./a.a
nm ./test.so 
00001034 A __bss_start
00001034 A _edata
00001034 A _end

如果我提供目标文件而不是档案:

ld -shared -o test.so ./a.o ./b.o
nm ./test.so 
00001220 a _DYNAMIC
00000000 a _GLOBAL_OFFSET_TABLE_
00001298 A __bss_start
00001298 A _edata
00001298 A _end
000001a0 T b
00000194 T foo
         U print

有没有办法告诉链接器不要丢弃他认为未使用的目标文件而不必列出所有目标文件?我知道有一个--whole-archive选项,但是我将这个库构建为Android NDK项目的一部分,并没有找到为特定库1传递此选项的方法。

更新我完全理解我的原始问题并找到了正确的解决方案。首先是我上面的示例:链接器从入口点开始,并搜索它们使用的所有符号。这些在当前库中查找。一旦找到,它会将它们使用的符号添加到其列表中,因此强制使用。这些库只按进程一次进行,并按照它们在命令行中出现的顺序进行。因此,如果第二个库使用第一个库中的符号 - 符号将保持未定义,因为链接器不会返回。所以在我的例子中我应该告诉他b()将被外部调用,我可以通过使用--undefined = b来实现:

ld -shared -o test.so --undefined=b ./b.a ./a.a

在最初的问题中,我有两个静态库之间的循环引用。 好像我在b档案中有一个文件b1.c,其函数foo_b()是从foo()调用的。对于这种情况,我找到了3种可能的解决方案:

  1. 列出两次b:ld -shared -o test.so --undefined = b ./b.a ./a.a ./b.a
  2. 使用--whole-archive
  3. 使用--start-group archives --end-group 选项。重复搜索指定的存档,直到没有新的存档 未定义的引用已创建。
  4. 对于Android NDK库,只有第一个和第二个选项似乎可用,因为NDK的makefile没有提供指定存档组的方法

    希望这对其他人也有用!

3 个答案:

答案 0 :(得分:1)

尝试使用--whole-archive选项:

ld -shared -o test.so --whole-archive ./a.a ./b.a

答案 1 :(得分:1)

好的,如果它对追随者有用,我发现了一个非常有用(和奇怪)的行为。

如果您在命令行上有此链接顺序:

-lsomething1 -lsomething2

如果libsomething1.a中包含的各种.o文件“互相引用”(在它们之间有一些共享方法等),并且至少有一个.o文件具有“有用/”的依赖关系使用“由程序,然后所有的链接.o文件将被加载”当该库链接“(基本上,链接器点击-lsomething1命令时)。

然后链接器继续并尝试加载库“something2”。 如果“something2”中存在“something2”依赖于“something1”

中的.o文件

a)如果“something1”中的.o文件之间存在依赖关系,那么它将加载,即使之前未使用该特定依赖项。

b)如果something1中的.o文件之间没有相互依赖关系,则.o文件可能正如OP所述,在加载时被“丢弃”,因此无法使用。

所以基本上它可能“有时”在something1中拥有满足与something2的链接时间依赖关系的对象,并且链接器很高兴,即使它们是在“错误的顺序”中指定的(something1)然后是something2,在这种情况下,正确的顺序与此相反,满足依赖关系的东西会在链接链中出现。太混乱了。

显然,如果它注意到它在加载时需要/想要一个.o文件,它会加载整个.o文件,而不仅仅是它知道它需要/想要的符号。

因此,如果您不小心删除了相互依赖关系,您可能会突然遇到之前不存在的undefined reference个失败(通常可以通过将“-l”命令置于“正确”顺序来对抗)。这也意味着您可以“意外地”满足前向依赖性,这不是ld通常的工作方式。去搞清楚。您可以意外地满足前向依赖[!]

通过将-Wl,-verbose添加到链接命令行,您可以更准确地查看正在发生的事情(包含或不包含.o文件)。

您可以使用nm命令(例如:nm libmylib.a)或者如果您正在交叉,可以查看.a文件(及其符号)中包含哪些文件(及其符号)编译类似i686-mingw-nm libmylib.a之类的内容。 GL!

答案 2 :(得分:0)

避免将静态对象链接到共享库中,因为共享库最好包含position independent code(以避免在动态链接时进行过多的重定位)。

在实践中,使用foo.c重新编译gcc -fPIC -O -Wall -c foo.c -o foo.pic.o之类的所有源文件,并将它们全部链接起来以构建您的共享库,例如gcc -shared *.pic.o -o libshared.so(您可以将所需的库链接到.so