9

Spent all day investigating this bug, and my colleagues are saying it looks like a linker or library bug. I've never had anything like that before, so I am here to document it and ask for help!

My executable segfaults before main is called

Program received signal SIGSEGV, Segmentation fault.
0x0000000000000000 in ?? ()
(gdb) bt
#0  0x0000000000000000 in ?? ()
#1  0x00007ffff7b47901 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#2  0x00007ffff7b47943 in std::locale::locale() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#3  0x00007ffff7b44724 in std::ios_base::Init::Init() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#4  0x0000000000400c1c in __static_initialization_and_destruction_0 (__initialize_p=1, __priority=65535)
    at /usr/include/c++/4.8/iostream:74
#5  0x0000000000400c45 in _GLOBAL__sub_I__ZN9CrashTestC2Ev () at crash_test.cc:8
#6  0x0000000000400c9d in __libc_csu_init ()
#7  0x00007ffff7512e55 in __libc_start_main (main=0x400bea <main()>, argc=1, argv=0x7fffffffdca8, 
    init=0x400c50 <__libc_csu_init>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffdc98)
    at libc-start.c:246
#8  0x0000000000400ad9 in _start ()
(gdb) 

The resulting crash seems similar to this question which leads to this bug report, but my code is different and very sensitive to changes. I narrowed down the issue to 5 requirements:

  1. Have an class implemented in a shared library.
  2. Declare an instance of that class in another, immediately following a std::string member declaration.
  3. Include iostream
  4. Link with pthreads
  5. Use g++-4.8 (or 4.9) and the gold linker

That's it. Change or omit any requirement, and the segfault does not occur.

I've created a minimal test case. Here is the executable header

// crash_test.h
#pragma once
#include <string>
#include "crash.h"

class CrashTest {
  CrashTest();  // must have a constructor

  std::string first_;  // must be a string declared before Crash object
  Crash crash_;  // must be a value, not pointer
};

And the main function is empty. I don't even construct the class I defined!

#include "crash_test.h"
#include <iostream>  // required

CrashTest::CrashTest() { }  // must be here, not header

int main() {
  return 0;
}

The Crash class can't get much simpler

// crash.h
#pragma once
struct Crash {
  Crash();
};

But it does require an implementation to create a shared library.

#include "crash.h"
Crash::Crash() {}  // must be here, not header

I've also tested this in a Docker container on a fresh install of Ubuntu 14.04, with g++-4.8 installed via apt-get.

Here is the build script.

#! /bin/sh
COMPILE="/usr/bin/x86_64-linux-gnu-g++-4.8 -Wall -Wextra -Wno-sign-compare -Wno-unused-parameter -Wno-missing-field-initializers -Werror -std=c++11 -O0 -g "

# Segfault only occurs using the gold linker.
LINKER="-fuse-ld=gold"

# Compile a shared library and then an executable.
# If the latter is linked with pthread, it segfaults when run.
# If the shared library is removed, there is no segfault.
$COMPILE -fPIC -o crash.o -c crash.cc \
&& $COMPILE -o crash_test.o -c crash_test.cc \
&& $COMPILE -fPIC $LINKER -shared -Wl,-soname,libcrash.so -o libcrash.so crash.o -Wl,-rpath=. \
&& $COMPILE $LINKER crash_test.o -o crash_test -rdynamic libcrash.so -pthread -Wl,-rpath=. \
&& echo "Compiled and linked..." \
&& ./crash_test \
&& echo "Did not crash!"

I've put all the code in a github repo: crash_test

Advice appreciated!

Community
  • 1
  • 1
matt
  • 1,895
  • 5
  • 19
  • 26
  • How do you build the application? What commands and what arguments? Do you use a makefile? How does it look? – Some programmer dude Jan 15 '16 at 07:38
  • updated question to include the build script, but it is also in the repo I linked – matt Jan 15 '16 at 07:41
  • You don't *compile* with the `-pthread` argument, only link. Have you tried adding the `-pthread` flag argument when compiling too? – Some programmer dude Jan 15 '16 at 07:45
  • I tried to compile it with clang++ on os x and it did not segfault. Compiled using 'clang++ -Wall -O2 -o crash_test crash_test.cc crash.cc'. – kometen Jan 15 '16 at 07:49
  • @JoachimPileborg I tried just now, and it still crashes. However, removing pthreads from the final link step and adding to earlier steps does not result in a crash. So linking pthreads into the shared library and not directly into the final executable does not crash. – matt Jan 15 '16 at 08:06
  • 2
    Is it maybe related to [this bug](https://sourceware.org/bugzilla/show_bug.cgi?id=16417)? – mindriot Jan 15 '16 at 08:13
  • @mindriot I suspect so. What can I do? – matt Jan 15 '16 at 08:17
  • I wish I knew. Looks like the problem isn't resolved, or even clearly identified. I've reproduced the crash on my system (same Ubuntu version) as well, so it looks like a genuine problem. I think it's worth adding a comment with your discoveries to the bug I referred to. – mindriot Jan 15 '16 at 08:30
  • I fear the only thing you _can_ do is work around the problem, by avoiding one of your 5 requirements. – mindriot Jan 15 '16 at 08:30
  • Bug report link added. – matt Jan 15 '16 at 08:45
  • Notice that if you don't link with gold, then the ldd shows the libpthread.so while if you link with gold it doesn't! Bit weird! – Harald Jan 15 '16 at 09:05
  • 1
    Adding `-Wl,--no-as-needed` to your linker command line resolves the problem for me. Not sure if that is an acceptable solution for you though. – mindriot Jan 15 '16 at 09:36
  • which gold version are you using? For me your example works unless I add `-Wl,--as-needed`. This is with gold 1.11. – pseyfert Jan 15 '16 at 19:44
  • @pseyfert I think 1.11, as per the output of this command: g++-4.8 -fuse-ld=gold -Wl,--version – matt Jan 15 '16 at 19:48
  • @pseyfert what OS and g++ version are you using? – matt Jan 15 '16 at 19:49
  • 1
    @matt this is debian stable. g++ 4.8.4 (Debian 4.8.4-1) and g++ 4.9.2 (Debian 4.9.2-10). I just clone the repository you link (commit 69d209b) and get `Did not crash!` – pseyfert Jan 15 '16 at 19:55
  • 1
    @pseyfert The comments to the bug report suggest that it's not gold being at fault here, but rather the libstdc++ version. Ubuntu 14.04 seems to have `libstdc++.so.6.0.19`, while Debian stable has `libstdc++.so.6.0.20`. That could be the clue. – mindriot Jan 16 '16 at 09:23

1 Answers1

3

It seems you're running into the problem described in this gold ticket. Based on the comments to that ticket, the following workaround solved the problem for me: Change your linker command line to include the parameter -Wl,--no-as-needed. For your build script, that would be:

$COMPILE -fPIC -o crash.o -c crash.cc \
&& $COMPILE -o crash_test.o -c crash_test.cc \
&& $COMPILE -fPIC $LINKER -shared -Wl,-soname,libcrash.so \
   -o libcrash.so crash.o -Wl,-rpath=. \
&& $COMPILE $LINKER crash_test.o -o crash_test \
   -rdynamic libcrash.so -pthread -Wl,-rpath=. -Wl,--no-as-needed

But as I said, that's just a workaround, and I'm not sure if that is an acceptable solution for you. If you need a proper solution, you should probably revive that ticket and post your findings there.

mindriot
  • 5,413
  • 1
  • 25
  • 34