ld链接器问题: – 整个归档选项

我所看到的--whole-archive连接器选项的唯一真正用处是从静态库创build共享库。 最近我遇到了Makefile(s),它在与内部静态库链接时总是使用这个选项。 这当然会导致可执行文件不必要地引用未引用的目标代码。 我对此的反应是,这显然是错误的,我在这里错过了什么?

我有第二个问题必须处理我阅读有关整个档案选项,但不能parsing。 如果可执行文件还与一个共享库链接,而这个共享库又与静态库(部分)具有相同的目标代码,那么应该在与静态库链接时使用--whole-archive选项。 这就是共享库和静态库在目标代码方面有重叠。 使用此选项将强制所有符号(无论使用)在可执行文件中parsing。 这是为了避免目标代码重复。 这是令人困惑的,如果一个符号在程序中被引用,它必须在链接时唯一地解决,那么这个业务是如何重复的呢? (请原谅,如果这段不是清晰的缩影)

谢谢

在将可执行文件与静态库链接时,有一些合法的使用--whole-archive 。 一个例子是构buildC ++代码,其中全局实例在其构造函数中“注册”自己(警告:未经testing的代码):

main.cc

 typedef void (*handler)(const char *protocol); typedef map<const char *, handler> M; M m; void register_handler(const char *protocol, handler) { m[protocol] = handler; } int main(int argc, char *argv[]) { for (int i = 1; i < argc-1; i+= 2) { M::iterator it = m.find(argv[i]); if (it != m.end()) it.second(argv[i+1]); } } 

http.cc(libhttp.a的一部分)

 class HttpHandler { HttpHandler() { register_handler("http", &handle_http); } static void handle_http(const char *) { /* whatever */ } }; HttpHandler h; // registers itself with main! 

请注意, main.cc需要http.cc中没有符号。 如果你把这个链接起来

 g++ main.cc -lhttp 

不会得到一个链接到主要可执行文件的http处理程序,并且将无法调用handle_http() 。 将此与您链接时发生的情况作对比:

 g++ main.cc -Wl,--whole-archive -lhttp -Wl,--no-whole-archive 

在plain-C中,也可以使用相同的“自注册”风格,例如使用__attribute__((constructor)) GNU扩展。

--whole-archive另一个合法用途是工具箱开发人员在一个静态库中分发包含多个特性的库。 在这种情况下,提供者不知道图书馆的哪些部分将被消费者使用,因此必须包括所有内容。

我同意使用-whole-archive构build可执行文件可能不是您想要的(由于链接不需要的代码和创build臃肿的软件)。 如果他们有足够的理由这样做,他们应该在构build系统中logging下来,就像现在你可以猜测一样。

至于你的问题的第二部分。 如果可执行文件链接静态库和静态库(部分)与静态库相同的目标代码的dynamic库,那么-whole-archive将确保在链接时静态库中的代码是首选的。 这通常是你做静态链接时想要的。

旧的查询,但在你的第一个问题(“为什么”),我已经看到 – 整个库也用于内部库,主要是为了绕开这些库之间的循环引用。 它倾向于隐藏图书馆的贫穷build筑,所以我不推荐它。 不过,这是快速试用的快速方法。

对于第二个查询,如果在共享对象和静态库中存在相同的符号,则链接器将首先满足与它所遇到的任何库相关的引用。
如果共享库和静态库有精确的代码共享,这可能都是正常的。 但是在共享库和静态库有相同符号的不同实现的地方,你的程序仍然可以编译,但是依据库的顺序会有不同的performance。

强制所有符号从静态库中加载是消除混淆从哪里加载什么的一种方法。 但总的来说,这听起来像解决了错误的问题。 你大多不会在不同的库中需要相同的符号。

在处理静态库增量链接时,– --whole-archive是一个很好的scheme。

让我们假设:

  1. libA实现了a()b()函数。
  2. 程序的某些部分只能与libA链接,例如由于某些函数使用 – --wrap (一个经典的例子是malloc
  3. libC实现了c()函数,并使用a()
  4. 最后的程序使用a()c()

增量链接步骤可以是:

 ld -r -o step1.o module1.o --wrap malloc --whole-archive -lA ld -r -o step2.o step1.o module2.o --whole-archive -lC cc step3.o module3.o -o program 

无法插入 – 整个归档将剥离program无论如何使用的函数c() ,阻止正确的编译过程。

当然,这是一个特殊的情况,在这种情况下必须进行增量链接,以避免将所有对malloc调用封装在所有模块中,但是这种情况是由--whole-archive成功支持的。