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

41 linker build-process static-libraries

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

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

谢谢

Emp*_*ian 67

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

main.cc

typedef void (*handler)(const char *data);
void register_handler(const char *protocol, handler h);
handler get_handler(const char *protocol);
Run Code Online (Sandbox Code Playgroud)

http.cc(libhttp.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;
}
Run Code Online (Sandbox Code Playgroud)

请注意,http.ccmain.cc需求中没有符号.如果你把它链接为

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

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

#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]);
   }
}
Run Code Online (Sandbox Code Playgroud)

在plain-C中也可以使用相同的"自注册"样式,例如使用__attribute__((constructor))GNU扩展.

  • Russion如果可以构建libhttp.a,那么它证明了libhttp.a中存在register_handler函数.那么这个函数怎么能引用main.cc中的register_handler呢?所以这种情况下我们必须用另一种方式来实现你的想法. (2认同)
  • @longbkit我已经更新了答案,以便处理程序被分解到较低级别的库中,因为它需要如此。我抵制住了将“handler”类型从 C 函数指针更改为 C++“std::function”的诱惑。 (2认同)
  • @David如果您只想提取一组特定的符号,而不是整个档案,那么“--whole-archive”(显然)是不合适的。请改用“-Wl,-u,needed_symbol”。 (2认同)

小智 11

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

  • S /共享/静态/ (5认同)
  • 绝对不正确。当您创建库时,无论是静态还是动态,它将包含所有命名的目标文件。 (4认同)
  • 静态库包含不使用--whole-archive的所有内容,这似乎是一件毫无意义的事情 (2认同)

lot*_*har 5

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

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


ilp*_*lle 5

另一个很好用的场景--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
Run Code Online (Sandbox Code Playgroud)

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

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