[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
library/1750: static linking + C++ exceptions + pthreads = SEGV
- To: gnats@openbsd.org
- Subject: library/1750: static linking + C++ exceptions + pthreads = SEGV
- From: "Stephen J. Bevan" <stephen@etunnels.com>
- Date: Fri, 30 Mar 2001 11:46:20 -0800 (PST)
- Resent-Date: Fri, 30 Mar 2001 12:50:02 -0700 (MST)
- Resent-From: gnats@cvs.openbsd.org (GNATS Management)
- Resent-Message-Id: <200103301950.f2UJo2D24734@cvs.openbsd.org>
- Resent-Reply-To: gnats@cvs.openbsd.org, stephen@etunnels.com
- Resent-To: bugs@cvs.openbsd.org
>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: