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

相关推荐
乱蜂朝王13 小时前
Ubuntu 20.04安装CUDA 11.8
linux·运维·ubuntu
君义_noip13 小时前
信息学奥赛一本通 1661:有趣的数列 | 洛谷 P3200 [HNOI2009] 有趣的数列
c++·算法·组合数学·信息学奥赛·csp-s
程序员:钧念13 小时前
深度学习与强化学习的区别
人工智能·python·深度学习·算法·transformer·rag
数据与后端架构提升之路14 小时前
TeleTron 源码揭秘:如何用适配器模式“无缝魔改” Megatron-Core?
人工智能·python·适配器模式
梁洪飞14 小时前
clk学习
linux·arm开发·嵌入式硬件·arm
~光~~15 小时前
【嵌入式linux驱动——点亮led】基于鲁班猫4 rk3588s
linux·点灯·嵌入式linux驱动
hele_two15 小时前
快速幂算法
c++·python·算法
OopspoO15 小时前
C++杂记——Name Mangling
c++
yuanmenghao15 小时前
车载Linux 系统问题定位方法论与实战系列 - 车载 Linux 平台问题定位规范
linux·运维·服务器·网络·c++
小羊羊Python15 小时前
SoundMaze v1.0.1正式发布!
开发语言·c++