C++ ABI 兼容 | C & C++
本文介绍了 C++ 中的 ABI 兼容问题与解决方法。
什么是 ABI 兼容问题?
当我们的应用程序引用了一个以二进制形式发布的库时,在源代码层面,我们使用了这个库的 API;而在编译、链接之后,在运行时,我们的应用程序通过 ABI 在与这个库通信。从这个角度看来,ABI 只是 API 的底层实现,所以多数时候我们才不需要去关心这个问题。当人们提及 ABI 兼容时,一般主要是在说 Binary-compatible 即二进制兼容性。
C++ 如何解决 ABI 兼容问题
在 GCC 5.1 版本中,libstdc++ 引入了一个新的库 ABI,为了符合 2011 C++ 标准,其中包括 std::string
和 std::list
的新实现。
为了保持与 libstdc++ 链接的现有代码的向后兼容,库的 soname 没有更改,旧实现仍与新实现并行支持。这是通过在内联命名空间中定义新实现来实现的,因此它们具有不同的名称以用于链接目的,例如新版本的 std::list<int>
实际上被定义为 std::__cxx11::list<int>
。因为新实现的符号具有不同的名称,所以两个版本的定义可以存在于同一个库中。
_GLIBCXX_USE_CXX11_ABI
宏控制库头文件中的声明是使用旧 ABI 还是新 ABI。因此,可以为每个正在编译的源文件单独决定使用哪个 ABI。在大部分 GNU/Linux 发行版中,宏的默认值为 1,这会开启新 ABI。因此要使用旧 ABI,需要在引入任何库头文件之前将宏显式定义为 0。通过设置宏,可以直接将 C++03 和 C++11 代码链接在一起。
如果链接时出现涉及 std::__cxx11
命名空间的未定义引用的错误,则表明正在尝试将使用不同宏值编译的目标文件链接在一起。当链接到使用旧版本 GCC 编译的第三方库时,通常会发生这种情况。如果第三方库不能用新的 ABI 重新编译,那么需要用旧的 ABI 重新编译代码。
总结
- 带
std::__cxx11
的是新版实现(c++11规范),不带的是旧版实现(c++03规范,即 pre-cxx11) _GLIBCXX_USE_CXX11_ABI=0
链接旧版库(不带 cxx11)_GLIBCXX_USE_CXX11_ABI=1
链接新版库(带 cxx11)
参考
C++ ABI 兼容 | C & C++