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
我一直在寻找一个相当复杂的产品中的一些 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