TL;DR:
I have an issue where if I have a .so linked with -static-lib* making
all STL symbols private, and if I throw an exception out of that .so to
be caught by the caller, then I get a SIGABRT from a gcc_assert() down
in the guts of the signal handling:

#0  0x00007ffff773a428 in raise () from /lib/x86_64-linux-gnu/libc.so.6
#1  0x00007ffff773c02a in abort () from /lib/x86_64-linux-gnu/libc.so.6
#2  0x000000000040e938 in _Unwind_SetGR (context=<optimized out>, 
index=<optimized out>, val=<optimized out>) at 
/usr/src/cc/gcc-6.2.0/libgcc/unwind-dw2.c:271
271       gcc_assert (index < (int) sizeof(dwarf_reg_size_table));

Should it be possible to do this successfully or am I doomed to failure?
More details and a test case below.


More detail:

I'm trying to distribute a shared library built with the latest version
of C++ (well, GCC 6.2 with C++14) on GNU/Linux.  I compile it with an
older sysroot, taken from RHEL 6.3 (glibc 2.12) so it will run on older
systems.

My .so is written in C++ and programs that link it will also be written
in C++ although they may be compiled and linked with potentially much
older versions of GCC (like, 4.9 etc.)  I'm not worried about programs
compiled with clang or whatever at this point.

Because I want to use new C++ but want users to be able to use my .so on
older systems, I link with -static-libgcc -static-libstdc++.  Because I
don't want to worry about security issues etc. in system libraries, I
don't link anything else statically.

I also use a linker script to force all symbols (even libstdc++ symbols)
to be private to my shared library except the ones I want to publish.

Using "nm | grep ' [A-TV-Z] '" I can see that no other symbols besides
mine are public.

However, if my library throws an exception which I expect to be handled
by the program linking my library, then I get a SIGABRT, as above; the
full backtrace is:

#0  0x00007ffff773a428 in raise () from /lib/x86_64-linux-gnu/libc.so.6
#1  0x00007ffff773c02a in abort () from /lib/x86_64-linux-gnu/libc.so.6
#2  0x000000000040e938 in _Unwind_SetGR (context=<optimized out>, 
index=<optimized out>, val=<optimized out>) at 
/usr/src/cc/gcc-6.2.0/libgcc/unwind-dw2.c:271
#3  0x00000000004012a2 in __gxx_personality_v0 ()
#4  0x00007ffff7feb903 in _Unwind_RaiseException_Phase2 
(exc=exc@entry=0x43b890, context=context@entry=0x7fffffffe330) at 
/usr/src/cc/gcc-6.2.0/libgcc/unwind.inc:62
#5  0x00007ffff7febf8a in _Unwind_RaiseException (exc=0x43b890) at 
/usr/src/cc/gcc-6.2.0/libgcc/unwind.inc:131
#6  0x00007ffff7fde84b in __cxa_throw () from 
/home/psmith/src/static-eh/libmylib.so
#7  0x00007ffff7fddecb in MyLib::create () at mylib.cpp:2
#8  0x0000000000400da4 in main () at myprog.cpp:2

I should note that if I use the GCC 5.4 that comes standard on my OS
rather than my locally-built version I get identical behavior and
backtrace (except not as much debuggability of course).  So I don't
think it's an incorrect build.

If I don't use -static-libstdc++ with my .so then it doesn't fail.  Also
if I don't use a linker script to hide all the C++ symbols it doesn't
fail (but of course anyone who links with my shared library will use my
copy of the STL).


Here's a repro case (this shows the problem on my Ubuntu GNOME 16.04
GNU/Linux system with GCC 5.4 and binutils 2.26.1):

~$ cat mylib.h
class MyLib { public: static void create(); };

~$ cat mylib.cpp
#include "mylib.h"
void MyLib::create() { throw 42; }

~$ cat myprog.cpp
#include "mylib.h"
int main() { try { MyLib::create(); } catch (...) { return 0; } return 1; }

~$ cat ver.map
{ global: _ZN5MyLib6createEv; local: *; };

~$ g++ -I. -g -fPIC -static-libgcc -static-libstdc++ \
    -Wl,--version-script=ver.map -Wl,-soname=libmylib.so \
    -shared -o libmylib.so mylib.cpp

~$ g++ -I. -g -fPIC  -L. -Wl,-rpath="\$ORIGIN" -o myprog myprog.cpp \
    -lmylib

~$ ./myprog
Aborted (core dumped)

Now if I rebuild without the --version-script argument or without
-static-libstdc++, I get success as expected:

~$ ./myprog 

~$ echo $?
0

Reply via email to