[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

library/1750: static linking + C++ exceptions + pthreads = SEGV




>Number:         1750
>Category:       library
>Synopsis:       static linking + C++ exceptions + pthreads = SEGV
>Confidential:   no
>Severity:       serious
>Priority:       low
>Responsible:    bugs
>State:          open
>Class:          sw-bug
>Submitter-Id:   net
>Arrival-Date:   Fri Mar 30 12:50:01 MST 2001
>Last-Modified:
>Originator:     Stephen J. Bevan
>Organization:
eTunnels
>Release:        2.8
>Environment:
	System      : OpenBSD 2.8
	Architecture: OpenBSD.i386
	Machine     : i386
>Description:
	Statically linking a C++ program that has a try/catch in main
        causes the program to SEGV when it is run.  For example :-

$ cat crash.cpp
#include <unistd.h>

int main(int argc, char ** argv)
{
  try
  {
    write(1, "hello\n", 6);
  }
  catch (...)
  {
    write(1, "oops\n", 5);
  }
  return 0;
}
$ c++ -static -o crash crash.cpp -pthread
$ ./crash
Memory fault (core dumped) 
$ 

>How-To-Repeat:

  I observed the problem with the version of gcc that came with
  OpenBSD 2.8.  However since it is possible that the problem is the
  fault of gcc I installed gcc-2.95.3 as announced on
  http://gcc.gnu.org on 2001-03-16 to try and head off the "try it
  with the latest version of the compiler" reply.  Here's how I
  installed gcc :-
  
$ ./configure --prefix=/home/stephen/gcc-2.95.3 --enable-threads=posix
$ make LANGUAGES="c c++"
$ make LANGUAGES="c c++" install

  Here's the simple test program :-

$ cat crash.cpp
#include <unistd.h>

int main(int argc, char ** argv)
{
  try
  {
    write(1, "hello\n", 6);
  }
  catch (...)
  {
    write(1, "oops\n", 5);
  }
  return 0;
}

  And how it compiles :-

$ gcc-2.95.3 c++ -static -v -save-temps -g -O2 -o crash crash.cpp -pthread
Reading specs from /home/stephen/gcc-2.95.3/lib/gcc-lib/i386-unknown-openbsd2.8/2.95.3/specs
gcc version 2.95.3 20010315 (release)
 /home/stephen/gcc-2.95.3/lib/gcc-lib/i386-unknown-openbsd2.8/2.95.3/cpp0 -lang-c++ -v -D__GNUC__=2 -D__GNUG__=2 -D__GNUC_MINOR__=95 -D__cplusplus -D__unix__ -D__i386__ -D__OpenBSD__ -D__unix__ -D__i386__ -D__OpenBSD__ -Asystem(unix) -Asystem(OpenBSD) -Acpu(i386) -Amachine(i386) -D__EXCEPTIONS -D__OPTIMIZE__ -g -D_POSIX_THREADS crash.cpp crash.ii
GNU CPP version 2.95.3 20010315 (release) (80386, BSD syntax)
#include "..." search starts here:
#include <...> search starts here:
 /home/stephen/gcc-2.95.3/lib/gcc-lib/i386-unknown-openbsd2.8/2.95.3/../../../../include/g++-3
 /home/stephen/gcc-2.95.3/include
 /home/stephen/gcc-2.95.3/lib/gcc-lib/i386-unknown-openbsd2.8/2.95.3/../../../../i386-unknown-openbsd2.8/include
 /home/stephen/gcc-2.95.3/lib/gcc-lib/i386-unknown-openbsd2.8/2.95.3/include
 /usr/include
End of search list.
The following default directories have been omitted from the search path:
End of omitted list.
 /home/stephen/gcc-2.95.3/lib/gcc-lib/i386-unknown-openbsd2.8/2.95.3/cc1plus crash.ii -quiet -dumpbase crash.cc -g -O2 -version -o crash.s
GNU C++ version 2.95.3 20010315 (release) (i386-unknown-openbsd2.8) compiled by GNU C version 2.95.3 20010315 (release).
 as -o crash.o crash.s
 /home/stephen/gcc-2.95.3/lib/gcc-lib/i386-unknown-openbsd2.8/2.95.3/collect2 -e start -dc -dp -Bstatic -o crash /usr/lib/crt0.o -L/home/stephen/gcc-2.95.3/lib/gcc-lib/i386-unknown-openbsd2.8/2.95.3 -L/home/stephen/gcc-2.95.3/lib crash.o -lstdc++ -lm -lgcc -lc_r -lgcc
$ 

  Notice I have to pass -pthread even though the program does not
  explicitly use threads otherwise I get a link failure :-

$ c++ -static -o crash crash.cpp
_eh.o: Undefined symbol `_pthread_setspecific' referenced from text segment
_eh.o: Undefined symbol `_pthread_key_create' referenced from text segment
_eh.o: Undefined symbol `_pthread_once' referenced from text segment
_eh.o: Undefined symbol `_pthread_getspecific' referenced from text segment
_eh.o: Undefined symbol `_pthread_setspecific' referenced from text segment
./frame.c:627: Undefined symbol `_pthread_mutex_lock' referenced from text segment
./frame.c:627: Undefined symbol `_pthread_mutex_unlock' referenced from text segment
./frame.c:627: Undefined symbol `_pthread_mutex_lock' referenced from text segment
./frame.c:627: Undefined symbol `_pthread_mutex_unlock' referenced from text segment
./frame.c:627: Undefined symbol `_pthread_mutex_lock' referenced from text segment
./frame.c:627: Undefined symbol `_pthread_mutex_unlock' referenced from text segment
./frame.c:627: Undefined symbol `_pthread_mutex_lock' referenced from text segment
./frame.c:627: Undefined symbol `_pthread_mutex_unlock' referenced from text segment
./frame.c:627: Undefined symbol `_pthread_mutex_unlock' referenced from text segment
collect2: ld returned 1 exit status
$ 

  Note that a version of gcc built without --enable-threads=posix does
  not need the -pthreads flag and has no problem with running
  crash.cpp when statically linked.  However, programs that do use
  threads don't run when compiled with that flavour of gcc (I'm
  assuming that is known and why the --enable-threads option is there,
  but I can submit a separate sample program showing the failure if it
  is not known).

  Anyway, running the program gives :-

$ ./crash
Memory fault (core dumped) 
$ 

  The following is my attempted diagnosis of the problem, feel free to
  ignore it if you think I'm going down a blind alley :-

$ gdb crash
GNU gdb 4.16.1
Copyright 1996 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386-unknown-openbsd2.8"...
(gdb) Starting program: /home/stephen/misc/crash 

Program received signal SIGSEGV, Segmentation fault.
0x6ba2 in pthread_mutex_lock ()
(gdb) bt
#0  0x6bca in pthread_mutex_lock ()
#1  0x5d4c in pthread_once ()
#2  0x1ae0 in eh_context_initialize ()
#3  0x1a7d in __get_eh_context ()
#4  0x17b6 in main (argc=1, argv=0xdfbfdc60) at crash.cpp:4
(gdb) x/10i 0x6b92
0x6b92 <pthread_mutex_lock+770>:	mov    DWORD PTR [%edx+44],%eax
0x6b95 <pthread_mutex_lock+773>:	mov    %eax,%ds:0x18114
0x6b9a <pthread_mutex_lock+778>:	mov    %edx,DWORD PTR [%eax+420]
0x6ba0 <pthread_mutex_lock+784>:	mov    %eax,DWORD PTR [%ebx]
0x6ba2 <pthread_mutex_lock+786>:	mov    DWORD PTR [%edx],%eax
0x6ba4 <pthread_mutex_lock+788>:	mov    %edx,%ds:0x18114
0x6baa <pthread_mutex_lock+794>:	mov    %eax,DWORD PTR [%ebx]
0x6bac <pthread_mutex_lock+796>:	add    %eax,40
0x6baf <pthread_mutex_lock+799>:	mov    DWORD PTR [%edx+420],%eax
0x6bb5 <pthread_mutex_lock+805>:	jmp    0x6c5d <pthread_mutex_lock+973>
(gdb) info reg edx
(gdb) edx            0x0	0

  That fact that edx is 0 clearly explains the SEGV.  The following is
  my guess as to the problem, you can take it with a pinch of salt
  though ...

  Looking at the code the above seems to match up with the following
  line from src/lib/libc_r/uthread/uthread_mutex.c:pthread_mutex_lock :- 

    TAILQ_INSERT_TAIL(&_thread_run->mutexq, (*mutex), m_qe);

  and in particular the line 

	*(head)->tqh_last = (elm);

  which becomes 

	*(&_thread_run->mutexq)->tqh_last = (*mutex);

  After 0x6b95 %eax contains _thread_run which is 0x1b348, so looking
  at 420 from that we see :-

(gdb) p/x 0x1b348+420
$19 = 0x1b4ec
(gdb) x/2wx 0x1b4ec
0x1b4ec <_thread_kern_thread+420>:	0x00000000	0x00000000
(gdb) 

  so tqh_last is 0.  Looking at _thread_run in general :-

(gdb) x/50wx _thread_run
0x1b348 <_thread_kern_thread>:	0x00000000	0x00000000	0x00000000	0x00000000
0x1b358 <_thread_kern_thread+16>:	0x00000000	0x00000000	0x00000000	0x00000000
0x1b368 <_thread_kern_thread+32>:	0x00000000	0x00000000	0x00000000	0x00000000
0x1b378 <_thread_kern_thread+48>:	0x00000000	0x00000000	0x00000000	0x00000000
0x1b388 <_thread_kern_thread+64>:	0x00000000	0x00000000	0x00000000	0x00000000
0x1b398 <_thread_kern_thread+80>:	0x00000000	0x00000000	0x00000000	0x00000000
0x1b3a8 <_thread_kern_thread+96>:	0x00000000	0x00000000	0x00000000	0x00000000
0x1b3b8 <_thread_kern_thread+112>:	0x00000000	0x00000000	0x00000000	0x00000000
0x1b3c8 <_thread_kern_thread+128>:	0x00000000	0x00000000	0x00000000	0x00000000
0x1b3d8 <_thread_kern_thread+144>:	0x00000000	0x00000000	0x00000000	0x00000000
0x1b3e8 <_thread_kern_thread+160>:	0x00000000	0x00000000	0x00000000	0x00000000
0x1b3f8 <_thread_kern_thread+176>:	0x00000000	0x00000000	0x00000000	0x00000000
0x1b408 <_thread_kern_thread+192>:	0x00000000	0x00000000
(gdb) 

  so it seems clear that either _thread_kern_thread needs to be
  initialised before this point or _thread_run should be pointing at
  something else.  My guess is that you expect
  src/lib/libc_r/uthread/uthread_init.c:_thread_init() to have 
  been called before any code gets into pthread_mutex_lock().
  As I understand it _thread_init() is called via a global
  constructor and constructors are run when __main is called.
  As you can see from the following, __get_eh_context is called
  *before* __main is called :-

(gdb) x/20i main
0x17a8 <main>:	push   %ebp
0x17a9 <main+1>:	mov    %ebp,%esp
0x17ab <main+3>:	sub    %esp,92
0x17ae <main+6>:	push   %edi
0x17af <main+7>:	push   %esi
0x17b0 <main+8>:	push   %ebx
0x17b1 <main+9>:	call   0x1a70 <__get_eh_context>
0x17b6 <main+14>:	mov    %eax,%eax
0x17b8 <main+16>:	mov    %edx,%eax
0x17ba <main+18>:	mov    %eax,%edx
0x17bc <main+20>:	mov    DWORD PTR [%ebp-80],%eax
0x17bf <main+23>:	call   0x1994 <__main>
0x17c4 <main+28>:	mov    %eax,DWORD PTR [%ebp-80]
0x17c7 <main+31>:	add    %eax,4
0x17ca <main+34>:	mov    %edx,DWORD PTR [%eax]
0x17cc <main+36>:	mov    DWORD PTR [%ebp-24],%edx
0x17cf <main+39>:	mov    DWORD PTR [%ebp-20],0x0
0x17d6 <main+46>:	lea    %edx,[%ebp-16]
0x17d9 <main+49>:	mov    DWORD PTR [%edx],%ebp
0x17db <main+51>:	mov    %ecx,0x17ec
(gdb)

  I haven't included the files generated by save-temps since they are
  quite bulky.  However, if there is a discrepancy between what I'm
  seeing and your build then I'll be happy to pass them on to you.

>Fix:
	I don't have a fix since I'm not clear as to whether the fault
	is with gcc for planting the call to __get_eh_context before the
	call to __main or whether the pthread code needs patching to
	make sure that _thread_init is via __get_eh_context.  The
	workaround I'm using is to dynamically link the program :-

$ c++ -v -save-temps -g -O2 -o crash crash.cpp -pthread
Reading specs from /home/stephen/gcc-2.95.3/lib/gcc-lib/i386-unknown-openbsd2.8/2.95.3/specs
gcc version 2.95.3 20010315 (release)
 /home/stephen/gcc-2.95.3/lib/gcc-lib/i386-unknown-openbsd2.8/2.95.3/cpp0 -lang-c++ -v -D__GNUC__=2 -D__GNUG__=2 -D__GNUC_MINOR__=95 -D__cplusplus -D__unix__ -D__i386__ -D__OpenBSD__ -D__unix__ -D__i386__ -D__OpenBSD__ -Asystem(unix) -Asystem(OpenBSD) -Acpu(i386) -Amachine(i386) -D__EXCEPTIONS -D__OPTIMIZE__ -g -D_POSIX_THREADS crash.cpp crash.ii
GNU CPP version 2.95.3 20010315 (release) (80386, BSD syntax)
#include "..." search starts here:
#include <...> search starts here:
 /home/stephen/gcc-2.95.3/lib/gcc-lib/i386-unknown-openbsd2.8/2.95.3/../../../../include/g++-3
 /home/stephen/gcc-2.95.3/include
 /home/stephen/gcc-2.95.3/lib/gcc-lib/i386-unknown-openbsd2.8/2.95.3/../../../../i386-unknown-openbsd2.8/include
 /home/stephen/gcc-2.95.3/lib/gcc-lib/i386-unknown-openbsd2.8/2.95.3/include
 /usr/include
End of search list.
The following default directories have been omitted from the search path:
End of omitted list.
 /home/stephen/gcc-2.95.3/lib/gcc-lib/i386-unknown-openbsd2.8/2.95.3/cc1plus crash.ii -quiet -dumpbase crash.cc -g -O2 -version -o crash.s
GNU C++ version 2.95.3 20010315 (release) (i386-unknown-openbsd2.8) compiled by GNU C version 2.95.3 20010315 (release).
 as -o crash.o crash.s
 /home/stephen/gcc-2.95.3/lib/gcc-lib/i386-unknown-openbsd2.8/2.95.3/collect2 -e start -dc -dp -o crash /usr/lib/crt0.o -L/home/stephen/gcc-2.95.3/lib/gcc-lib/i386-unknown-openbsd2.8/2.95.3 -L/home/stephen/gcc-2.95.3/lib crash.o -lstdc++ -lm -lgcc -lc_r -lgcc
$ ./crash
hello
$ 

>Audit-Trail:
>Unformatted: