diff --git a/compiler-rt/lib/tsan/rtl/CMakeLists.txt b/compiler-rt/lib/tsan/rtl/CMakeLists.txt index 057e53a00d91329014871059ee42d9e7fca072a4..3b225fb2d09cc85e1dbeb5d59b68fec41026621c 100644 --- a/compiler-rt/lib/tsan/rtl/CMakeLists.txt +++ b/compiler-rt/lib/tsan/rtl/CMakeLists.txt @@ -14,12 +14,16 @@ set(TSAN_DYNAMIC_LINK_LIBS ${SANITIZER_CXX_ABI_LIBRARIES} ${SANITIZER_COMMON_LIN #OHOS_LOCAL set(TSAN_DYNAMIC_LINK_FLAGS ${SANITIZER_COMMON_LINK_FLAGS}) +#OHOS_LOCAL +set(TSAN_STATIC_FLAGS "") + #OHOS_LOCAL begin if(OHOS) # Put tsan library in the global group so that libraries which don't depend on tsan can also find symbols from tsan. if (COMPILER_RT_HAS_Z_GLOBAL) list(APPEND TSAN_DYNAMIC_LINK_FLAGS -Wl,-z,global) endif() + list(APPEND TSAN_STATIC_FLAGS "-DTSAN_STATIC") endif() #OHOS_LOCAL end @@ -255,7 +259,8 @@ else() $ $ ADDITIONAL_HEADERS ${TSAN_HEADERS} - CFLAGS ${TSAN_RTL_CFLAGS} + #OHOS_LOCAL + CFLAGS ${TSAN_RTL_CFLAGS} ${TSAN_STATIC_FLAGS} PARENT_TARGET tsan) add_compiler_rt_runtime(clang_rt.tsan_cxx STATIC @@ -263,7 +268,8 @@ else() SOURCES ${TSAN_CXX_SOURCES} $ ADDITIONAL_HEADERS ${TSAN_HEADERS} - CFLAGS ${TSAN_RTL_CFLAGS} + #OHOS_LOCAL + CFLAGS ${TSAN_RTL_CFLAGS} ${TSAN_STATIC_FLAGS} PARENT_TARGET tsan) list(APPEND TSAN_RUNTIME_LIBRARIES clang_rt.tsan-${arch} clang_rt.tsan_cxx-${arch}) diff --git a/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp b/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp index 595c7715b6af7b47c6a055d53c5fcd17188acc9a..4b92cc57eb34ba5d3660288627d516b9be8221d6 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp +++ b/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp @@ -25,6 +25,11 @@ #include "interception/interception.h" #include "tsan_interceptors.h" #include "tsan_interface.h" +// OHOS_LOCAL begin +#if SANITIZER_OHOS +#include "tsan_interface_ann.h" +#endif +// OHOS_LOCAL end #include "tsan_platform.h" #include "tsan_suppressions.h" #include "tsan_rtl.h" @@ -1969,6 +1974,74 @@ TSAN_INTERCEPTOR(int, pthread_sigmask, int how, const __sanitizer_sigset_t *set, return REAL(pthread_sigmask)(how, set, oldset); } +#if SANITIZER_OHOS && !TSAN_STATIC +// OHOS_LOCAL begin +struct call_once_callback_args { + void (*orig_func)(void *arg); + void *orig_arg; + void *flag; +}; + +void call_once_callback_wrapper(void *arg) { + call_once_callback_args *new_args = (call_once_callback_args *)arg; + new_args->orig_func(new_args->orig_arg); + __tsan_release(new_args->flag); +} + +// We need a special way to intercept call_once. +#define INTERCEPTOR_CALL_ONCE(ret_type, func, ...) \ + DECLARE_WRAPPER(ret_type, func, __VA_ARGS__) \ + extern "C" \ + INTERCEPTOR_ATTRIBUTE \ + ret_type WRAP(func)(__VA_ARGS__) + +DEFINE_REAL(void, _ZNSt3__h11__call_onceERVmPvPFvS2_E, void *flag, void *arg, + void (*func)(void *arg)) + +DEFINE_REAL(void, _ZNSt4__n111__call_onceERVmPvPFvS2_E, void *flag, void *arg, + void (*func)(void *arg)) + +// This adds a libc++ interceptor for: +// void __call_once(volatile unsigned long&, void*, void(*)(void*)); +// Tsan can't see the atomic operation in this interface. To avoid false +// positives, we intercept it and do an explicit Release after the user code. +INTERCEPTOR_CALL_ONCE(void, _ZNSt3__h11__call_onceERVmPvPFvS2_E, void *flag, + void *arg, void (*func)(void *arg)) { + call_once_callback_args new_args = {func, arg, flag}; + if (REAL(_ZNSt3__h11__call_onceERVmPvPFvS2_E)) { + REAL(_ZNSt3__h11__call_onceERVmPvPFvS2_E)(flag, &new_args, + call_once_callback_wrapper); + } else if (REAL(_ZNSt4__n111__call_onceERVmPvPFvS2_E)) { + REAL(_ZNSt4__n111__call_onceERVmPvPFvS2_E)(flag, &new_args, + call_once_callback_wrapper); + } else { + Report("ThreadSanitizer: can't find call_once.\n"); + Die(); + } +}; + +// For __call_once in libc++_shared.so. +// We can't intercept this symbol in libc++_shared.so, because libc++_shared.so +// may not be loaded when we do the intercept, so we call the symbol in +// libc++.so instead. +INTERCEPTOR_CALL_ONCE(void, _ZNSt4__n111__call_onceERVmPvPFvS2_E, void *flag, + void *arg, void (*func)(void *arg)) { + call_once_callback_args new_args = {func, arg, flag}; + if (REAL(_ZNSt4__n111__call_onceERVmPvPFvS2_E)) { + REAL(_ZNSt4__n111__call_onceERVmPvPFvS2_E)(flag, &new_args, + call_once_callback_wrapper); + } else if (REAL(_ZNSt3__h11__call_onceERVmPvPFvS2_E)) { + REAL(_ZNSt3__h11__call_onceERVmPvPFvS2_E)(flag, &new_args, + call_once_callback_wrapper); + } else { + Report("ThreadSanitizer: can't find call_once.\n"); + Die(); + } +}; + +// OHOS_LOCAL end +#endif + namespace __tsan { static void ReportErrnoSpoiling(ThreadState *thr, uptr pc, int sig) { @@ -2962,6 +3035,13 @@ void InitializeInterceptors() { #endif #endif + // OHOS_LOCAL begin +#if SANITIZER_OHOS && !TSAN_STATIC + TSAN_INTERCEPT(_ZNSt3__h11__call_onceERVmPvPFvS2_E); + TSAN_INTERCEPT(_ZNSt4__n111__call_onceERVmPvPFvS2_E); +#endif + // OHOS_LOCAL end + TSAN_MAYBE_INTERCEPT__LWP_EXIT; TSAN_MAYBE_INTERCEPT_THR_EXIT; diff --git a/compiler-rt/test/tsan/libcxx_call_once.cpp b/compiler-rt/test/tsan/libcxx_call_once.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3e9e20a8e47d5ded6185a5e07136d05bea4ae239 --- /dev/null +++ b/compiler-rt/test/tsan/libcxx_call_once.cpp @@ -0,0 +1,27 @@ +// RUN: %clangxx_tsan %s -o %t && %run %t 2>&1 | FileCheck %s + +// REQUIRES: ohos_family + +#include +#include + +static int global = 0; +static std::once_flag flag; + +static void thread_func() { + std::call_once(flag, [] { global = 1; }); + fprintf(stderr, "global = %d.\n", global); +} + +int main() { + fprintf(stderr, "Begin.\n"); + std::thread t1(thread_func); + std::thread t2(thread_func); + t1.join(); + t2.join(); + fprintf(stderr, "End.\n"); +} + +// CHECK: Begin. +// CHECK-NOT: WARNING: ThreadSanitizer +// CHECK: End.