Index: cmake/Modules/SanitizerUtils.cmake =================================================================== --- cmake/Modules/SanitizerUtils.cmake +++ cmake/Modules/SanitizerUtils.cmake @@ -44,6 +44,33 @@ endif() endmacro() +macro(add_sanitizer_version_script name verscript) + set(stamp ${CMAKE_CURRENT_BINARY_DIR}/${name}.ver-stamp) + add_custom_command(OUTPUT ${stamp} + COMMAND ${CMAKE_COMMAND} -E copy ${verscript} $.ver + COMMAND ${CMAKE_COMMAND} -E touch ${stamp} + DEPENDS ${verscript} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMENT "Generating version script for ${name}" + VERBATIM) + add_custom_target(${name}-version ALL DEPENDS ${stamp} SOURCES ${verscript}) + if(NOT CMAKE_VERSION VERSION_LESS 3.0) + install(FILES $.ver DESTINATION ${COMPILER_RT_LIBRARY_INSTALL_DIR}) + else() + # Per-config install location. + if(CMAKE_CONFIGURATION_TYPES) + foreach(c ${CMAKE_CONFIGURATION_TYPES}) + get_target_property(libfile ${name} LOCATION_${c}) + install(FILES ${libfile}.ver CONFIGURATIONS ${c} + DESTINATION ${COMPILER_RT_LIBRARY_INSTALL_DIR}) + endforeach() + else() + get_target_property(libfile ${name} LOCATION_${CMAKE_BUILD_TYPE}) + install(FILES ${libfile}.ver DESTINATION ${COMPILER_RT_LIBRARY_INSTALL_DIR}) + endif() + endif() +endmacro() + # Add target to check code style for sanitizer runtimes. if(UNIX) add_custom_target(SanitizerLintCheck Index: lib/asan/CMakeLists.txt =================================================================== --- lib/asan/CMakeLists.txt +++ lib/asan/CMakeLists.txt @@ -164,6 +164,11 @@ add_dependencies(asan clang_rt.asan-${arch}-symbols) endif() + if (UNIX AND NOT ANDROID) + add_sanitizer_version_script(clang_rt.asan-${arch} ../sanitizer_common/sanitizer.ver) + add_dependencies(asan clang_rt.asan-${arch}-version) + endif() + if (WIN32) add_compiler_rt_runtime(clang_rt.asan_dll_thunk-${arch} ${arch} STATIC SOURCES asan_dll_thunk.cc Index: lib/interception/interception.h =================================================================== --- lib/interception/interception.h +++ lib/interception/interception.h @@ -239,18 +239,12 @@ #if defined(__linux__) || defined(__FreeBSD__) # include "interception_linux.h" # define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_LINUX_OR_FREEBSD(func) -# define INTERCEPT_FUNCTION_VER(func, symver) \ - INTERCEPT_FUNCTION_VER_LINUX_OR_FREEBSD(func, symver) #elif defined(__APPLE__) # include "interception_mac.h" # define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_MAC(func) -# define INTERCEPT_FUNCTION_VER(func, symver) \ - INTERCEPT_FUNCTION_VER_MAC(func, symver) #else // defined(_WIN32) # include "interception_win.h" # define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_WIN(func) -# define INTERCEPT_FUNCTION_VER(func, symver) \ - INTERCEPT_FUNCTION_VER_WIN(func, symver) #endif #undef INCLUDED_FROM_INTERCEPTION_LIB Index: lib/interception/interception_linux.h =================================================================== --- lib/interception/interception_linux.h +++ lib/interception/interception_linux.h @@ -35,12 +35,14 @@ (::__interception::uptr) & WRAP(func)) #if !defined(__ANDROID__) // android does not have dlvsym -# define INTERCEPT_FUNCTION_VER_LINUX_OR_FREEBSD(func, symver) \ +# define INTERCEPT_FUNCTION_VER_SINGLE(func, symver) \ ::__interception::real_##func = (func##_f)(unsigned long) \ ::__interception::GetFuncAddrVer(#func, symver) -#else -# define INTERCEPT_FUNCTION_VER_LINUX_OR_FREEBSD(func, symver) \ - INTERCEPT_FUNCTION_LINUX_OR_FREEBSD(func) + +#define INTERCEPT_FUNCTION_VER(func, funcver, symver, def) \ + __asm__(".symver " #funcver "," #func "@" def #symver); \ + ::__interception::real_##funcver = (funcver##_f)(unsigned long) \ + ::__interception::GetFuncAddrVer(#func, #symver) #endif // !defined(__ANDROID__) #endif // INTERCEPTION_LINUX_H Index: lib/msan/CMakeLists.txt =================================================================== --- lib/msan/CMakeLists.txt +++ lib/msan/CMakeLists.txt @@ -32,6 +32,10 @@ add_sanitizer_rt_symbols(clang_rt.msan-${arch} msan.syms.extra) add_dependencies(msan clang_rt.msan-${arch}-symbols) endif() + if(UNIX AND NOT ANDROID) + add_sanitizer_version_script(clang_rt.msan-${arch} ../sanitizer_common/sanitizer.ver) + add_dependencies(msan clang_rt.msan-${arch}-version) + endif() endif() add_compiler_rt_resource_file(msan_blacklist msan_blacklist.txt) Index: lib/sanitizer_common/sanitizer.ver =================================================================== --- lib/sanitizer_common/sanitizer.ver +++ lib/sanitizer_common/sanitizer.ver @@ -0,0 +1,6 @@ +GLIBC_2.2.5 { +}; +GLIBC_2.3.2 { + global: *; +} GLIBC_2.2.5; + Index: lib/sanitizer_common/sanitizer_common_interceptors.inc =================================================================== --- lib/sanitizer_common/sanitizer_common_interceptors.inc +++ lib/sanitizer_common/sanitizer_common_interceptors.inc @@ -2649,43 +2649,7 @@ #endif #if SANITIZER_INTERCEPT_PTHREAD_COND -// Problem: -// NPTL implementation of pthread_cond has 2 versions (2.2.5 and 2.3.2). -// pthread_cond_t has different size in the different versions. -// If call new REAL functions for old pthread_cond_t, they will corrupt memory -// after pthread_cond_t (old cond is smaller). -// If we call old REAL functions for new pthread_cond_t, we will lose some -// functionality (e.g. old functions do not support waiting against -// CLOCK_REALTIME). -// Proper handling would require to have 2 versions of interceptors as well. -// But this is messy, in particular requires linker scripts when sanitizer -// runtime is linked into a shared library. -// Instead we assume we don't have dynamic libraries built against old -// pthread (2.2.5 is dated by 2002). And provide legacy_pthread_cond flag -// that allows to work with old libraries (but this mode does not support -// some features, e.g. pthread_condattr_getpshared). -static void *init_cond(void *c, bool force = false) { - // sizeof(pthread_cond_t) >= sizeof(uptr) in both versions. - // So we allocate additional memory on the side large enough to hold - // any pthread_cond_t object. Always call new REAL functions, but pass - // the aux object to them. - // Note: the code assumes that PTHREAD_COND_INITIALIZER initializes - // first word of pthread_cond_t to zero. - // It's all relevant only for linux. - if (!common_flags()->legacy_pthread_cond) - return c; - atomic_uintptr_t *p = (atomic_uintptr_t*)c; - uptr cond = atomic_load(p, memory_order_acquire); - if (!force && cond != 0) - return (void*)cond; - void *newcond = WRAP(malloc)(pthread_cond_t_sz); - internal_memset(newcond, 0, pthread_cond_t_sz); - if (atomic_compare_exchange_strong(p, &cond, (uptr)newcond, - memory_order_acq_rel)) - return newcond; - WRAP(free)(newcond); - return (void*)cond; -} +namespace __sanitizer { struct CondMutexUnlockCtx { void *ctx; @@ -2696,49 +2660,42 @@ COMMON_INTERCEPTOR_MUTEX_LOCK(arg->ctx, arg->m); } -namespace __sanitizer { int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m, void *abstime), void *c, void *m, void *abstime, void(*cleanup)(void *arg), void *arg); -} // namespace __sanitizer -INTERCEPTOR(int, pthread_cond_init, void *c, void *a) { - void *cond = init_cond(c, true); - void *ctx; - COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_init, cond, a); +static int cond_init(void *ctx, int(*real)(void *c, void *a), + void *c, void *a) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, c, sizeof(uptr)); - return REAL(pthread_cond_init)(cond, a); + return real(c, a); } -INTERCEPTOR(int, pthread_cond_wait, void *c, void *m) { - void *cond = init_cond(c); - void *ctx; - COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_wait, cond, m); +static int cond_wait(void *ctx, int(*real)(void *c, void *m), + void *c, void *m) { COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m); COMMON_INTERCEPTOR_READ_RANGE(ctx, c, sizeof(uptr)); CondMutexUnlockCtx arg = {ctx, m}; // This ensures that we handle mutex lock even in case of pthread_cancel. // See test/tsan/cond_cancel.cc. int res = __sanitizer::call_pthread_cancel_with_cleanup( - (int(*)(void *c, void *m, void *abstime))REAL(pthread_cond_wait), - cond, m, 0, (void(*)(void *arg))cond_mutex_unlock, &arg); + (int(*)(void *c, void *m, void *abstime))real, + c, m, 0, (void(*)(void *arg))cond_mutex_unlock, &arg); if (res == errno_EOWNERDEAD) COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m); COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m); return res; } -INTERCEPTOR(int, pthread_cond_timedwait, void *c, void *m, void *abstime) { - void *cond = init_cond(c); - void *ctx; - COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_timedwait, cond, m, abstime); +static int cond_timedwait(void *ctx, + int (*real)(void *c, void *m, void *abstime), + void *c, void *m, void *abstime) { COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m); COMMON_INTERCEPTOR_READ_RANGE(ctx, c, sizeof(uptr)); CondMutexUnlockCtx arg = {ctx, m}; // This ensures that we handle mutex lock even in case of pthread_cancel. // See test/tsan/cond_cancel.cc. int res = __sanitizer::call_pthread_cancel_with_cleanup( - REAL(pthread_cond_timedwait), cond, m, abstime, + real, c, m, abstime, (void(*)(void *arg))cond_mutex_unlock, &arg); if (res == errno_EOWNERDEAD) COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m); @@ -2746,44 +2703,193 @@ return res; } +static int cond_signal(void *ctx, int(*real)(void *c), void *c) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, c, sizeof(uptr)); + return real(c); +} + +static int cond_broadcast(void *ctx, int(*real)(void *c), void *c) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, c, sizeof(uptr)); + return real(c); +} + +static int cond_destroy(void *ctx, int(*real)(void *c), void *c) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, c, sizeof(uptr)); + return real(c); +} + +} // namespace __sanitizer + +#ifdef SI_LINUX_NOT_ANDROID + +// NPTL implementation of pthread_cond has 2 versions of pthread_cond_* +// functions (2.2.5 and 2.3.2). Intercept both. + +INTERCEPTOR(int, pthread_cond_init_232, void *c, void *a) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_init_232, c, a); + return cond_init(ctx, REAL(pthread_cond_init_232), c, a); +} + +INTERCEPTOR(int, pthread_cond_wait_232, void *c, void *m) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_wait_232, c, m); + return cond_wait(ctx, REAL(pthread_cond_wait_232), c, m); +} + +INTERCEPTOR(int, pthread_cond_timedwait_232, void *c, void *m, void *abstime) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_timedwait_232, c, m, abstime); + return cond_timedwait(ctx, REAL(pthread_cond_timedwait_232), c, m, abstime); +} + +INTERCEPTOR(int, pthread_cond_signal_232, void *c) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_signal_232, c); + return cond_signal(ctx, REAL(pthread_cond_signal_232), c); +} + +INTERCEPTOR(int, pthread_cond_broadcast_232, void *c) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_broadcast_232, c); + return cond_broadcast(ctx, REAL(pthread_cond_broadcast_232), c); +} + +INTERCEPTOR(int, pthread_cond_destroy_232, void *c) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_destroy_232, c); + return cond_destroy(ctx, REAL(pthread_cond_destroy_232), c); +} + +INTERCEPTOR(int, pthread_cond_init_225, void *c, void *a) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_init_225, c, a); + return cond_init(ctx, REAL(pthread_cond_init_225), c, a); +} + +INTERCEPTOR(int, pthread_cond_wait_225, void *c, void *m) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_wait_225, c, m); + return cond_wait(ctx, REAL(pthread_cond_wait_225), c, m); +} + +INTERCEPTOR(int, pthread_cond_timedwait_225, void *c, void *m, void *abstime) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_timedwait_225, c, m, abstime); + return cond_timedwait(ctx, REAL(pthread_cond_timedwait_225), c, m, abstime); +} + +INTERCEPTOR(int, pthread_cond_signal_225, void *c) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_signal_225, c); + return cond_signal(ctx, REAL(pthread_cond_signal_225), c); +} + +INTERCEPTOR(int, pthread_cond_broadcast_225, void *c) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_broadcast_225, c); + return cond_broadcast(ctx, REAL(pthread_cond_broadcast_225), c); +} + +INTERCEPTOR(int, pthread_cond_destroy_225, void *c) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_destroy_225, c); + return cond_destroy(ctx, REAL(pthread_cond_destroy_225), c); +} + +extern "C" int __interceptor_pthread_cond_init(void *c, void *a) + SANITIZER_INTERFACE_ATTRIBUTE + ALIAS("__interceptor_pthread_cond_init_232"); +extern "C" int __interceptor_pthread_cond_wait(void *c, void *m) + SANITIZER_INTERFACE_ATTRIBUTE + ALIAS("__interceptor_pthread_cond_wait_232"); +extern "C" int __interceptor_pthread_cond_timedwait(void *c, void *m, void *t) + SANITIZER_INTERFACE_ATTRIBUTE + ALIAS("__interceptor_pthread_cond_timedwait_232"); +extern "C" int __interceptor_pthread_cond_signal(void *c) + SANITIZER_INTERFACE_ATTRIBUTE + ALIAS("__interceptor_pthread_cond_signal_232"); +extern "C" int __interceptor_pthread_cond_broadcast(void *c) + SANITIZER_INTERFACE_ATTRIBUTE + ALIAS("__interceptor_pthread_cond_broadcast_232"); +extern "C" int __interceptor_pthread_cond_destroy(void *c) + SANITIZER_INTERFACE_ATTRIBUTE + ALIAS("__interceptor_pthread_cond_destroy_232"); + +#define INIT_PTHREAD_COND \ + INTERCEPT_FUNCTION_VER(pthread_cond_init, pthread_cond_init_232, \ + GLIBC_2.3.2, "@"); \ + INTERCEPT_FUNCTION_VER(pthread_cond_destroy, pthread_cond_destroy_232, \ + GLIBC_2.3.2, "@"); \ + INTERCEPT_FUNCTION_VER(pthread_cond_signal, pthread_cond_signal_232, \ + GLIBC_2.3.2, "@"); \ + INTERCEPT_FUNCTION_VER(pthread_cond_broadcast, pthread_cond_broadcast_232, \ + GLIBC_2.3.2, "@"); \ + INTERCEPT_FUNCTION_VER(pthread_cond_wait, pthread_cond_wait_232, \ + GLIBC_2.3.2, "@"); \ + INTERCEPT_FUNCTION_VER(pthread_cond_timedwait, pthread_cond_timedwait_232, \ + GLIBC_2.3.2, "@"); \ + INTERCEPT_FUNCTION_VER(pthread_cond_init, pthread_cond_init_225, \ + GLIBC_2.2.5, ""); \ + INTERCEPT_FUNCTION_VER(pthread_cond_destroy, pthread_cond_destroy_225, \ + GLIBC_2.2.5, ""); \ + INTERCEPT_FUNCTION_VER(pthread_cond_signal, pthread_cond_signal_225, \ + GLIBC_2.2.5, ""); \ + INTERCEPT_FUNCTION_VER(pthread_cond_broadcast, pthread_cond_broadcast_225, \ + GLIBC_2.2.5, ""); \ + INTERCEPT_FUNCTION_VER(pthread_cond_wait, pthread_cond_wait_225, \ + GLIBC_2.2.5, ""); \ + INTERCEPT_FUNCTION_VER(pthread_cond_timedwait, pthread_cond_timedwait_225, \ + GLIBC_2.2.5, "") + +#else // #ifdef SI_LINUX_NOT_ANDROID + +INTERCEPTOR(int, pthread_cond_init, void *c, void *a) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_init, c, a); + return cond_init(ctx, REAL(pthread_cond_init), c, a); +} + +INTERCEPTOR(int, pthread_cond_wait, void *c, void *m) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_wait, c, m); + return cond_wait(ctx, REAL(pthread_cond_wait), c, m); +} + +INTERCEPTOR(int, pthread_cond_timedwait, void *c, void *m, void *abstime) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_timedwait, c, m, abstime); + return cond_timedwait(ctx, REAL(pthread_cond_timedwait), c, m, abstime); +} + INTERCEPTOR(int, pthread_cond_signal, void *c) { - void *cond = init_cond(c); void *ctx; - COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_signal, cond); - COMMON_INTERCEPTOR_READ_RANGE(ctx, c, sizeof(uptr)); - return REAL(pthread_cond_signal)(cond); + COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_signal, c); + return cond_signal(ctx, REAL(pthread_cond_signal), c); } INTERCEPTOR(int, pthread_cond_broadcast, void *c) { - void *cond = init_cond(c); void *ctx; - COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_broadcast, cond); - COMMON_INTERCEPTOR_READ_RANGE(ctx, c, sizeof(uptr)); - return REAL(pthread_cond_broadcast)(cond); + COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_broadcast, c); + return cond_broadcast(ctx, REAL(pthread_cond_broadcast), c); } INTERCEPTOR(int, pthread_cond_destroy, void *c) { - void *cond = init_cond(c); void *ctx; - COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_destroy, cond); - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, c, sizeof(uptr)); - int res = REAL(pthread_cond_destroy)(cond); - if (common_flags()->legacy_pthread_cond) { - // Free our aux cond and zero the pointer to not leave dangling pointers. - WRAP(free)(cond); - atomic_store((atomic_uintptr_t*)c, 0, memory_order_relaxed); - } - return res; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_destroy, c); + return cond_broadcast(ctx, REAL(pthread_cond_destroy), c); } #define INIT_PTHREAD_COND \ - INTERCEPT_FUNCTION_VER(pthread_cond_init, "GLIBC_2.3.2"); \ - INTERCEPT_FUNCTION_VER(pthread_cond_signal, "GLIBC_2.3.2"); \ - INTERCEPT_FUNCTION_VER(pthread_cond_broadcast, "GLIBC_2.3.2"); \ - INTERCEPT_FUNCTION_VER(pthread_cond_wait, "GLIBC_2.3.2"); \ - INTERCEPT_FUNCTION_VER(pthread_cond_timedwait, "GLIBC_2.3.2"); \ - INTERCEPT_FUNCTION_VER(pthread_cond_destroy, "GLIBC_2.3.2") -#else + INTERCEPT_FUNCTION(pthread_cond_init); \ + INTERCEPT_FUNCTION(pthread_cond_signal); \ + INTERCEPT_FUNCTION(pthread_cond_broadcast); \ + INTERCEPT_FUNCTION(pthread_cond_wait); \ + INTERCEPT_FUNCTION(pthread_cond_timedwait); \ + INTERCEPT_FUNCTION(pthread_cond_destroy) + +#endif // #ifdef SI_LINUX_NOT_ANDROID +#else // #ifdef SANITIZER_INTERCEPT_PTHREAD_COND #define INIT_PTHREAD_COND #endif Index: lib/sanitizer_common/sanitizer_flags.h =================================================================== --- lib/sanitizer_common/sanitizer_flags.h +++ lib/sanitizer_common/sanitizer_flags.h @@ -49,7 +49,6 @@ bool detect_deadlocks; uptr clear_shadow_mmap_threshold; const char *color; - bool legacy_pthread_cond; bool intercept_tls_get_addr; bool help; }; Index: lib/sanitizer_common/sanitizer_flags.cc =================================================================== --- lib/sanitizer_common/sanitizer_flags.cc +++ lib/sanitizer_common/sanitizer_flags.cc @@ -52,7 +52,6 @@ f->detect_deadlocks = false; f->clear_shadow_mmap_threshold = 64 * 1024; f->color = "auto"; - f->legacy_pthread_cond = false; f->intercept_tls_get_addr = false; } @@ -114,8 +113,6 @@ "memset(). This is the threshold size in bytes."); ParseFlag(str, &f->color, "color", "Colorize reports: (always|never|auto)."); - ParseFlag(str, &f->legacy_pthread_cond, "legacy_pthread_cond", - "Enables support for dynamic libraries linked with libpthread 2.2.5."); ParseFlag(str, &f->intercept_tls_get_addr, "intercept_tls_get_addr", "Intercept __tls_get_addr."); ParseFlag(str, &f->help, "help", "Print the flag descriptions."); Index: lib/sanitizer_common/sanitizer_internal_defs.h =================================================================== --- lib/sanitizer_common/sanitizer_internal_defs.h +++ lib/sanitizer_common/sanitizer_internal_defs.h @@ -147,7 +147,11 @@ # define FORMAT(f, a) __attribute__((format(printf, f, a))) # define NOINLINE __attribute__((noinline)) # define NORETURN __attribute__((noreturn)) +#if defined(__PIC__) && !defined(__PIE__) +# define THREADLOCAL __thread __attribute__((tls_model("initial-exec"))) +#else # define THREADLOCAL __thread +#endif # define NOTHROW throw() # define LIKELY(x) __builtin_expect(!!(x), 1) # define UNLIKELY(x) __builtin_expect(!!(x), 0) Index: lib/tsan/CMakeLists.txt =================================================================== --- lib/tsan/CMakeLists.txt +++ lib/tsan/CMakeLists.txt @@ -90,9 +90,11 @@ $ CFLAGS ${TSAN_RTL_CFLAGS} DEFS ${TSAN_COMMON_DEFINITIONS}) + add_dependencies(tsan clang_rt.tsan-${arch}) add_sanitizer_rt_symbols(clang_rt.tsan-${arch} rtl/tsan.syms.extra) - add_dependencies(tsan clang_rt.tsan-${arch} - clang_rt.tsan-${arch}-symbols) + add_dependencies(tsan clang_rt.tsan-${arch}-symbols) + add_sanitizer_version_script(clang_rt.tsan-${arch} ../sanitizer_common/sanitizer.ver) + add_dependencies(tsan clang_rt.tsan-${arch}-version) endif() add_dependencies(compiler-rt tsan) Index: lib/tsan/Makefile.old =================================================================== --- lib/tsan/Makefile.old +++ lib/tsan/Makefile.old @@ -1,5 +1,5 @@ DEBUG=0 -LDFLAGS=-ldl -lpthread -pie +LDFLAGS=-ldl -lpthread -lrt -pie CXXFLAGS = -std=c++11 -fPIE -fno-rtti -g -Wall -Werror \ -DGTEST_HAS_RTTI=0 -DTSAN_DEBUG=$(DEBUG) -DSANITIZER_DEBUG=$(DEBUG) CLANG=clang @@ -70,8 +70,9 @@ $(MAKE) -f Makefile.old run DEBUG=1 -j 16 CC=$(CLANG) CXX=$(CLANG)++ # Release build with clang. $(MAKE) -f Makefile.old clean - $(MAKE) -f Makefile.old run DEBUG=0 -j 16 CC=$(CLANG) CXX=$(CLANG)++ + $(MAKE) -f Makefile.old run DEBUG=0 -j 16 CC=$(CLANG) CXX=$(CLANG)++ CFLAGS=-fPIC ./check_memcpy.sh + ./check_runtime_library.sh # Debug build with gcc $(MAKE) -f Makefile.old clean $(MAKE) -f Makefile.old run DEBUG=1 -j 16 CC=gcc CXX=g++ Index: lib/tsan/check_runtime_library.sh =================================================================== --- lib/tsan/check_runtime_library.sh +++ lib/tsan/check_runtime_library.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +# Test the case when tsan runtime is linked into a shared library. + +set -eu + +ROOTDIR=$(dirname $0) +TEST_DIR=$ROOTDIR/../../test/tsan +SRC=$TEST_DIR/runtime_in_shared_lib.cc +: ${CXX:=clang++} + +$CXX $SRC -fsanitize=thread -DLIB -fPIC -O1 -g -shared $ROOTDIR/rtl/libtsan.a \ + -Wl,--version-script=$ROOTDIR/../sanitizer_common/sanitizer.ver -lpthread -ldl -lrt \ + -o libruntime_in_shared_lib.so +$CXX $SRC -fPIE -pie -O1 -g -L. -lruntime_in_shared_lib -o runtime_in_shared_lib.exe +LD_LIBRARY_PATH=. ./runtime_in_shared_lib.exe + Index: lib/tsan/dd/dd_interceptors.cc =================================================================== --- lib/tsan/dd/dd_interceptors.cc +++ lib/tsan/dd/dd_interceptors.cc @@ -315,12 +315,12 @@ INTERCEPT_FUNCTION(pthread_rwlock_timedwrlock); INTERCEPT_FUNCTION(pthread_rwlock_unlock); - INTERCEPT_FUNCTION_VER(pthread_cond_init, "GLIBC_2.3.2"); - INTERCEPT_FUNCTION_VER(pthread_cond_signal, "GLIBC_2.3.2"); - INTERCEPT_FUNCTION_VER(pthread_cond_broadcast, "GLIBC_2.3.2"); - INTERCEPT_FUNCTION_VER(pthread_cond_wait, "GLIBC_2.3.2"); - INTERCEPT_FUNCTION_VER(pthread_cond_timedwait, "GLIBC_2.3.2"); - INTERCEPT_FUNCTION_VER(pthread_cond_destroy, "GLIBC_2.3.2"); + INTERCEPT_FUNCTION_VER_SINGLE(pthread_cond_init, "GLIBC_2.3.2"); + INTERCEPT_FUNCTION_VER_SINGLE(pthread_cond_signal, "GLIBC_2.3.2"); + INTERCEPT_FUNCTION_VER_SINGLE(pthread_cond_broadcast, "GLIBC_2.3.2"); + INTERCEPT_FUNCTION_VER_SINGLE(pthread_cond_wait, "GLIBC_2.3.2"); + INTERCEPT_FUNCTION_VER_SINGLE(pthread_cond_timedwait, "GLIBC_2.3.2"); + INTERCEPT_FUNCTION_VER_SINGLE(pthread_cond_destroy, "GLIBC_2.3.2"); // for symbolizer INTERCEPT_FUNCTION(realpath); Index: lib/tsan/tests/unit/tsan_flags_test.cc =================================================================== --- lib/tsan/tests/unit/tsan_flags_test.cc +++ lib/tsan/tests/unit/tsan_flags_test.cc @@ -75,7 +75,6 @@ " leak_check_at_exit=0" " allocator_may_return_null=0" " print_summary=0" - " legacy_pthread_cond=0" ""; static const char *options2 = @@ -119,7 +118,6 @@ " leak_check_at_exit=true" " allocator_may_return_null=true" " print_summary=true" - " legacy_pthread_cond=true" ""; void VerifyOptions1(Flags *f) { @@ -163,7 +161,6 @@ EXPECT_EQ(f->leak_check_at_exit, 0); EXPECT_EQ(f->allocator_may_return_null, 0); EXPECT_EQ(f->print_summary, 0); - EXPECT_EQ(f->legacy_pthread_cond, false); } void VerifyOptions2(Flags *f) { @@ -207,7 +204,6 @@ EXPECT_EQ(f->leak_check_at_exit, true); EXPECT_EQ(f->allocator_may_return_null, true); EXPECT_EQ(f->print_summary, true); - EXPECT_EQ(f->legacy_pthread_cond, true); } static const char *test_default_options; Index: test/tsan/runtime_in_shared_lib.cc =================================================================== --- test/tsan/runtime_in_shared_lib.cc +++ test/tsan/runtime_in_shared_lib.cc @@ -0,0 +1,29 @@ +// RUN: %clangxx_tsan -O1 %s -DLIB -fPIC -shared -o %T/libruntime_in_shared_lib.so +// RUN: %clangxx_tsan -O1 %s -L%T -lruntime_in_shared_lib -o %t +// RUN: LD_LIBRARY_PATH=%T${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH} %t 2>&1 | FileCheck %s + +// Test the case when tsan runtime is linked into a shared library. +// This functionality is actually tested in tsan make presubmit, +// because lit tests can't link tsan runtime into dynamic libraries. + +#ifndef LIB + +#include + +int foo_in_dynamic_lib(); + +int main(int argc, char **argv) { + foo_in_dynamic_lib(); + fprintf(stderr, "DONE\n"); +} + +#else // #ifdef LIB + +int foo_in_dynamic_lib() { + return 42; +} + +#endif // #ifdef LIB + +// CHECK: DONE + Index: test/tsan/test_output.sh =================================================================== --- test/tsan/test_output.sh +++ test/tsan/test_output.sh @@ -45,6 +45,10 @@ echo SKIPPING $c -- requires TSAN_OPTIONS continue fi + if [ "`grep "LD_LIBRARY_PATH" $c`" ]; then + echo SKIPPING $c -- requires LD_LIBRARY_PATH + continue + fi COMPILER=$CXX case $c in *.c) COMPILER=$CC