今天我发现了一个有趣的双libstdc ABI案例,影响了库的兼容性 .
长话短说,我有两个库都在内部使用std :: regex . 一个是使用CXX11 ABI构建的,另一个不是 . 当这两个库在一个可执行文件中链接在一起时,它会在启动时崩溃(在输入 main
之前) .
这些库是不相关的,并且不公开提及任何 std::
类型的接口 . 我认为这些库应该不受双重ABI问题的影响 . 显然不是!
这个问题可以通过这种方式轻松复制:
// file.cc
#include <regex>
static std::regex foo("(a|b)");
// main.cc
int main() {}
// build.sh
g++ -o new.o file.cc
g++ -o old.o file.cc -D_GLIBCXX_USE_CXX11_ABI=0
g++ -o main main.cc new.o old.o
./main
输出是:
terminate called after throwing an instance of 'std::bad_alloc'
what(): std::bad_alloc
Aborted (core dumped)
无论我做什么,问题仍然存在 . file.cc
可以分成两个独立的源文件,编译成单独的共享库,两个 std::regex
对象可能有不同的名称,它们可以是全局的,静态的或自动的(一个需要从 main
调用相应的函数) . 这些都没有帮助 .
显然(这是我的简短调查得出的结果)libstdc正则表达式编译器有一些存储 std::string
的内部静态数据,当两个ABI不兼容的代码片段试图使用该数据时,它会得到有关布局的相互矛盾的想法 . std::string
个对象 .
所以我的问题是:
-
这个问题有解决方法吗?
-
这应该被视为libstdc中的错误吗?
这个问题在g / libstdc的几个版本中是可重现的(我尝试了一些从5.4到7.1) . libc不会发生这种情况 .
1 回答
问题源于libstdc为何具有dual ABI的原因 . 从这两个重要的陈述:(1)它是专门为了符合新的第11个标准而引入的,以及
string
(以及与此讨论无关的其他方面)如何起作用; (2)_GLIBCXX_USE_CXX11_ABI
独立于方言工作,并用于一起编译C 03和C 11 .regex
模块是在第11个标准中引入的,并在内部使用字符串 . 因此,您使用_GLIBCXX_USE_CXX11_ABI=0
构建c -11(或更高版本)模板basic_regex
代码 . 这意味着您正在使用c -11regex
对象以及字符串的pre-c -11实现 .那会有用吗?取决于
regex
如何使用字符串,如果它依赖于新的实现(例如禁止写入时复制),则不,否则为是 . 怎么会发生什么?任何东西 .在它的底部,你不应该在任何使用post-c -03方言的新代码(即c -11,14,17,...)上使用
_GLIBCXX_USE_CXX11_ABI=0
,因为它引入了与新保证不兼容的实现在标准对象上,尤其是std::string
.我可以使用
_GLIBCXX_USE_CXX11_ABI=0
与std> = c -11吗? GCC开发人员注意到您可以使用旧的ABI运行新的东西,它有可能使用旧的共享库运行新功能 . 然而,这可能不是一个好主意,也因为代码是新标准,但标准库不符合此标准,可能会在以后发生严重后果 . 你的问题就是一个例子 . 你可以通过混合两个ABI,在这里,我们是不工作 .如果您调用某些.so库中定义的
foo(std::string const&)
,使用旧的ABI编译,则_GLIBCXX_USE_CXX11_ABI=0
确实可用 . 然后在新的源文件中,您希望使用旧的ABI编译此源代码 . 但是你会用新的ABI保留所有其他来源 .libc++
没有这种二元性,即单一string
实现 .我没有给出这个例外的来源或原因 . 我只能猜测有一些与
regex
,string
或locale
相关的共享全局资源在ABI之间没有明确区分 . 并且不同的ABI以不同的方式工作,导致任何事情,例如,异常,段错误,任何意外行为 . 恕我直言,我更喜欢坚持上面提到的规则,这些规则最接近_GLIBCXX_USE_CXX11_ABI
和双ABI的意图 .