如何检查为什么链接需要某些符号?

How to check why some symbol is required for linkage?

这个问题比较麻烦,让我们试试简短的版本: 通常,当您使用 unresolved symbol reference 失败时,它是相当直截了当的,在这里您调用了链接器无法找到的内容。您只需为您的链接器提供库,它就可以正常工作。有时,有些情况下,当你用头撞墙时,不明白为什么链接器在这里或那里想要这个符号,它没有被调用,至少没有被直接调用。是否有一个 tool/linker 开关可以解释为什么它认为需要该符号 "here"?

原题: 这都是关于静态链接的。我有一个小实用程序,几行代码,几个包含。该实用程序与名为 lib1 的库静态链接。假设 lib1 依赖于另一个库 lib2,因为 lib1 使用来自 lib2 的符号 sym1。但是,使用 lib2 中的 sym1 的任何东西都不是实用程序中的 used/called,也不是可能依赖于 lib2lib1 中的任何东西。然而,上述微型实用程序因 sym1 的未解析符号而失败。第一个问题是为什么?因为,在实用程序中,任何地方都不需要 sym1,甚至没有来自 lib1 的符号在实用程序中使用 sym1,为什么链接器首先要费心寻找这个符号?第二个问题,包含链有可能将符号 sym1 引入我的实用程序,然后它回答了 "why" 但它不应该引入它(至少没有明显的原因),所以第二个问题是我如何找到链接器认为实用程序需要 sym1 来自 lib2 的原因?

What/whenre/why: Linux, C/C++, GCC-9/Clang-9

好吧,显然我设法回答了没有看到代码和错误消息的问题。是时候打开我的 psi-consultancy 了。

关于 link 在 Linux/ELF 目标上,重要的是要记住 linker 在尝试 satisfy/resolve 符号时正在合并(并复制到最终executable) 部分(又名段)。通常应用程序有 .text(代码段)、.rodata(只读数据)段、.data(r/w 初始化数据)段、.bss(未初始化数据)等。因此,如果需要,符号在其中,比如说,一个编译文件中的三个函数,将选择文件的整个 .text 部分。如果未使用但出现在部分函数中调用其他内容,linker 将开始搜索 "something else" 以满足,即使它与应用程序无关。

此外,还有一些 C++ 特定的东西:对于 class 和虚函数编译器生成 vtable,带有指向每个虚函数的指针,并将这个 table 移动到 .rodata部分。请注意,那是什么 我们认为代码实际上是在(只读)数据部分结束的。

如果您定义了除一个虚函数之外的所有虚函数,link用户很可能会抱怨

之类的错误消息
/tmp/cc5YTcBb.o:(.rodata._ZTV3CL1[_ZTV3CL1]+0x18): undefined reference to `CL1::fnc2()

您可以在哪里看到问题出在 .rodata,而不是 .text。

这个故事的寓意:将您的代码和数据分成大量尽可能小的 sections/segments,您的 linking 原子。理想情况下,每个函数都进入自己的部分,以及一段初始化或 r/o 数据。

最后一步是指示 linker(通过 -Wl 选项)丢弃(垃圾收集)所有未使用的部分。

一般来说,linker 应该会使用更多的 RAM,link 阶段可能会更慢,但应用程序会更小更快。

命令行使用,看看GCC手册wrt选项的含义。

g++ -fdata-sections -ffunction-sections -fipa-pta main.cpp -Wl,--gc-sections -Wl,-O1 -Wl,--as-needed