3

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

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

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

extern int foo();

交流

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

公元前

#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 项目的一部分,并且没有找到一种方法为特定库传递此选项。

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

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

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

  1. 列出 b 两次: ld -shared -o test.so --undefined=b ./ba ./aa ./ba
  2. 使用 --whole-archive
  3. 使用 --start-group archives --end-group 选项。重复搜索指定的档案,直到没有创建新的未定义引用。

对于 Android NDK 库,似乎只有第一个和第二个选项可用,因为 NDK 的 makefile 不提供指定存档组的方法

希望这对其他人也有用!

4

3 回答 3

1

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

ld -shared -o test.so --whole-archive ./a.a ./b.a
于 2012-12-30T15:02:31.970 回答
1

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

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

-lsomething1 -lsomething2

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

然后链接器继续并尝试加载库“something2”。如果“something2”中存在对“something1”中的 .o 文件的依赖关系

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

b)如果 .o 文件之间没有相互依赖关系,something1那么 .o 文件可能就像 OP 所说的那样,在加载时“被丢弃”,因此将不可用。

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

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

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

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

nm您可以通过使用命令(例如:)nm libmylib.a或交叉编译类似i686-mingw-nm libmylib.a或类似的东西来查看您的 .a 文件中包含哪些 .o 文件(及其符号)(全部一起) 。GL!

于 2016-01-08T01:18:10.490 回答
0

避免将静态对象链接到共享库,因为共享库最好包含与位置无关的代码(以避免在动态链接时进行过多的重定位)。

在实践中,重新编译每个源文件,如foo.cwithgcc -fPIC -O -Wall -c foo.c -o foo.pic.o并将它们全部链接以创建您的共享库,例如gcc -shared *.pic.o -o libshared.so(并且您可以将所需的库链接到您的.so.

于 2012-12-30T19:31:31.073 回答