理解 _GLIBCXX_USE_CXX11_ABI: 兼容性与现代化之间的平衡
随着 C++ 标准的不断演进,编译器和标准库实现也在不断更新,以支持新的语言特性和库功能。然而,这些更新有时会引入不兼容的更改,特别是应用程序二进制接口(ABI)的更改。在 GCC 5.1 版本中,引入了新的 C++ 标准库 ABI,这一变化通过 _GLIBCXX_USE_CXX11_ABI
宏进行控制。本文将详细介绍这个宏的作用及其在项目中的使用方法。
什么是 ABI?
ABI(Application Binary Interface)定义了程序二进制接口,包括函数调用约定、参数传递方式、数据结构布局、库函数名称修饰等。ABI 的一致性对于确保编译的二进制文件能够正确链接和运行至关重要。
GCC 5.1 引入的新 ABI
在 GCC 5.1 之前,C++ 标准库使用旧的 ABI。当 GCC 5.1 引入了对 C++11 的更好支持时,同时引入了新的 ABI,这些更改解决了一些长期存在的问题,如改善了 std::string
和 std::list
的实现,但也引入了一些不兼容性。
_GLIBCXX_USE_CXX11_ABI 宏
为了在新旧 ABI 之间提供兼容性,GCC 引入了 _GLIBCXX_USE_CXX11_ABI
宏。这个宏可以在编译时定义,以控制编译器使用哪个 ABI。
_GLIBCXX_USE_CXX11_ABI=0
:使用旧的 ABI(GCC 5.1 之前的 ABI)。_GLIBCXX_USE_CXX11_ABI=1
:使用新的 ABI(GCC 5.1 及之后的 ABI)。
何时使用 _GLIBCXX_USE_CXX11_ABI
在以下情况下,你可能需要使用 _GLIBCXX_USE_CXX11_ABI
宏:
-
与旧库兼容:如果你的项目依赖于使用旧 ABI 编译的第三方库,你需要使用旧 ABI 来避免链接和运行时的兼容性问题。
-
逐步迁移:如果你正在逐步迁移到新的 ABI,可以使用这个宏在项目的不同部分控制 ABI 的使用,以确保在过渡期间的兼容性。
怎么使用 _GLIBCXX_USE_CXX11_ABI
1、命令行
g++ -D_GLIBCXX_USE_CXX11_ABI=0 xxx.cpp
g++ -D_GLIBCXX_USE_CXX11_ABI=1 xxx.cpp
2、cmake
为一个目标全局指定abi
target_compile_definitions(moduleA PRIVATE _GLIBCXX_USE_CXX11_ABI=0)target_compile_definitions(moduleB PRIVATE _GLIBCXX_USE_CXX11_ABI=1)
单个cpp文件指定不同的abi,可与上面全局指定同时存在
set_source_files_properties(xxx.cpp PROPERTIES COMPILE_DEFINITIONS _GLIBCXX_USE_CXX11_ABI=0)
特殊场景当一个可执行程序同时依赖一个旧ABI库和一个新ABI库时的处理
源码liba.cpp
#include <string>
std::string aaaaaaaaaa(){
return "this is a";
}
源码libb.cpp
#include <string>
std::string bbbbbbbbbb(){
return "this is b";
}
使用liba.so的源码a.cpp
#include <string>
#include <iostream>
using namespace std;
extern string aaaaaaaaaa();
int use_aaaaaaaaaa()
{
cout << aaaaaaaaaa() << endl;
return 0;
}
使用libb.so的源码b.cpp
#include <string>
#include <iostream>
using namespace std;
extern string bbbbbbbbbb();
int use_bbbbbbbbbb()
{
cout << bbbbbbbbbb() <<endl;
return 1;
}
源码main.cpp
#include <string>
#include <iostream>
using namespace std;
extern int use_aaaaaaaaaa();
extern int use_bbbbbbbbbb();
int main()
{
cout << use_aaaaaaaaaa() << endl;
cout << use_bbbbbbbbbb() << endl;
cout << "hello" << endl;
return 0;
}
Makefile
all: liba.so libb.so main
liba.so:
g++ -D_GLIBCXX_USE_CXX11_ABI=0 liba.cpp -shared -fPIC -o liba.so
libb.so:
g++ -D_GLIBCXX_USE_CXX11_ABI=1 libb.cpp -shared -fPIC -o libb.so
main: main.o a.o b.o
g++ -o $@ $+ -L. -la -lb
a.o: a.cpp
g++ -D_GLIBCXX_USE_CXX11_ABI=0 -fPIC -c -o $@ $<
b.o: b.cpp
g++ -D_GLIBCXX_USE_CXX11_ABI=1 -fPIC -c -o $@ $<
main.o: main.cpp
g++ -D_GLIBCXX_USE_CXX11_ABI=1 -fPIC -c -o $@ $<
clean:
$(RM) liba.so libb.so main *.o
test:
LD_LIBRARY_PATH=. ./main
两个so的符号对比
liba.so 使用旧ABI
libb.so 使用新ABI,符号多了cxx11
这两个库不能直接同时在一个cpp中使用,要么用g++4.8编译找不到libb.so里面的bbbbbbbbbb函数定义,要么用g++5.1找不到liba.so里面aaaaaaaaaaa函数定义。究其原因是生成的符号不兼容会无法找到
如果用高于5.1的g++编译,并且
使用liba.so的编译模块a.cpp用-D_GLIBCXX_USE_CXX11_ABI=0编译,且a.cpp不要导出使用了std::string 和std::list的函数
使用libb.so的编译模块b.cpp用-D_GLIBCXX_USE_CXX11_ABI=1编译,且b.cpp不要导出使用了std::string 和std::list的函数
最后编译出来的可执行程序就能兼容两种ABI。
结论
通过使用 _GLIBCXX_USE_CXX11_ABI
宏,你可以灵活地控制项目中不同部分使用的 ABI,从而在保持与旧库兼容的同时,逐步迁移到新的 C++ 标准库 ABI。了解并正确使用这个宏,可以帮助你在项目中平衡现代化与兼容性。
希望本文能帮助你更好地理解和使用 _GLIBCXX_USE_CXX11_ABI
宏,使你的项目在面对 ABI 变化时更加灵活和健壮。
参考链接
https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_dual_abi.html