在std :: filesystem :: path追加上的c ++ std :: bad_alloc

Pau*_*erg 8 c++ gdb exception libstdc++ c++17

我遇到一个非常奇怪的行为,我将其提炼为一个非常基本的测试:

#include <string>
#include <filesystem>

int main(void)
{
  const std::string name = "foo";
  const std::filesystem::path lock_dir = "/tmp";
  std::filesystem::path lockfile = lock_dir / name;

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

我用编译g++ -std=c++17 -Wall -Wextra -Werror -g foo.cpp -o foo。当我运行它时,在添加两个路径的那一行上,我得到了一个std :: bad_alloc异常。这是我在gdb中看到的

#0  __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
#1  0x00007ffff742c801 in __GI_abort () at abort.c:79
#2  0x00007ffff7a8e1f2 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#3  0x00007ffff7a99e36 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#4  0x00007ffff7a99e81 in std::terminate() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#5  0x00007ffff7a9a0b5 in __cxa_throw () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#6  0x00007ffff7a907a7 in std::__throw_bad_alloc() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#7  0x0000555555558cfe in __gnu_cxx::new_allocator<std::filesystem::__cxx11::path::_Cmpt>::allocate (this=0x7fffffffe080, __n=12297828079348111650) at /usr/include/c++/8/ext/new_allocator.h:102
#8  0x00005555555587d0 in std::allocator_traits<std::allocator<std::filesystem::__cxx11::path::_Cmpt> >::allocate (__a=..., __n=12297828079348111650) at /usr/include/c++/8/bits/alloc_traits.h:436
#9  0x0000555555557f76 in std::_Vector_base<std::filesystem::__cxx11::path::_Cmpt, std::allocator<std::filesystem::__cxx11::path::_Cmpt> >::_M_allocate (this=0x7fffffffe080, __n=12297828079348111650)
    at /usr/include/c++/8/bits/stl_vector.h:296
#10 0x0000555555558387 in std::_Vector_base<std::filesystem::__cxx11::path::_Cmpt, std::allocator<std::filesystem::__cxx11::path::_Cmpt> >::_M_create_storage (this=0x7fffffffe080, __n=12297828079348111650)
    at /usr/include/c++/8/bits/stl_vector.h:311
#11 0x00005555555579cf in std::_Vector_base<std::filesystem::__cxx11::path::_Cmpt, std::allocator<std::filesystem::__cxx11::path::_Cmpt> >::_Vector_base (this=0x7fffffffe080, __n=12297828079348111650, __a=...)
    at /usr/include/c++/8/bits/stl_vector.h:260
#12 0x0000555555556d39 in std::vector<std::filesystem::__cxx11::path::_Cmpt, std::allocator<std::filesystem::__cxx11::path::_Cmpt> >::vector (this=0x7fffffffe080, 
    __x=std::vector of length -1303124922760, capacity -1303124922760 = {...}) at /usr/include/c++/8/bits/stl_vector.h:460
#13 0x000055555555635f in std::filesystem::__cxx11::path::path (this=0x7fffffffe060, Python Exception <class 'gdb.error'> There is no member or method named _M_t.: 
__p=...) at /usr/include/c++/8/bits/fs_path.h:166
#14 0x00005555555563c8 in std::filesystem:: (Python Exception <class 'gdb.error'> There is no member or method named _M_t.: 
__lhs=..., Python Exception <class 'gdb.error'> There is no member or method named _M_t.: 
__rhs=...) at /usr/include/c++/8/bits/fs_path.h:554
#15 0x0000555555555fbe in main () at foo.cpp:8
Run Code Online (Sandbox Code Playgroud)

这带来了几个问题:

  1. 我的测试代码有什么问题?
  2. 为什么GDB在调用堆栈中使用python显示任何内容?

预料到这个问题,我的g ++ gcc version 8.3.0 (Ubuntu 8.3.0-6ubuntu1~18.04.1)和我的gdb是GNU gdb (Ubuntu 8.2-0ubuntu1~18.04) 8.2

UPDATE这是成功编译的可执行文件的ldd输出

linux-vdso.so.1 (0x00007ffc697b2000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f5c35444000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f5c3522c000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f5c34e3b000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f5c34a9d000)
/lib64/ld-linux-x86-64.so.2 (0x00007f5c35a2d000)
Run Code Online (Sandbox Code Playgroud)

Jon*_*ely 6

这是由Ubuntu的“功能”引起的,该功能libstdc++.so比系统随附的功能要晚g++。有关更多详细信息,请参见https://bugs.launchpad.net/ubuntu/+source/gcc-8/+bug/1824721

通常,在使用GCC 8进行编译时,这些std::filesystem符号不存在libstdc++.so,因此,如果无法链接,-lstdc++fs则会出现链接器错误。但是由于libstdc++.soGCC 9 的较新版本确实包含的符号std::filesystem,因此不会发生链接器错误。不幸的是,文件系统符号的GCC 9版本与GCC 8标头不兼容(因为文件系统库在GCC 8中是试验性的,不稳定,并且filesystem::pathGCC 9 的布局已更改)。这意味着您的程序链接了,但是在运行时,它使用的符号错误filesystem::path,并发生了不好的事情。

我没有预料到这个问题,因为我不知道Ubuntu将旧的libstdc ++头文件与新的libstdc ++共享库混合在一起。这通常是安全的,除非 使用“实验性”的,不完整的功能,例如GCC 8中的C ++ 17功能

我建议对Ubuntu进行的修复是将其g++自动添加-lstdc++fs到编译命令的末尾。如果您使用任何std::filesystem功能,则应在GCC 8 libstdc++fs.a(而不是GCC 9 libstdc++.so)中找到这些符号的正确定义,并且在大多数情况下,所有内容都应能正常工作。如果Ubuntu尚未使用该解决方法更新其GCC软件包,则也可以通过确保手动链接来使其正常工作-lstdc++fs(无论如何,GCC 8都记录了该链接)。