Rom*_*n B 5 c++ linker compilation
我正在构建一个基于两个对象(add.o 和 sub.o)的库“libsimplemath.a”。
主要只依赖于 add.o 接口。当代码与 libsimplemath.a 链接时,我希望它可以工作。
在构建 libsimplemath.a 时,我决定将 -std 标志从 c++14 切换到 c++17。在这一点上,由于某些原因 sub.o 也由链接器带来,并且因为它包含未定义的符号链接失败。
要了解为什么 sub.o 被考虑在内,可以查看链接器映射文件:
Archive member included to satisfy reference by file (symbol)
./libsimplemath.a(add.o) main.o (add(int, int))
./libsimplemath.a(sub.o) main.o (std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string())
/usr/lib/x86_64-linux-gnu/libc_nonshared.a(elf-init.oS)
/usr/lib/gcc/x86_64-linux-gnu/8/../../../x86_64-linux-gnu/Scrt1.o (__libc_csu_init)
Run Code Online (Sandbox Code Playgroud)
一些 stl 符号,这里的 ~basic_string() 在 sub.o 中解析。
问题是:即使没有明确使用 sub.o 也会被考虑在内吗?如果是这样,我目前正在做的事情有什么问题?
语境:
$ g++ --version
g++ (Ubuntu 8.3.0-6ubuntu1) 8.3.0
$ ld --version
GNU ld (GNU Binutils for Ubuntu) 2.32
Run Code Online (Sandbox Code Playgroud)
添加.cpp
int add(int a, int b) { return a + b; }
Run Code Online (Sandbox Code Playgroud)
子.cpp
#include <string>
void undef_symb(void);
int sub(int a, int b)
{
std::string s;
undef_symb();
return a - b;
}
Run Code Online (Sandbox Code Playgroud)
主程序
#include <string>
int add(int, int);
int main()
{
std::string s = "Hello";
return add(0, 0);
}
Run Code Online (Sandbox Code Playgroud)
汇编
g++ -O0 -std=c++17 -o sub.o -c sub.cpp
g++ -O0 -std=c++17 -o add.o -c add.cpp
ar rc libsimplemath.a add.o sub.o
g++ -O0 -std=c++14 -o main.o -c main.cpp
g++ -o main main.o -L. -lsimplemath -Wl,-Map=main.g++.map
/usr/bin/ld: ./libsimplemath.a(sub.o): in function `sub(int, int)':
sub.cpp:(.text+0x2b): undefined reference to `undef_symb()'
collect2: error: ld returned 1 exit status
make: *** [Makefile:8: all] Error 1
Run Code Online (Sandbox Code Playgroud)
extern template class ...是一个完全正常的 C++ 声明,可以出现在用户代码中,无论选择何种 C++ 标准。您不应该自己实例化标准库中的事物(无论合法与否),但任何用户定义的类模板都会发生同样的事情。您可以不使用标准库和选项来构建几乎相同的示例-std=。只需制作一个模板,添加extern template class ...到一个文件中,不要将其添加到另一文件中。
没有任何标准和 ABI 可以保护您免受此类事件的影响。您始终需要小心链接到程序中的对象。
当然,这在实践中仍然是一个问题,恕我直言,这是 libstdc++ 中的一个错误(它不应该让简单的事情变得困难),但是一旦你知道发生了什么,就有一个简单的解决方法。
编译(使用-std=c++20 -O0)一个包含以下内容的对象:
// for the benefit of C++17 users
// who compile their libraries with -O0
#include <string>
std::string a;
Run Code Online (Sandbox Code Playgroud)
并在链接器有机会看到 之前将其链接到您的程序sub.o。例如,将其放在第一位libsimplemath.a。
您仍然可以在 C++17 和 C++20 之间正常互操作。不需要在链接时提供每个依赖项(这将是一件可怕的事情)。