【发布时间】:2012-12-15 00:38:40
【问题描述】:
我有许多静态库,我将它们链接到一个共享库中。其中一个,比如 libUsefulFunc.a 包含一个对象文件 usefulFunc.o,它带有一个函数,有用的Func(),它只能从另一个静态库中使用,比如说 usingFunc(),驻留在 libUsingFunc.a 中的 usingFunc.c 中
问题是链接器丢弃了有用的Func.o,我得到错误“未定义的引用”。我尝试了两种链接顺序。
我用我能想到的最简单的文件重新创建了这种情况:
啊。
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 项目的一部分,但没有找到一种方法为特定库传递此选项。
更新我已经完全理解了我原来的问题并找到了正确的解决方案。首先是我上面的示例:链接器从入口点开始并搜索它们使用的所有符号。这些在当前库中查找。一旦找到,它就会将他们使用的符号添加到其列表中,从而强制执行。这些库仅是一次进程,并且按照它们在命令行中出现的顺序。因此,如果第二个库使用第一个库中的符号 - 该符号将保持未定义,因为链接器不会返回。所以在我的例子中,我应该告诉他 b() 将被外部调用,我可以使用 --undefined=b:
ld -shared -o test.so --undefined=b ./b.a ./a.a
在最初的问题中,我遇到了两个静态库之间的循环引用。 就好像我在 b 存档中有一个文件 b1.c,它具有从 foo() 调用的函数 foo_b()。对于这种情况,我找到了 3 种可能的解决方案:
- 列出 b 两次:ld -shared -o test.so --undefined=b ./b.a ./a.a ./b.a
- 使用 --whole-archive
- 使用 --start-group 归档 --end-group 选项。重复搜索指定的档案,直到没有新的 未定义的引用被创建。
对于 Android NDK 库,似乎只有第一个和第二个选项可用,因为 NDK 的 makefile 不提供指定存档组的方法
希望这对其他人也有用!
【问题讨论】:
-
第一次调用
ld不应该读成ld -shared -o test.so ./b.a ./a.a吗? -
对于那些还想知道是否真的需要使用
extern关键字和/或会影响这里发生的事情的人,这个答案:stackoverflow.com/a/856736/694576 可能很有趣。 -
在带有 GCC 4.7.1 的 Mac OS X 10.7.5 上,我不得不用
gcc替换ld命令(ld命令说“未知选项-shared,但是 GCC知道该怎么做)。完成后,假设第一个ld行引用了a.a和b.a(而不是问题中写的.o文件),第一个共享对象是空的——没有从任何一个存档库复制.我不相信这是出乎意料的行为;根据我的经验,共享库不会被构建,因此它直接包含来自其他库(静态或共享)的材料,尽管它可能包含对它们的引用。