GCC ABI炸弹

GCC ABI炸弹

GCC想要解决std::string/list等的实现缺陷,但是又没有办法直接做到旧版本兼容,于是引入了_GLIBCXX_USE_CXX11_ABI编译选择,设置为0就使用旧版本的ABI,设置为1就使用新版本。这会要求一个应用程序以及它依赖的所有动态库,同时使用相同的ABI标记。如果不是,最轻的就是在编译时报错,比如出现报错:undefined reference to std::__cxx11::basic_string。严重的就是在运行时莫名其妙就跪。

我就遇到了莫名其妙跪的情况,看coredump信息还没有cxx11相关的符号:

复制代码
#0  0x0000ffff48a23d30 in char32_t std::(anonymous namespace)::read_utf8_code_point<char>(std::(anonymous namespace)::range<char const, true>&, unsigned long) () from /root/.seekdb/cache/py3.9/1.0.0/libseekdb_python.so
#1  0x0000ffff48a25ccc in std::codecvt_base::result std::(anonymous namespace)::utf16_in<char, char16_t>(std::(anonymous namespace)::range<char const, true>&, std::(anonymous namespace)::range<char16_t, true>&, unsigned long, std::codecvt_mode, std::(anonymous namespace)::surrogates) () from /root/.seekdb/cache/py3.9/1.0.0/libseekdb_python.so
#2  0x0000ffff48a25e0c in std::codecvt<char16_t, char, __mbstate_t>::do_in(__mbstate_t&, char const*, char const*, char const*&, char16_t*, char16_t*, char16_t*&) const () from /root/.seekdb/cache/py3.9/1.0.0/libseekdb_python.so
#3  0x0000ffff48a8f0c4 in std::ostream& std::ostream::_M_insert<unsigned long>(unsigned long) ()
   from /root/.seekdb/cache/py3.9/1.0.0/libseekdb_python.so

问了下AI,它说还是有可能是ABI不一致导致的。

我这里出现问题的场景是在Python中同时使用torch包和pylibseekdb包,pylibseekdb是我们的包,torch是一个很流行的python包。很多与AI相关的Python包,比如sentence-transformers,都会依赖torch,因此torch还是避不开的。可以使用下面的命令判断torch使用哪种ABI编译的:

python 复制代码
>>> python3 -c 'import torch; print(f"PyTorch compiled with CXX11 ABI: {torch.compiled_with_cxx11_abi()}")'
PyTorch compiled with CXX11 ABI: True

torch编译时使用了 _GLIBCXX_USE_CXX11_ABI=1

由于pylibseekdb有很多依赖包,很难在短时间内把所有的依赖包都是用 _GLIBCXX_USE_CXX11_ABI=1 重新编译,只能另寻出路了。

我们知道Linux的程序在查找动态库引用的符号时,会优先在进程空间内寻找,然后再自己的动态库内寻找。这就导致pylibseekdb.so加载后,查找stdlibc++中 ABI=0 的符号时,先从进程空间查找,也就是 torch 引用的 ABI=1 的符号了,名字相同而实现不同。这里的符号包含了函数和全局变量。这种冲突也就导致了出现莫名其妙的coredump。

想要解决这个问题,一种方法是pylibseekdb也改成使用ABI=1来编译,一种方法是符号查找时优先从动态库本身查找,比如使用隐藏符号的方法。Linux上隐藏符号不好整,就从另一种方法着手尝试吧。

如果程序在使用 dlopen 打开动态库时指定了 RTLD_LOCAL 标识的话,那这个动态库的符号会先从动态库内部查找。否则就是使用默认搜索顺序。不过我们是没有办法修改python程序本身的代码来修改这个标识符的。

另一种方法是在编译动态库的时候处理,告诉程序我们的符号先从自己的动态库中查找,就是在编译时增加一个链接选项 -Bsymbolic告诉编译器动态库查找符号时优先从当前库中查找。

当然要解决这个问题还得静态链接C++的库: -static-libstdc++

最后再吐槽一下Linux上的动态库符号问题。Linux的动态库符号默认都是全局可见的,而且没有简便的方法来处理将符号隐藏在动态库内部。这个设计还是Windows的动态库做的好,默认就是隐藏符号,暴露哪些需要明确说明。

一些参考资料

Option -Bsymbolic 会导致严重副作用
关于Bsymbolic
gcc 编译参数:-Wl,-Bsymbolic 与 - Bsymbolic
ELF interposition and -Bsymbolic
GCC 关于CXX ABI的说明

相关推荐
拳里剑气1 小时前
C++算法:BFS解决FloodFill算法
c++·算法·bfs·宽度优先
爱写代码的小朋友1 小时前
从零开始学 Win32 API:C++ 窗口编程实战(VS Code + MinGW-w64 命令行详解)
开发语言·c++
yangshicong1 小时前
第19章:AI安全防护与AI安全
人工智能·python·安全·prompt·ai编程
果汁华1 小时前
Function Calling 与 Python 实战完整指南
开发语言·网络·python
c_lb72882 小时前
2026年不同基础做量化,先找AI能参与的位置
人工智能·python
小小晓.2 小时前
C++:语句和作用域
开发语言·c++
fqbqrr2 小时前
2607C++,soui,xplayer视频播放器
c++·soui
longerxin20202 小时前
nano编辑器插入、编辑完整操作教程(Linux日志/配置专用)
linux·运维·编辑器
zh路西法3 小时前
【10天速通ROS2-PX4无人机】(四) 关掉GPS和气压计,纯激光定位还能飞吗
c++·无人机·px4·ros2·卡尔曼滤波·fastlio2
chenment3 小时前
ComfyUI 自定义节点开发:从零扩展你的图像生成工作流
python·stable diffusion