I'll answer my own question in case this is ever useful for someone else. OS X sets up gs
to point to the TLS storage for the current thread. This is actually a part of the thread's data block (struct _pthread
), as can be found out by reading Darwin source code:
http://www.opensource.apple.com/source/Libc/Libc-391/pthreads/pthread_internals.h
It's easy to retrieve a pointer to this data block: pthread_self
will return it. By logging this, I found out that the data block was most likely freed by someone else while the thread is still executing. By trapping vm_deallocate
using mach_override, I found out that this was done by the cleanup code for another thread.
Eventually it turned out that I was calling pthread_join
on a thread that was already detached via pthread_detach
. Both functions will free the thread storage. After the thread had been detached (but before the erroneous join), another thread was created with the exact same base address by chance. The join would free the new thread, leaving it to execute without its data block. This bug was caused by the different behavior of the pthread library compared to Windows, where waiting on a thread (join) and closing it (detach) are two completely different things.