std::regex and dual ABI

std::regex and dual ABI

本文关键字:ABI dual regex std and      更新时间:2023-10-16

今天我发现了一个有趣的案例,即双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++regex编译器有某种存储std::string的内部静态数据,当两段与ABI不兼容的代码试图使用这些数据时,它会对std::string对象的布局产生冲突。

所以我的问题是:

  • 有解决这个问题的方法吗
  • 这应该被认为是libstdc++中的一个错误吗

这个问题在g++/libstdc++的几个版本中都是可重复的(我在5.4到7.1中尝试了一些(。libc++不会出现这种问题。

问题源于libstdc++具有双重ABI的原因。从这两个重要的陈述中:(1(它被特别引入,以符合关于string(以及与本讨论无关的其他(如何工作的新的第11标准;(2(_GLIBCXX_USE_CXX11_ABI独立于方言工作,用于编译C++03和C++11。

regex模块在第11个标准中引入,并在内部使用字符串。因此,您可以使用_GLIBCXX_USE_CXX11_ABI=0构建c++-11(或更高版本(模板basic_regex代码。这意味着您使用的是带有字符串的c++-11之前实现的c++-11regex对象。

这样行吗?根据regex使用字符串的方式,如果它确实依赖于新的实现(例如写入时禁止复制(,则为否,否则为是。会发生什么?任何东西

归根结底,您不应该在任何使用后c++-03方言(即c++-11、14、17…(的新代码上使用_GLIBCXX_USE_CXX11_ABI=0,因为它引入了与标准对象上的新保证不兼容的实现,尤其是std::string

我可以使用标准>=c++-11的_GLIBCXX_USE_CXX11_ABI=0吗?GCC开发人员注意到,您可以使用旧的ABI运行新的东西,这得益于使用旧的共享库运行新功能的可能性。然而,这可能不是一个好主意,也因为代码是在一个新的标准中,然而标准库不符合这个标准,以后可能会出现糟糕的结果。你的问题就是一个例子。你可以通过混合两个ABI,而我们在这里,这是行不通的。

如果您调用(例如(某个.so库中定义的foo(std::string const&)(使用旧ABI编译(,_GLIBCXX_USE_CXX11_ABI=0就非常有用。然后,在新的源文件中,您希望使用旧的ABI编译此源文件。但所有其他来源,你会保留一个新的ABI。

这个问题在g++/libstdc++的几个版本中都是可重复的(我在5.4到7.1中尝试了一些(。libc++不会出现这种问题。

libc++不具有这种对偶性,即单个string实现。

我没有给出一个明确的答案,这个例外是从哪里来的,或者为什么。我只能猜测,有一些与regexstringlocale相关的共享全局资源在ABI之间没有明确区分。不同的ABI处理它的方式不同,可能导致任何事情,例如异常、段故障、任何意外行为。IMHO,我更喜欢遵守我上面提到的规则,这些规则最能反映_GLIBCXX_USE_CXX11_ABI和双重ABI的意图。