Bug 16417 - executable linked with gold segfaults before main
Summary: executable linked with gold segfaults before main
Status: REOPENED
Alias: None
Product: binutils
Classification: Unclassified
Component: gold (show other bugs)
Version: 2.24
: P2 critical
Target Milestone: ---
Assignee: Cary Coutant
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2014-01-09 05:40 UTC by Alex Turbov
Modified: 2015-11-17 23:31 UTC (History)
5 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:


Attachments
output of `cave info` (3.26 KB, text/plain)
2014-01-09 05:40 UTC, Alex Turbov
Details
compiled object file (23.55 KB, application/x-object)
2014-01-16 18:18 UTC, Alex Turbov
Details
cave info (4.22 KB, text/plain)
2014-03-16 02:43 UTC, Aleksandar Petrinic
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Alex Turbov 2014-01-09 05:40:50 UTC
Created attachment 7342 [details]
output of `cave info`

I'm using gentoo w/ my custom compilation flags. I've found that simple program being compiled and linked w/ gold segfaults w/ the following backtrace:

Program received signal SIGSEGV, Segmentation fault.
0x0000000000000000 in ?? ()
(gdb) bt
#0  0x0000000000000000 in ?? ()
#1  0x00007ffff7f2a1e1 in __gthread_once (__func=0x7ffff7f2a180 <std::locale::_S_initialize_once()>, __once=<optimized out>)
    at /storage/tmp/paludis/sys-devel-gcc-4.8.2/work/build/x86_64-pc-linux-gnu/libstdc++-v3/include/x86_64-pc-linux-gnu/bits/gthr-default.h:699
#2  std::locale::_S_initialize () at /storage/tmp/paludis/sys-devel-gcc-4.8.2/work/gcc-4.8.2/libstdc++-v3/src/c++98/locale_init.cc:276
#3  0x00007ffff7f2a223 in std::locale::locale (this=0x7ffff7fbffb8 <__gnu_internal::buf_cout_sync+56>)
    at /storage/tmp/paludis/sys-devel-gcc-4.8.2/work/gcc-4.8.2/libstdc++-v3/src/c++98/locale_init.cc:210
#4  0x00007ffff7f2710c in basic_streambuf (this=<optimized out>)
    at /storage/tmp/paludis/sys-devel-gcc-4.8.2/work/build/x86_64-pc-linux-gnu/libstdc++-v3/include/streambuf:466
#5  stdio_sync_filebuf (__f=0x7ffff7dd62a0 <_IO_2_1_stdout_>, this=<optimized out>)
    at /storage/tmp/paludis/sys-devel-gcc-4.8.2/work/build/x86_64-pc-linux-gnu/libstdc++-v3/include/ext/stdio_sync_filebuf.h:77
#6  std::ios_base::Init::Init (this=<optimized out>) at /storage/tmp/paludis/sys-devel-gcc-4.8.2/work/gcc-4.8.2/libstdc++-v3/src/c++98/ios_init.cc:85
#7  0x0000000000400dce in __static_initialization_and_destruction_0 (__initialize_p=1, __priority=65535)
    at /usr/lib/gcc/x86_64-pc-linux-gnu/4.8.2/include/g++-v4/iostream:74
#8  _GLOBAL__sub_I_main () at print_exports.cc:33
#9  0x0000000000400f2d in __libc_csu_init (argc=argc@entry=1, argv=argv@entry=0x7fffffffcc98, envp=0x7fffffffcca8) at elf-init.c:83
#10 0x00007ffff7a55905 in __libc_start_main (main=0x400be0 <main()>, argc=1, ubp_av=0x7fffffffcc98, init=0x400ee0 <__libc_csu_init>,
    fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffcc88) at libc-start.c:217
#11 0x0000000000400e11 in _start () at ../sysdeps/x86_64/start.S:123

the source code is:

#include <string>
#include <iostream>

extern char ** environ;

int
main()
{
    for (char ** it = environ; nullptr != *it; ++it)
    {
        std::string str(*it);
        std::cout << "export " << str.substr(0, str.find('=')) << std::endl;
    }
}

compile commands:

x86_64-pc-linux-gnu-g++ -pthread -std=gnu++0x -pedantic -ggdb -O2 -pipe -march=native -minline-stringops-dynamically -mtls-dialect=gnu2 -floop-block -floop-interchange -ftree-loop-distribution -floop-strip-mine -ftree-vectorize -fmerge-all-constants -fira-loop-pressure -pedantic -c -o print_exports.o print_exports.cc

x86_64-pc-linux-gnu-g++ -pthread -std=gnu++0x -pedantic -ggdb -O2 -pipe -march=native -minline-stringops-dynamically -mtls-dialect=gnu2 -floop-block -floop-interchange -ftree-loop-distribution -floop-strip-mine -ftree-vectorize -fmerge-all-constants -fira-loop-pressure -pedantic -Wl,-O1 -Wl,--sort-common -Wl,--enable-new-dtags -Wl,--gc-sections -Wl,--hash-style=gnu -o print_exports print_exports.o  -Wl,--as-needed -pthread -Wl,-fuse-ld=gold

GCC info:

/home/zaufi〉 g++ -v
Using built-in specs.
COLLECT_GCC=/usr/x86_64-pc-linux-gnu/gcc-bin/4.8.2/g++
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-pc-linux-gnu/4.8.2/lto-wrapper
Target: x86_64-pc-linux-gnu
Configured with: /storage/tmp/paludis/sys-devel-gcc-4.8.2/work/gcc-4.8.2/configure --host=x86_64-pc-linux-gnu --build=x86_64-pc-linux-gnu --prefix=/usr --bindir=/usr/x86_64-pc-linux-gnu/gcc-bin/4.8.2 --includedir=/usr/lib/gcc/x86_64-pc-linux-gnu/4.8.2/include --datadir=/usr/share/gcc-data/x86_64-pc-linux-gnu/4.8.2 --mandir=/usr/share/gcc-data/x86_64-pc-linux-gnu/4.8.2/man --infodir=/usr/share/gcc-data/x86_64-pc-linux-gnu/4.8.2/info --with-gxx-include-dir=/usr/lib/gcc/x86_64-pc-linux-gnu/4.8.2/include/g++-v4 --with-python-dir=/share/gcc-data/x86_64-pc-linux-gnu/4.8.2/python --enable-languages=c,c++ --enable-obsolete --enable-secureplt --disable-werror --with-system-zlib --disable-nls --enable-checking=release --with-bugurl=https://bugs.gentoo.org/ --with-pkgversion='Gentoo 4.8.2 p1.2, pie-0.5.8' --enable-libstdcxx-time --enable-shared --enable-threads=posix --enable-__cxa_atexit --enable-clocale=gnu --enable-multilib --with-multilib-list=m32,m64 --disable-altivec --disable-fixed-point --enable-targets=all --disable-libgcj --enable-libgomp --enable-libmudflap --disable-libssp --disable-libquadmath --enable-lto --with-cloog --disable-isl-version-check
Thread model: posix
gcc version 4.8.2 (Gentoo 4.8.2 p1.2, pie-0.5.8)
Comment 1 Cary Coutant 2014-01-16 01:06:31 UTC
I tried compiling with a 4.9.0 compiler, but my copy doesn't have the -floop-* options implemented. Compiling without those options does not reproduce the problem. Can you attach your .o file?
Comment 2 Alex Turbov 2014-01-16 18:18:18 UTC
Created attachment 7358 [details]
compiled object file
Comment 3 Aleksandar Petrinic 2014-03-16 02:40:55 UTC
I investigated this bug a more further and this is only triggered when gold is used in combination with -Wl,--as-needed. I'm on Exherbo and will attach cave info.

Anyway, take a look:

x86_64-pc-linux-gnu-g++ -pthread -std=gnu++0x -pedantic -ggdb -O2 -pipe -march=native -pedantic -c -o print_exports.o print_exports.cc

x86_64-pc-linux-gnu-g++ -pthread -std=gnu++0x -pedantic -ggdb -O2 -pipe -march=native -pedantic -o print_exports print_exports.o  -Wl,--as-needed -pthread -Wl,-fuse-ld=gold

./print_exports -->(segfault)

x86_64-pc-linux-gnu-g++ -pthread -std=gnu++0x -pedantic -ggdb -O2 -pipe -march=native -pedantic -o print_exports print_exports.o  -Wl,--as-needed -pthread -Wl,-fuse-ld=bfd

./print_exports --> works

x86_64-pc-linux-gnu-g++ -pthread -std=gnu++0x -pedantic -ggdb -O2 -pipe -march=native -pedantic -o print_exports print_exports.o -pthread -Wl,-fuse-ld=gold
(Pay attention... as-needed is missing here)

./print_exports --> works
Comment 4 Aleksandar Petrinic 2014-03-16 02:43:01 UTC
Created attachment 7472 [details]
cave info
Comment 5 Aleksandar Petrinic 2014-03-16 02:47:18 UTC
Maybe this is useful too

g++ -v
Using built-in specs.
COLLECT_GCC=g++
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-pc-linux-gnu/4.8.2/lto-wrapper
Target: x86_64-pc-linux-gnu
Configured with: /var/tmp/paludis/build/sys-devel-gcc-4.8.2-r2/work/gcc-4.8.2/configure --prefix=/usr --host=x86_64-pc-linux-gnu --build=x86_64-pc-linux-gnu --mandir=/usr/share/man --infodir=/usr/share/info --datadir=/usr/share --sysconfdir=/etc --localstatedir=/var/lib --disable-silent-rules --enable-fast-install --libdir=/usr/lib64 --cache-file=config.cache --libdir=/usr/lib64 --with-pkgversion='exherbo gcc-4.8.2-r2' --program-suffix=-4.8 --disable-bootstrap --enable-clocale=gnu --enable-languages=c,c++,fortran --enable-lto --enable-multilib --enable-nls --enable-serial-configure --enable-libquadmath --enable-libquadmath-support --with-cloog --enable-libgomp --disable-libobjc --disable-libssp --with-as=x86_64-pc-linux-gnu-as --with-ld=x86_64-pc-linux-gnu-ld --with-system-zlib
Thread model: posix
gcc version 4.8.2 (exherbo gcc-4.8.2-r2)
Comment 6 Markus Trippelsdorf 2014-04-03 16:15:22 UTC
markus@x4 tmp % cat print_exports.ii
static __typeof 0 a __attribute__ ((__weakref__ ("__pthread_key_create")));
void *b = &a;
namespace std
{
class A
{
public:
  A (void *);
};
class ios_base
{
public:
  class Init
  {
  public:
    Init ();
  };
};
ios_base::Init c;
}

int main ()
{
  for (int d; 0;)
    std::A e (&d);
}
markus@x4 tmp % g++ -pthread -O2 print_exports.ii -Wl,--as-needed
markus@x4 tmp % ./a.out
[1]    8353 segmentation fault  ./a.out
markus@x4 tmp % nm a.out | grep pthread
                 w __pthread_key_create
markus@x4 tmp % ldd ./a.out
        linux-vdso.so.1 (0x00007fff709ff000)
        libstdc++.so.6 => /usr/lib/gcc/x86_64-pc-linux-gnu/4.9.0/libstdc++.so.6 (0x00007fb001be9000)
        libc.so.6 => /lib/libc.so.6 (0x00007fb0017a1000)
        libm.so.6 => /lib/libm.so.6 (0x00007fb001512000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fb001b18000)
        libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x00007fb001bd0000)


markus@x4 tmp % g++ -pthread -O2 print_exports.ii -Wl,--as-needed -Wl,-fuse-ld=bfd
markus@x4 tmp % ./a.out
markus@x4 tmp % nm a.out | grep pthread
                 w __pthread_key_create@@GLIBC_2.2.5
markus@x4 tmp % ldd ./a.out
        linux-vdso.so.1 (0x00007fff599ff000)
        libstdc++.so.6 => /usr/lib/gcc/x86_64-pc-linux-gnu/4.9.0/libstdc++.so.6 (0x00007f25053ac000)
        libpthread.so.0 => /lib/libpthread.so.0 (0x00007f25050bd000)
        libc.so.6 => /lib/libc.so.6 (0x00007f2504d46000)
        libm.so.6 => /lib/libm.so.6 (0x00007f2504ab7000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f25052db000)
        libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x00007f2505393000)
markus@x4 tmp %
Comment 7 Cary Coutant 2014-04-03 17:01:35 UTC
The ld documentation says:

--as-needed
--no-as-needed
   This option affects ELF DT_NEEDED tags for dynamic libraries
   mentioned on the command line after the --as-needed option.
   Normally the linker will add a DT_NEEDED tag for each dynamic
   library mentioned on the command line, regardless of whether
   the library is actually needed or not. --as-needed causes a
   DT_NEEDED tag to only be emitted for a library that at that
   point in the link satisfies a non-weak undefined symbol
   reference from a regular object file or, if the library is not
   found in the DT_NEEDED lists of other libraries, a non-weak
   undefined symbol reference from another dynamic library.
   Object files or libraries appearing on the command line after
   the library in question do not affect whether the library is
   seen as needed. This is similar to the rules for extraction of
   object files from archives. --no-as-needed restores the
   default behaviour.

Note where it says *non-weak*. It seems to me that gold is doing the right thing here and BFD ld is not.
Comment 8 Alan Modra 2014-04-04 03:45:31 UTC
bfd ld behaviour with the testcase in comment #6 is a little weird, but I think correct given the description of --as-needed.

On my x86_64 Ubuntu 12.04 box, it isn't __pthread_key_create that causes libpthread to be seen as needed.  libpthread.so.0 is being included in the link due to a strong reference in libstdc++.so.6 to write@GLIBC_2.2.5.  libpthread.so.0 defines a weak write@@GLIBC_2.2.5 so satisfies the --as-needed requirement.

The order in which the libraries are linked, -lstdc++, -lpthread, -lc, means we haven't seen the definition in libc.so.6 for "write" at the time we're checking whether libpthread.so.6 is needed..  If I modify the order to -lstdc++, -lc, -lpthread, -lc then libpthread is seen as not needed, and the resulting a.out does not crash.

So I think Cary was a little hasty in closing this bug.
Comment 9 Cary Coutant 2014-04-04 17:13:36 UTC
"Normally the linker will add a DT_NEEDED tag for each dynamic library mentioned on the command line, regardless of whether the library is actually needed or not.  --as-needed causes a DT_NEEDED tag to only be emitted for a library that at that point in the link

[a] satisfies a non-weak undefined symbol reference from a regular object file or,

[b] if the library is not found in the DT_NEEDED lists of other libraries, a non-weak undefined symbol reference from another dynamic library."

Case [a] is not satisfied here, since there is no reference to libpthread from a regular object file.

Case [b] is related to one of the major differences between BFD ld and gold: gold does not track DT_NEEDED lists from shared libraries, so it does not check for this case.

I'm still a bit puzzled as to why there's a weak reference to libpthread in the first place. And if there's a weak references, why isn't the code prepared to deal with it remaining unresolved? This seems like an artificial test case to me.
Comment 10 Alan Modra 2014-04-05 01:10:51 UTC
I was also puzzled as to why libpthread.so is exporting symbols defined in libc.so, and vice versa, but on looking at the code there is some justification for some of the duplication.  The libc versions have extra code (testing for multple threads) before taking locks.  So the libc versions would be a tiny bit slower than libpthread versions, when you know for sure that multiple threads are running.  Now if you're linking against libpthread you probably do have multiple threads, but clearly the testcase in comment #6 does not have multiple threads but manages to pull in libpthread.  Note that this is *not* via the weak reference to __pthread_key_create but due to the duplicate symbols!  And if libpthread is pulled in accidentally, then single-threaded code will run slower..

Another thing I just found is that the testcase in comment #6 does not segfault when linked by mainline gold on my x86_64 system.  (My g++ reports (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3)  I guess I should have checked gold before reopening this bug.

I think it likely that newer versions of libstdc++ are at fault here.