ld链接器问题:--whole-archive选项

时间:2009-04-30 06:13:21

标签: build-process linker static-libraries

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

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

谢谢

5 个答案:

答案 0 :(得分:59)

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

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!

请注意,http.ccmain.cc不需要符号g++ main.cc -lhttp 。如果您将其链接为

handle_http()

您将获取链接到主可执行文件的http处理程序,并且无法调用g++ main.cc -Wl,--whole-archive -lhttp -Wl,--no-whole-archive 。将此与您链接时发生的情况进行对比:

__attribute__((constructor))

在普通C中也可以使用相同的“自注册”样式,例如:使用{{1}} GNU扩展名。

答案 1 :(得分:9)

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

答案 2 :(得分:4)

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

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

答案 3 :(得分:3)

旧查询,但在您的第一个问题上(&#34;为什么&#34;),我已经看到--whole-archive也用于内部库,主要是为了回避这些库之间的循环引用。它往往隐藏了图书馆的糟糕架构,所以我不推荐它。然而,这是快速试用的快捷方式。

对于第二个查询,如果共享对象和静态库中存在相同的符号,则链接器将使用它首先遇到的库来满足引用。
如果共享库和静态库具有完全共享的代码,那么这可能只是起作用。但是,在共享库和静态库具有相同符号的不同实现的情况下,您的程序仍将编译,但基于库的顺序将表现不同。

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

答案 4 :(得分:0)

使用--whole-archive的另一个好方案是处理静态库增量链接时。

让我们假设:

  1. libA实现了a()b()函数。
  2. 该计划的某些部分必须仅与libA相关联,例如由于使用--wrap进行了一些功能包装(经典示例为malloc
  3. libC实现c()个功能并使用a()
  4. 最终计划使用a()c()
  5. 增量链接步骤可以是:

    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成功支持这种情况。 / p>