这个问题很麻烦,让我们尝试一下简短的版本:
通常当你失败时 unresolved symbol reference
这是相当困难的,在这里你调用链接器找不到的东西。您只需向链接器提供库,它就可以工作了。有时,在某些情况下,当你把头撞在墙上时,不明白为什么链接器在这里或那里想要这个符号,它没有被调用,至少没有直接调用。是否有一个工具/链接器开关可以解释为什么它认为“此处”需要该符号?
原问题:
这都是关于静态链接的。我有一个小实用程序,几行代码,几个包含内容。该实用程序与名为 lib1
的库静态链接。可以说lib1
依赖于另一个库 lib2
,自 lib1
使用符号sym1
来自lib2
。但是没有任何使用 sym1
来自lib2
是从实用程序中使用/调用的,也不是 lib1
中的任何内容。这可能取决于 lib2
。然而,上述的小实用程序失败了,并显示 sym1
的未解析符号。 。第一个问题是为什么?因为,在实用程序中 sym1
任何地方都不需要,甚至不需要 lib1
中的符号使用 sym1
在实用程序中使用时,为什么链接器首先要费心寻找这个符号?第二个问题,包含链有可能引入符号sym1
到我的实用程序,然后它回答“为什么”,但不应该引入它(至少没有明显的原因),所以第二个问题是我如何找到为什么链接器认为实用程序需要 sym1
来自lib2
?
什么/何时/为什么:Linux、C/C++、GCC-9/Clang-9
最佳答案
好吧,显然我在没有看到代码和错误消息的情况下设法回答了问题。是时候开设我的 psi 咨询公司了。
关于 Linux/ELF 目标上的链接,重要的是要记住链接器在尝试满足/解析符号时正在合并(并复制到最终可执行文件)部分(也称为段)。通常应用程序有.text(代码段)、.rodata(只读数据)段、.data(读/写初始化数据)段、.bss(未初始化数据)等。因此,如果需要符号,则可以在三个函数之间在一个编译文件中,将选择文件的整个 .text 部分。如果未使用但存在于部分函数中,则调用其他内容,链接器将开始搜索“其他内容”来满足,即使它与应用程序无关。
另外,还有一些 C++ 特有的东西:对于具有虚函数的类,编译器会生成 vtable,其中包含指向每个虚函数的指针,并将该表移动到 .rodata 部分。注意,那是什么 我们认为代码实际上最终位于(只读)DATA 部分。
如果您定义了除一个之外的所有虚拟函数,链接器很可能会发出错误消息,例如
/tmp/cc5YTcBb.o:(.rodata._ZTV3CL1[_ZTV3CL1]+0x18): undefined reference to `CL1::fnc2()
您可以看到问题出在 .rodata,而不是 .text。
这个故事的寓意:将代码和数据切成大量可能的最小部分/段,即链接原子。理想情况下,每个函数都有自己的部分,以及初始化或 r/o 数据。
最后一步是指示链接器(通过 -Wl 选项)丢弃(垃圾收集)所有未使用的部分。
一般来说,链接器会使用更多的 RAM,链接阶段可能会更慢,但应用程序会更小、更快。
命令行使用,看看GCC手册wrt选项含义。
g++ -fdata-sections -ffunction-sections -fipa-pta main.cpp -Wl,--gc-sections -Wl,-O1 -Wl,--as-needed
关于c++ - 如何检查为什么链接需要某些符号?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62450033/