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的说明

相关推荐
冉佳驹8 小时前
Linux ——— 静态库和动态库的设计与使用
linux·动态库·静态库·fpic
doupoa8 小时前
内存指针是什么?为什么指针还要有偏移量?
android·c++
独好紫罗兰9 小时前
对python的再认识-基于数据结构进行-a006-元组-拓展
开发语言·数据结构·python
Dfreedom.9 小时前
图像直方图完全解析:从原理到实战应用
图像处理·python·opencv·直方图·直方图均衡化
冉佳驹9 小时前
C++ ——— 异常处理的核心机制和智能指针管理
c++·异常捕获·异常继承体与多态·重载抛异常·raii思想·智能指针shared_ptr·weak_ptr指针
C++ 老炮儿的技术栈9 小时前
Qt 编写 TcpClient 程序 详细步骤
c语言·开发语言·数据库·c++·qt·算法
陌上花开缓缓归以9 小时前
linux mtd-utils使用源码分析(ubuntu测试版)
linux·arm开发·ubuntu
yuuki2332339 小时前
【C++】继承
开发语言·c++·windows
铉铉这波能秀9 小时前
LeetCode Hot100数据结构背景知识之集合(Set)Python2026新版
数据结构·python·算法·leetcode·哈希算法
梵刹古音9 小时前
【C++】 析构函数
开发语言·c++