Calling pthread_exit() in the main thread segfaults in a statically-linked executable. $ cat pthread-exit-main.c #include <pthread.h> int main() { pthread_exit(NULL); return 1; } $ gcc -Wall pthread-exit-main.c -o pthread-exit-main $ ./pthread-exit-main $ echo $? 0 $ gcc -Wall pthread-exit-main.c -o pthread-exit-main -static -lpthread $ ./pthread-exit-main Segmentation fault $ gdb ./pthread-exit-main (gdb) run Starting program: /home/mseaborn/test/pthread-exit-main Program received signal SIGSEGV, Segmentation fault. 0x0000000000000000 in ?? () This was with glibc 2.11.1 on Ubuntu Lucid. I have also seen this with glibc 2.9.
I am not able to reproduce this on trunk.
I reproduced it with glibc 2.12.2, gcc 4.4.6, binutils 2.20.1. It is a linking issue. Look at a file csu/libc-start.c: //======================================================= extern unsigned int __nptl_nthreads __attribute ((weak)); unsigned int *const ptr = &__nptl_nthreads; if (! atomic_decrement_and_test (ptr)) /* Not much left to do but to exit the thread, not the process. */ __exit_thread (0); //======================================================= This atomic_decrement_and_test finally looks like this: 0x0000000000400629 <+489>: lock decl -0x400630(%rip) # 0x0 0x0000000000400630 <+496>: sete %dl So it seems that address of __nptl_nthreads became NULL after linking. The variable is stored in file nptl/pthread_create.c. The program above will work fine if you add dummy call to pthread_create(): $ cat exit.c #include <pthread.h> void foo() { pthread_create(NULL, NULL, NULL, NULL); } int main() { pthread_exit(NULL); return 1; } $ gcc exit.c -static -lpthread $ ./a.out $ echo $? 0 $ objdump -d ./a.out # ................. 409e09: f0 ff 0d 40 62 29 00 lock decl 0x296240(%rip) # 6a0050 <__nptl_nthreads> 409e10: 0f 94 c2 sete %dl # .................
I guess nobody expected static program working with threads but not invoking pthread_create() anywhere. So in real cases weak reference doesn't make problems Obvious solution could be adding reference to __nptl_nthreads in pthread_exit.c
(In reply to comment #3) > I guess nobody expected static program working with threads but not invoking > pthread_create() anywhere. So in real cases weak reference doesn't make > problems > Obvious solution could be adding reference to __nptl_nthreads in pthread_exit.c The fact that this doesn't crash in master is sheer luck. In the non-static case the forwarder calls exit() when you ask for pthread_exit() and everything works correctly. In the static case you actually call pthread_exit() and it breaks because nothing included __nptl_nthreads from pthread_create.os and that means it has a zero value. Static linking should work for all reasonable cases. This case is reasonable because the main thread is a real thread and you should be able to exit from it using pthread_exit. We need a reference to __nptl_nthreads as you suggest. Doing so will pull in all of pthread_exit.os into the application image, so it might grow a little in size. I don't see that as a problem. Please post a patch to libc-alpha@sourceware.org to fix this. I suggest building glibc master, and testing the patch with that, and making sure `make -k check' doesn't show any regressions. See: http://sourceware.org/glibc/wiki/Testing/Builds and http://sourceware.org/glibc/wiki/Contribution%20checklist Your change is sufficiently minimal that it probably falls below the legally significant barrier and doesn't require a copyright assignment. However, if you plan to contribute more changes then we strongly recommend signing a copyright assignment for glibc to the FSF (or whatever you want to do e.g. individual disclaimer).
There are two another solutions: 1) Move __nptl_nthreads to separate object file and don't mark the reference as weak. I think it is better semantically. 2) Check if &__nptl_nthreads == NULL in libc-start. What do you think about it?
(In reply to Vladimir Nikulichev from comment #5) > There are two another solutions: > > 1) Move __nptl_nthreads to separate object file and don't mark the reference > as weak. I think it is better semantically. Please investigate having pthread_exit reference __nptl_nthreads. We don't want to unconditionally include __nptl_nthreads if we can avoid it. > 2) Check if &__nptl_nthreads == NULL in libc-start. This isn't needed if pthread_exit correctly references all of the data that it needs. Keeping the code simpler is better for maintenance.
I sent a patch to libc-alpha@sourceware.org
Obviously better fix (sent another patch): http://sourceware.org/ml/libc-alpha/2013-06/msg00862.html
~~~ Vladimir Nikulichev 2013-06-23 14:56:33 UTC ~~~ Obviously better fix (sent another patch): http://sourceware.org/ml/libc-alpha/2013-06/msg00862.html. ~~~
Fixed. commit 963509c045d192b5a27891617b907fb857042f36 Author: Vladimir Nikulichev <v.nikulichev@gmail.com> Date: Mon Jun 24 17:08:07 2013 -0400 BZ #12310: pthread_exit in static app. segfaults Static applications that call pthread_exit on the main thread segfault. This is because after a thread terminates __libc_start_main decrements __nptl_nthreads which is only defined in pthread_create. Therefore the right solution is to add a requirement to pthread_create from pthread_exit. ~~~ nptl/ 2013-06-24 Vladimir Nikulichev <v.nikulichev@gmail.com> [BZ #12310] * pthread_exit.c: Add reference to pthread_create. ---