You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

gcc 4.x与gcc 6.x之间libstdc++的make_shared内存布局是否变化?

问题根源:跨GCC版本混用旧ABI时std::make_shared的内部实现不兼容

首先要明确一个关键事实:_GLIBCXX_USE_CXX11_ABI=0强制启用的旧ABI(C++03时代的ABI)并不是完全一成不变的。GCC在维护旧ABI的过程中,会为了修复bug、优化实现对标准库内部细节做调整——而这些调整在跨版本编译的代码中,就可能引发兼容性问题,你的例子就是典型场景。

具体崩溃原因拆解

你的例子中,foo.cxx用GCC4.8.2编译,main.cxx用GCC6.2.0编译(都指定旧ABI),问题出在std::make_shared<X>的内部实现上:

  • std::make_shared的核心优化是一次性分配对象实例和shared_ptr的控制块(_Sp_counted_ptr_inplace)的内存,把两者打包在同一块内存区域里。
  • GCC4.8.2和GCC6+的旧ABI版本中,这个打包内存块的布局(比如控制块的大小、内部成员的偏移)发生了不兼容的变化。
  • 当foo()调用make_shared时,用的是GCC4.8.2的布局逻辑来写入内存,但GCC6+编译的主程序(带ASAN)会按照自己的布局预期来检查内存访问——这就导致了ASAN检测到堆缓冲区溢出:写入的位置超出了GCC6+预期的内存范围。

为什么GCC5.4没问题?

GCC5.x是C++11 ABI切换的过渡版本,它的旧ABI实现和GCC4.8.x的兼容性做得更好,没有引入导致这种内存布局冲突的变更。而GCC6+对旧ABI的内部实现做了更多调整(比如适配新特性的回退逻辑、修复共享指针的线程安全bug),这些调整在跨版本混用旧ABI时就暴露了问题。

为什么只针对std::make_shared?

std::make_shared的内存打包逻辑是标准库的私有实现细节,不属于公开ABI的范畴。而如果改用std::shared_ptr<X>(new X()),对象和控制块是分开分配的,shared_ptr本身的接口和内存布局属于公开ABI,跨版本混用旧ABI时兼容性更好。但make_shared的内部组合方式没有强制的ABI约束,不同GCC版本可以自由调整,这就导致了跨版本崩溃。

解决方案

  1. 统一编译版本:所有翻译单元必须用同一版本的GCC编译,哪怕都指定旧ABI也不能跨版本混用。
  2. 替换make_shared:如果必须跨版本编译,暂时改用std::shared_ptr<X>(new X())替代std::make_shared<X>(),牺牲一点内存分配效率来保证兼容性。

内容的提问来源于stack exchange,提问作者Barry

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:43:15