C++ ABI 兼容 | C & C++

本文介绍了 C++ 中的 ABI 兼容问题与解决方法。

什么是 ABI 兼容问题?

当我们的应用程序引用了一个以二进制形式发布的库时,在源代码层面,我们使用了这个库的 API;而在编译、链接之后,在运行时,我们的应用程序通过 ABI 在与这个库通信。从这个角度看来,ABI 只是 API 的底层实现,所以多数时候我们才不需要去关心这个问题。当人们提及 ABI 兼容时,一般主要是在说 Binary-compatible 即二进制兼容性。

C++ 如何解决 ABI 兼容问题

在 GCC 5.1 版本中,libstdc++ 引入了一个新的库 ABI,为了符合 2011 C++ 标准,其中包括 std::stringstd::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)

参考

你们说的ABI,Application Binary Interface到底是什么东西?- Aman的回答

Dual ABI

作者

zhongtian

发布于

2022-02-09

更新于

2023-12-16

许可协议

评论