Skip to content

Commit 26e0fb8

Browse files
author
Julian Lettner
committed
[TSan] Support initialize/finalize hooks in dynamic libraries
Make TSan runtime initialization and finalization hooks work even if these hooks are not built in the main executable. When these hooks are defined in another library that is not directly linked against the TSan runtime (e.g., Swift runtime) we cannot rely on the "strong-def overriding weak-def" mechanics and have to look them up via `dlsym()`. Let's also define hooks that are easier to use from C-only code: ``` extern "C" void __tsan_on_initialize(); extern "C" int __tsan_on_finalize(int failed); ``` For now, these will call through to the old hooks. Eventually, we want to adopt the new hooks downstream and remove the old ones. This is part of the effort to support Swift Tasks (async/await and actors) in TSan. rdar://74256720 Reviewed By: vitalybuka, delcypher Differential Revision: https://reviews.llvm.org/D98810
1 parent 80f6c99 commit 26e0fb8

File tree

4 files changed

+63
-2
lines changed

4 files changed

+63
-2
lines changed

compiler-rt/include/sanitizer/tsan_interface.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ void __tsan_external_write(void *addr, void *caller_pc, void *tag);
141141
// and freed by __tsan_destroy_fiber.
142142
// - TSAN context of current fiber or thread can be obtained
143143
// by calling __tsan_get_current_fiber.
144-
// - __tsan_switch_to_fiber should be called immediatly before switch
144+
// - __tsan_switch_to_fiber should be called immediately before switch
145145
// to fiber, such as call of swapcontext.
146146
// - Fiber name can be set by __tsan_set_fiber_name.
147147
void *__tsan_get_current_fiber(void);
@@ -154,6 +154,15 @@ void __tsan_set_fiber_name(void *fiber, const char *name);
154154
// Do not establish a happens-before relation between fibers
155155
static const unsigned __tsan_switch_to_fiber_no_sync = 1 << 0;
156156

157+
// User-provided callback invoked on TSan initialization.
158+
void __tsan_on_initialize();
159+
160+
// User-provided callback invoked on TSan shutdown.
161+
// `failed` - Nonzero if TSan did detect issues, zero otherwise.
162+
// Return `0` if TSan should exit as if no issues were detected. Return nonzero
163+
// if TSan should exit as if issues were detected.
164+
int __tsan_on_finalize(int failed);
165+
157166
#ifdef __cplusplus
158167
} // extern "C"
159168
#endif

compiler-rt/lib/tsan/rtl/tsan_interface.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -415,6 +415,13 @@ void __tsan_go_atomic32_compare_exchange(ThreadState *thr, uptr cpc, uptr pc,
415415
SANITIZER_INTERFACE_ATTRIBUTE
416416
void __tsan_go_atomic64_compare_exchange(ThreadState *thr, uptr cpc, uptr pc,
417417
u8 *a);
418+
419+
SANITIZER_INTERFACE_ATTRIBUTE
420+
void __tsan_on_initialize();
421+
422+
SANITIZER_INTERFACE_ATTRIBUTE
423+
int __tsan_on_finalize(int failed);
424+
418425
} // extern "C"
419426

420427
} // namespace __tsan

compiler-rt/lib/tsan/rtl/tsan_rtl.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "sanitizer_common/sanitizer_stackdepot.h"
2222
#include "sanitizer_common/sanitizer_symbolizer.h"
2323
#include "tsan_defs.h"
24+
#include "tsan_interface.h"
2425
#include "tsan_mman.h"
2526
#include "tsan_platform.h"
2627
#include "tsan_suppressions.h"
@@ -57,12 +58,23 @@ Context *ctx;
5758
bool OnFinalize(bool failed);
5859
void OnInitialize();
5960
#else
61+
#include <dlfcn.h>
6062
SANITIZER_WEAK_CXX_DEFAULT_IMPL
6163
bool OnFinalize(bool failed) {
64+
#if !SANITIZER_GO
65+
if (auto *ptr = dlsym(RTLD_DEFAULT, "__tsan_on_finalize"))
66+
return reinterpret_cast<decltype(&__tsan_on_finalize)>(ptr)(failed);
67+
#endif
6268
return failed;
6369
}
6470
SANITIZER_WEAK_CXX_DEFAULT_IMPL
65-
void OnInitialize() {}
71+
void OnInitialize() {
72+
#if !SANITIZER_GO
73+
if (auto *ptr = dlsym(RTLD_DEFAULT, "__tsan_on_initialize")) {
74+
return reinterpret_cast<decltype(&__tsan_on_initialize)>(ptr)();
75+
}
76+
#endif
77+
}
6678
#endif
6779

6880
static char thread_registry_placeholder[sizeof(ThreadRegistry)];
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// RUN: %clang_tsan -O1 %s -o %t.lib -fno-sanitize=thread -shared -fPIC -DBUILD_LIB=1
2+
// RUN: %clang_tsan -O1 %s %t.lib -o %t
3+
// RUN: %run %t | FileCheck %s
4+
5+
// Test that initialization/finalization hooks are called, even when they are
6+
// not defined in the main executable, but by another another library that
7+
// doesn't directly link against the TSan runtime.
8+
9+
#include <stdio.h>
10+
11+
#if BUILD_LIB
12+
13+
extern "C" void __tsan_on_initialize() {
14+
printf("__tsan_on_initialize()\n");
15+
}
16+
17+
extern "C" int __tsan_on_finalize(int failed) {
18+
printf("__tsan_on_finalize()\n");
19+
return failed;
20+
}
21+
22+
#else // BUILD_LIB
23+
24+
int main() {
25+
printf("main()\n");
26+
return 0;
27+
}
28+
29+
#endif // BUILD_LIB
30+
31+
// CHECK: __tsan_on_initialize()
32+
// CHECK: main()
33+
// CHECK: __tsan_on_finalize()

0 commit comments

Comments
 (0)