c++11 abi 兼容性

理解 _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::stringstd::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 宏:

  1. 与旧库兼容:如果你的项目依赖于使用旧 ABI 编译的第三方库,你需要使用旧 ABI 来避免链接和运行时的兼容性问题。

  2. 逐步迁移:如果你正在逐步迁移到新的 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

  • 21
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值