diff --git a/compiler-rt/include/sanitizer/tsan_interface.h b/compiler-rt/include/sanitizer/tsan_interface.h --- a/compiler-rt/include/sanitizer/tsan_interface.h +++ b/compiler-rt/include/sanitizer/tsan_interface.h @@ -141,7 +141,7 @@ // and freed by __tsan_destroy_fiber. // - TSAN context of current fiber or thread can be obtained // by calling __tsan_get_current_fiber. -// - __tsan_switch_to_fiber should be called immediatly before switch +// - __tsan_switch_to_fiber should be called immediately before switch // to fiber, such as call of swapcontext. // - Fiber name can be set by __tsan_set_fiber_name. void *__tsan_get_current_fiber(void); @@ -154,6 +154,15 @@ // Do not establish a happens-before relation between fibers static const unsigned __tsan_switch_to_fiber_no_sync = 1 << 0; +// User-provided callback invoked on TSan initialization. +void __tsan_on_initialize(); + +// User-provided callback invoked on TSan shutdown. +// `failed` - Nonzero if TSan did detect issues, zero otherwise. +// Return `0` if TSan should exit as if no issues were detected. Return nonzero +// if TSan should exit as if issues were detected. +int __tsan_on_finalize(int failed); + #ifdef __cplusplus } // extern "C" #endif diff --git a/compiler-rt/lib/tsan/rtl/tsan_interface.h b/compiler-rt/lib/tsan/rtl/tsan_interface.h --- a/compiler-rt/lib/tsan/rtl/tsan_interface.h +++ b/compiler-rt/lib/tsan/rtl/tsan_interface.h @@ -415,6 +415,13 @@ SANITIZER_INTERFACE_ATTRIBUTE void __tsan_go_atomic64_compare_exchange(ThreadState *thr, uptr cpc, uptr pc, u8 *a); + +SANITIZER_INTERFACE_ATTRIBUTE +void __tsan_on_initialize(); + +SANITIZER_INTERFACE_ATTRIBUTE +int __tsan_on_finalize(int failed); + } // extern "C" } // namespace __tsan diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp --- a/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp +++ b/compiler-rt/lib/tsan/rtl/tsan_rtl.cpp @@ -21,6 +21,7 @@ #include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_symbolizer.h" #include "tsan_defs.h" +#include "tsan_interface.h" #include "tsan_mman.h" #include "tsan_platform.h" #include "tsan_suppressions.h" @@ -57,12 +58,23 @@ bool OnFinalize(bool failed); void OnInitialize(); #else +#include SANITIZER_WEAK_CXX_DEFAULT_IMPL bool OnFinalize(bool failed) { +#if !SANITIZER_GO + if (auto *ptr = dlsym(RTLD_DEFAULT, "__tsan_on_finalize")) + return reinterpret_cast(ptr)(failed); +#endif return failed; } SANITIZER_WEAK_CXX_DEFAULT_IMPL -void OnInitialize() {} +void OnInitialize() { +#if !SANITIZER_GO + if (auto *ptr = dlsym(RTLD_DEFAULT, "__tsan_on_initialize")) { + return reinterpret_cast(ptr)(); + } +#endif +} #endif static char thread_registry_placeholder[sizeof(ThreadRegistry)]; diff --git a/compiler-rt/test/tsan/on_initialize_finalize_hooks.cpp b/compiler-rt/test/tsan/on_initialize_finalize_hooks.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/tsan/on_initialize_finalize_hooks.cpp @@ -0,0 +1,33 @@ +// RUN: %clang_tsan -O1 %s -o %t.lib -fno-sanitize=thread -shared -fPIC -DBUILD_LIB=1 +// RUN: %clang_tsan -O1 %s %t.lib -o %t +// RUN: %run %t | FileCheck %s + +// Test that initialization/finalization hooks are called, even when they are +// not defined in the main executable, but by another another library that +// doesn't directly link against the TSan runtime. + +#include + +#if BUILD_LIB + +extern "C" void __tsan_on_initialize() { + printf("__tsan_on_initialize()\n"); +} + +extern "C" int __tsan_on_finalize(int failed) { + printf("__tsan_on_finalize()\n"); + return failed; +} + +#else // BUILD_LIB + +int main() { + printf("main()\n"); + return 0; +} + +#endif // BUILD_LIB + +// CHECK: __tsan_on_initialize() +// CHECK: main() +// CHECK: __tsan_on_finalize()