std::正则表达式和双重 ABI

问题描述 投票:0回答:2

今天我发现了一个有趣的案例,双 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 -c file.cc
g++ -o old.o -c 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++ 不会出现这种情况。

c++ c++11 g++ libstdc++ abi
2个回答
7
投票

问题源于为什么 libstdc++ 具有双 ABI。从这两个重要的陈述来看:(1)专门引入了关于

string
(以及与本讨论无关的其他内容)如何工作的新11号标准; (2)
_GLIBCXX_USE_CXX11_ABI
独立于方言工作,用于一起编译 C++03 和 C++11。

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

这应该有效吗?根据

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 库中定义的、使用旧 ABI 编译的

_GLIBCXX_USE_CXX11_ABI=0

,那么 
foo(std::string const&)
确实很有用。然后,在新的源文件中,您希望使用旧的 ABI 编译此源。但您将使用新的 ABI 保留所有其他来源。

该问题在 g++/libstdc++ 的多个版本中都可以重现(我尝试了从 5.4 到 7.1 的一些版本)。 libc++ 不会出现这种情况。

libc++
不具有这种二元性,即单一
string
实现。

我没有给出明确的答案这个异常是从哪里来的或者为什么。我可能只是猜测有一些与

regex
string
locale
相关的共享全局资源在 ABI 之间没有明确区分。不同的 ABI 会以不同的方式使用它,这可能会导致任何结果,例如异常、段错误、任何意外行为。恕我直言,我更喜欢遵守我上面提到的规则,这些规则最能反映
_GLIBCXX_USE_CXX11_ABI
和双重 ABI 的意图。


0
投票

是的,这是一个 libstdc++ 错误;报告为 #118408。我希望在 2018 年首次提出这个问题时就对此进行报道 - 那时修复它可能会比今天更有价值。

问题是,尽管

std::basic_regex
std::regex_traits
带有 ABI 标记,但正则表达式帮助器类模板之一
std::__detail::_Scanner
却没有,尽管有
std::basic_string
数据成员。因此,在两个 ABI 下编译的
_Scanner
的成员函数彼此不兼容,但具有相同的损坏名称,并且当链接器混合和匹配时,会发生爆炸。 (
_Scanner
仅用作
_Compiler
的数据成员,它确实从其
regex_traits
模板参数获取ABI标记,但这不足以避免此问题。)

我不知道此问题有普遍适用的解决方法。

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.