Index: lib/tsan/rtl/tsan_interceptors.cc =================================================================== --- lib/tsan/rtl/tsan_interceptors.cc +++ lib/tsan/rtl/tsan_interceptors.cc @@ -821,6 +821,7 @@ thr->signal_ctx = 0; UnmapOrDie(sctx, sizeof(*sctx)); } + cur_thread_finalize(); } } // namespace __tsan Index: lib/tsan/rtl/tsan_platform_mac.cc =================================================================== --- lib/tsan/rtl/tsan_platform_mac.cc +++ lib/tsan/rtl/tsan_platform_mac.cc @@ -40,6 +40,44 @@ namespace __tsan { +#ifndef SANITIZER_GO +// On OS X, accessing TLVs via __thread or manually by using pthread_key_* is +// problematic, because there are several places where interceptors are called +// when TLVs are not accessible (early process startup, thread cleanup, ...). +// The following provides a "poor man's TLV" implementation, where we use the +// shadow memory of the pointer returned by pthread_self() to store a pointer to +// the ThreadState object. The main thread's ThreadState pointer is stored +// separately in a static variable, because we need to access it even before the +// shadow memory is set up. +static uptr main_thread_identity = 0; +static ThreadState *main_thread_state = nullptr; + +ThreadState *cur_thread() { + ThreadState **fake_tls; + uptr thread_identity = (uptr)pthread_self(); + if (thread_identity == main_thread_identity || main_thread_identity == 0) { + if (main_thread_identity == 0) main_thread_identity = thread_identity; + fake_tls = &main_thread_state; + } else { + fake_tls = (ThreadState **)MemToShadow(thread_identity); + } + if (*fake_tls == nullptr) { + *fake_tls = (ThreadState *)InternalAlloc(sizeof(ThreadState), nullptr); + internal_memset(*fake_tls, 0, sizeof(ThreadState)); + } + return *fake_tls; +} + +void cur_thread_finalize() { + ThreadState **fake_tls; + uptr thread_identity = (uptr)pthread_self(); + CHECK_NE(thread_identity, main_thread_identity); + fake_tls = (ThreadState **)MemToShadow(thread_identity); + InternalFree(*fake_tls, nullptr); + *fake_tls = nullptr; +} +#endif + uptr GetShadowMemoryConsumption() { return 0; } Index: lib/tsan/rtl/tsan_rtl.h =================================================================== --- lib/tsan/rtl/tsan_rtl.h +++ lib/tsan/rtl/tsan_rtl.h @@ -409,13 +409,20 @@ uptr tls_addr, uptr tls_size); }; -#ifndef SANITIZER_GO +#if !defined(SANITIZER_GO) && !SANITIZER_MAC __attribute__((tls_model("initial-exec"))) extern THREADLOCAL char cur_thread_placeholder[]; INLINE ThreadState *cur_thread() { return reinterpret_cast(&cur_thread_placeholder); } #endif +#if !defined(SANITIZER_GO) && SANITIZER_MAC +ThreadState *cur_thread(); +void cur_thread_finalize(); +#endif +#if !SANITIZER_MAC +INLINE void cur_thread_finalize() { } +#endif class ThreadContext : public ThreadContextBase { public: Index: lib/tsan/rtl/tsan_rtl.cc =================================================================== --- lib/tsan/rtl/tsan_rtl.cc +++ lib/tsan/rtl/tsan_rtl.cc @@ -44,7 +44,7 @@ namespace __tsan { -#ifndef SANITIZER_GO +#if !defined(SANITIZER_GO) && !SANITIZER_MAC THREADLOCAL char cur_thread_placeholder[sizeof(ThreadState)] ALIGNED(64); #endif static char ctx_placeholder[sizeof(Context)] ALIGNED(64);