GCC 链接器从 7.x 更改为 8.x

GCC linker changes from 7.x to 8.x

我一直在寻找一个相当复杂的产品中的一些 linker 错误,我 运行 的结果真是太疯狂了。下面是一个非常小的例子,展示了我关心的行为。我试图理解这种行为。如果行为的改变是有意的,是否可以追溯到 gcc 更新日志以便我将来可以依赖它?

这确实是一个 link 排序问题,似乎 link 不同版本的用户以意想不到的方式表现不同。

第一个共享库(libtest1.so):

void test2();
void test1()
{
  test2();
}

第二个共享库(libtest2.so):

#include <iostream>
void test2()
{
  std::cout << “calling test 2” << std::endl;
}

主程序:

#include “test1.hpp”
int main()
{
  test1();
}

当我构建共享库时,我不会让一个库依赖于另一个库。我只是将未解析的符号留在 test1 中,然后将 test1 和 test2 都放在 link 编译 main 的设置中。不要担心 rpath 和 LD_LIBRARY_PATH 之类的东西;这些在讨论中无关紧要。

使用 gcc 7.5.0

g++ -std=c++11 main.cpp -o main -L. -ltest1 -ltest2    //compiles OK
g++ -std=c++11 main.cpp -o main -L. -ltest2 -ltest1    //link fails; symbol test2 is unresolved
// This makes 100% good sense to me.  The linker is a single pass linker and order matters. 
// We know and understand this.

使用 gcc 8.3.0 或 gcc 9.2.0

g++ -std=c++11 main.cpp -o main -L. -ltest1 -ltest2    //compiles OK
g++ -std=c++11 main.cpp -o main -L. -ltest2 -ltest1    //compiles OK
// This totally breaks my brain!!!  This makes it seem like the linker in the newer gcc toolchains 
// is a multi pass linker which is doing a lot more work to find the symbols that are needed to 
// correctly link.  This seems like a HUGE change in the way the linker works.  However, I have
// scoured the change logs for the compilers and I haven’t been able to find any mention that the 
// way the linker operates has changed in any significant way.  This scares me and makes me worry
// that we can’t depend on this behavior consistently.

那么这里的故事是什么?我看不出更新的 linker 版本如何成功地多次传递目标文件。 gcc linker 变得更聪明了吗?如果是这样,它会变得更聪明吗?它会继续变得更聪明吗?如果您能提供任何帮助,我们将不胜感激。

两个编译器可能使用相同的链接器,因此链接器并没有变得更智能。这一切都在编译器默认传递给链接器的链接器选项中。 -v是你的朋友。

这些命令在任一版本的编译器中的工作方式相同。

g++ -Wl,-as-needed -std=c++11 main.cpp -o main -L. -ltest2 -ltest1 # fails
g++ -Wl,-no-as-needed -std=c++11 main.cpp -o main -L. -ltest2 -ltest1 #succeeds