为什么使用pthread链接会导致分段错误?

4

我有一个简化的程序,其中包含一个静态变量('abc.cpp'):

#include <iostream>

int main(int, char**)
{
  static const std::string a("123");
  std::cout << "Hello world" << std::endl;
  return 0;
}

我编译它,然后它就可以工作:

> g++ -ggdb abc.cpp -o abc
> ./abc
Hello world

然而,如果我链接 pthread 库...
> g++ -ggdb -lpthread abc.cpp -o abc
> ./abc
Segmentation fault (core dumped)

> gdb abc
(gdb) run
Starting program: abc

Program received signal SIGSEGV, Segmentation fault.
0x0000000000000000 in ?? ()
(gdb) where
#0  0x0000000000000000 in ?? ()
#1  0x00007ffff7b01681 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#2  0x00007ffff7b016c3 in std::locale::locale() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#3  0x00007ffff7afe244 in std::ios_base::Init::Init() ()
   from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#4  0x0000000000400d03 in __static_initialization_and_destruction_0 (__initialize_p=1,
    __priority=65535) at /usr/include/c++/4.9/iostream:74
#5  0x0000000000400d2c in _GLOBAL__sub_I_main () at abc.cpp:8
#6  0x0000000000400d7d in __libc_csu_init ()
#7  0x00007ffff74a6e55 in __libc_start_main (main=0x400c06 <main(int, char**)>, argc=1,
    argv=0x7fffffffdb58, init=0x400d30 <__libc_csu_init>, fini=<optimised out>,
    rtld_fini=<optimised out>, stack_end=0x7fffffffdb48) at libc-start.c:246
#8  0x0000000000400b39 in _start ()

我知道这里没有使用线程,但在实际的非削减程序中,它链接到一个使用线程的库。感觉即使没有实际使用线程,链接到pthread也应该是可以的。

有趣的是,添加了卫士程序后,它就不会崩溃了(不确定这是否是一种"未定义"/不稳定的解决方法...)。

> g++ -ggdb -fsanitize=undefined -lpthread abc.cpp -o abc
> ./abc
Hello world

为什么会导致段错误?

顺便提一下:Clang可以正常工作。

> clang++ -ggdb -lpthread abc.cpp -o abc
> ./abc
Hello world

版本信息:

> g++ -v
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.9/lto-wrapper
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 4.9.2-0ubuntu1~14.04' --with-bugurl=file:///usr/share/doc/gcc-4.9/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.9 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.9 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.9-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.9-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.9-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --disable-werror --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu
Thread model: posix
gcc version 4.9.2 (Ubuntu 4.9.2-0ubuntu1~14.04)

> dpkg -l 'libstdc++6*'
Desired=Unknown/Install/Remove/Purge/Hold
| Status=Not/Inst/Conf-files/Unpacked/halF-conf/Half-inst/trig-aWait/Trig-pend
|/ Err?=(none)/Reinst-required (Status,Err: uppercase=bad)
||/ Name                Version        Architecture   Description
+++-===================-==============-==============-===========================================
ii  libstdc++6:amd64    5-20150329-1ub amd64          GNU Standard C++ Library v3
un  libstdc++6-4.0-dbg  <none>         <none>         (no description available)
un  libstdc++6-4.1-dbg  <none>         <none>         (no description available)
un  libstdc++6-4.2-dbg  <none>         <none>         (no description available)
un  libstdc++6-4.3-dbg  <none>         <none>         (no description available)
un  libstdc++6-4.4-dbg  <none>         <none>         (no description available)
un  libstdc++6-4.5-dbg  <none>         <none>         (no description available)
un  libstdc++6-4.6-dbg  <none>         <none>         (no description available)
un  libstdc++6-4.7-dbg  <none>         <none>         (no description available)
un  libstdc++6-4.8-dbg  <none>         <none>         (no description available)
ii  libstdc++6-4.9-dbg: 4.9.2-0ubuntu1 amd64          GNU Standard C++ Library v3 (debugging file
un  libstdc++6-5-dbg    <none>         <none>         (no description available)
un  libstdc++6-dbg      <none>         <none>         (no description available)

这是gcc编译的ldd abc

linux-vdso.so.1 => (0x00007ffef8f2f000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f87b167c000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f87b1465000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f87b109f000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f87b0d99000)
/lib64/ld-linux-x86-64.so.2 (0x00007f87b1a11000)

对于clang构建的ldd abc(注意这里的pthread而不是gcc):

linux-vdso.so.1 => (0x00007fffa4cc7000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fab1f10d000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007fab1ed94000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fab1ea8d000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fab1e876000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fab1e4b1000)
/lib64/ld-linux-x86-64.so.2 (0x00007fab1f347000)

就我个人而言,我的语言环境设置为:

> locale
LANG=en_AU.UTF-8
LANGUAGE=en_AU:en
LC_CTYPE="en_AU.UTF-8"
LC_NUMERIC="en_AU.UTF-8"
LC_TIME="en_AU.UTF-8"
LC_COLLATE="en_AU.UTF-8"
LC_MONETARY="en_AU.UTF-8"
LC_MESSAGES="en_AU.UTF-8"
LC_PAPER="en_AU.UTF-8"
LC_NAME="en_AU.UTF-8"
LC_ADDRESS="en_AU.UTF-8"
LC_TELEPHONE="en_AU.UTF-8"
LC_MEASUREMENT="en_AU.UTF-8"
LC_IDENTIFICATION="en_AU.UTF-8"
LC_ALL=

设置默认语言环境会导致相同的结果(包括相同的堆栈跟踪):
> LC_ALL=C ./abc
Segmentation fault (core dumped)

1
除非有证据证明否则所有迹象都指向GCC中的一个错误。 - Jeff Hammond
你能告诉我们你实际链接的库是什么吗? - user149341
@Jeff:哦!确认会很好。同时确认它在5.0中已经修复也会很好。 - Jetski S-type
@duskwuff:这是我自己做的,而且在这里几乎没有什么相关性。 - Jetski S-type
1
由于看起来是在libstdc++中的空指针崩溃,可以安装调试包。然后堆栈跟踪应该更具信息性。这只会添加外部调试信息文件,因此不会更改您的libstdc++库文件。 - Christian Aichinger
显示剩余10条评论
4个回答

3
问题实际上是你的系统默认使用的gold链接器存在一个bug。请检查通过ld --version命令打印的名称。
这个bug似乎仍然没有解决: https://sourceware.org/bugzilla/show_bug.cgi?id=16417 使用标准的bfd链接器不会出现这个问题。链接器可以通过gcc -fuse-ld=gold指定,或通过/usr/bin/ld符号连接确定。

默认情况下它不使用gold - 我让它在使用/usr/bin/ld符号链接时使用gold。我需要Clang中的LTO使用gold。谢谢,看起来很相似,但我不完全确定...是编译器/链接器的组合引起的,而不是与不同编译器的其他组合?请记住,虽然仍然使用相同的gold链接器,但Clang和GCC 5.1都可以正常工作...我也(不认为?)我的使用了-Wl,-as-needed标志,就像他们在他们的错误中所需的那样。 - Jetski S-type
当ld符号链接指向默认使用的gold时。--as-needed在标准Ubuntu链接器中默认使用,在gcc规范Ubuntu版本中硬编码。虽然可能存在某些交互作用,但我尚未测试过该配置与5.1的兼容性。 - jtaylor

2
你安装的 libstdc++6 包在 Ubuntu 中甚至都不存在!
ii  libstdc++6:amd64  5-20150329-1ubuntu11 amd64    GNU Standard C++ Library v3

来自packages.ubuntu.com
trusty (14.04LTS) (libs): GNU Standard C++ Library v3
    4.8.2-19ubuntu1: amd64 i386
utopic (libs): GNU Standard C++ Library v3
    4.9.1-16ubuntu6: amd64 i386
vivid (libs): GNU Standard C++ Library v3
    4.9.2-10ubuntu13: amd64 i386 

我建议您检查您的/etc/apt/sources.list文件,并删除导致安装该软件包的行。我认为它可能是ppa:ubuntu-toolchain-r/test,这是一个用于“工具链测试构建”的PPA...

然后,您可以尝试将您的libstdc++6软件包降级以回到一个合理的版本(适用于您安装的Ubuntu版本)。在此过程中,您还应仔细检查其他软件包是否也受到影响。您不希望在核心库的测试构建上运行系统。

顺便说一下,这也解释了为什么gdb没有找到调试符号,因为它们是针对另一个版本的库的。


没错!我确实从https://launchpad.net/~ubuntu-toolchain-r/+archive/ubuntu/test获取了我的GCC构建。Ubuntu 14.04 LTS(4.8)中包含的版本对我来说太旧了。像这个(虽然有点老,但原理相同)和这个这样的页面似乎表明这是可以做的。我认为它已经足够接近实际的4.9.2版本了。我更喜欢4.9或5.1,所以我可能会在某个时候尝试5.1... - Jetski S-type
你可以在utopic中使用gcc 4.9ubuntu-toolchain-r比vivid更加前沿,但它是一个不受支持的测试包。个人而言,我不会在上面进行开发,因为你永远不知道崩溃是你的问题还是工具链的问题。gcc 5的功能不值得遇到这样的问题。 - Christian Aichinger
Utopic(14.10)不是LTS版本,我需要使用LTS版本。啊,稳定支持和前沿技术之间的张力。GCC 4.9和5.1相对于4.8是很大的进步,所以我不想退步。GCC 4.9已经发布一年了,所以仍然没有在最新的“测试”版本中修复它是非常遗憾的。让它非常接近官方的4.9.2版本是有意义的。 - Jetski S-type
确认:GCC 5.1(来自ubuntu-toolchain-r/test: gcc version 5.1.0 (Ubuntu 5.1.0-0ubuntu11~14.04.1)) 确实修复了这个段错误。这很好。因此,那个版本的libstdc++应该是有问题的。 - Jetski S-type
我将此标记为解决方案,尽管我不喜欢它的语气。您能否改进一下,说明段错误是由于libstdc++中的一个错误引起的,这是一个非官方不受支持的版本?您可以引用我的GCC 5.1测试作为证明已经修复了这个错误。然后您可以提供如何返回官方GCC软件包的步骤。 - Jetski S-type
抱歉,我的编辑建议被拒绝者似乎意味着最好的做法是自己回答...感谢您建议尝试不同的编译器版本并解释为什么gdb没有显示符号。 - Jetski S-type

2
原来是GCC编译器或libstdc++出现了故障/损坏,链接pthread不应该导致段错误。
使用相同的libstdc++,Clang没有发生段错误,这表明可能是编译器的问题。我测试了gcc version 5.1.0 (Ubuntu 5.1.0-0ubuntu11~14.04.1),发现它可以工作,因此证实了GCC/libstdc++的错误。
最初使用的GCC 4.9来自一个非官方的测试编译器存储库(https://launchpad.net/~ubuntu-toolchain-r/+archive/ubuntu/test),而不是官方的Ubuntu存储库。然而,经过测试的GCC 5.1也来自同一存储库,所以至少在那里修复了这个错误。目前还不知道官方的Ubuntu GCC 4.8是否会导致这个段错误。

-4
$ man gcc

是你的好朋友。使用线程库不仅仅是在链接步骤中包含该库:文件还应该使用“-pthread”开关进行编译。


GCC文档也是你的好朋友:https://gcc.gnu.org/onlinedocs/gcc-4.9.2/gcc/Option-Summary.html#Option-Summary 搜索“-pthread”选项会在“RS/6000和PowerPC选项”和“Solaris 2选项”中找到它。我正在使用x86_64,所以我觉得这个选项对我的平台不相关。这不正确吗? - Jetski S-type
我的编译和链接在一个命令中完成。我尝试过这个方法,用“-pthread”替换“-lpthread”,结果出现了完全相同的分段错误。 - Jetski S-type
至少在C11中,那个标志不再是必需的。据我所知,GCC已经遵循C11内存模型十年左右了。 - Jeff Hammond
1
而且,正如我所猜测的那样,在使用汇编输出进行测量后,-pthread编译选项对Mac x86_64上的GCC 4.9.2代码生成没有任何影响。 - Jeff Hammond

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接