63

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

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

谢谢

4

5 回答 5

94

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

处理程序.h

typedef void (*handler)(const char *data);
void register_handler(const char *protocol, handler h);
handler get_handler(const char *protocol);

handlers.cc(libhandlers.a 的一部分)

typedef map<const char*, handler> HandlerMap;
HandlerMap m;
void register_handler(const char *protocol, handler h) {
   m[protocol] = h;
}
handler get_handler(const char *protocol) {
   HandlerMap::iterator it = m.find(protocol);
   if (it == m.end()) return nullptr;
   return it->second;
}

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

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

主文件

#include <handlers.h>
int main(int argc, char *argv[])
{
    for (int i = 1; i < argc-1; i+= 2) {
        handler h = get_handler(argv[i]);
        if (h != nullptr) h(argv[i+1]);
    }
}

http.cc请注意,该main.cc需求中没有符号。如果您将其链接为

g++ main.cc -lhttp -lhandlers

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

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

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

于 2009-05-09T05:30:32.410 回答
12

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

于 2011-02-07T13:58:55.077 回答
6

另一个很好--whole-archive用的场景是处理静态库增量链接时。

让我们假设:

  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

未能插入 --whole-archive 将剥离c()无论如何使用的函数program,从而阻止正确的编译过程。

当然,这是一个特殊的极端情况,其中必须进行增量链接以避免将所有调用包装到malloc所有模块中,但是--whole-archive.

于 2017-01-10T20:46:26.980 回答
5

我同意使用—whole-archive来构建可执行文件可能不是您想要的(由于链接到不需要的代码并创建了臃肿的软件)。如果他们有充分的理由这样做,他们应该在构建系统中记录它,因为现在你只能猜测了。

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

于 2009-05-04T18:24:03.693 回答
3

旧查询,但在您的第一个问题(“为什么”)上,我已经看到 --whole-archive 也用于内部库,主要是为了回避这些库之间的循环引用。它往往会隐藏库的不良架构,所以我不推荐它。然而,这是一种快速试用的快速方法。

对于您的第二个查询,如果共享对象和静态库中存在相同的符号,则链接器将满足它首先遇到的任何库的引用。
如果共享库和静态库具有精确的代码共享,那么这一切都可以正常工作。但是,如果共享库和静态库对相同符号有不同的实现,您的程序仍然可以编译,但会根据库的顺序表现出不同的行为。

强制从静态库中加载所有符号是消除对从何处加载的混淆的一种方法。但总的来说,这听起来像是解决了错误的问题;您通常不希望在不同的库中使用相同的符号。

于 2015-10-27T16:32:42.680 回答