Index: CMakeLists.txt =================================================================== --- CMakeLists.txt +++ CMakeLists.txt @@ -231,6 +231,8 @@ # COMPILER_RT_DEBUG_PYBOOL is used by lit.common.configured.in. pythonize_bool(COMPILER_RT_DEBUG) +option(COMPILER_RT_BUILD_SHARED_ASAN "Build shared version of AddressSanitizer runtime" OFF) + #================================ # Setup Compiler Flags #================================ Index: cmake/Modules/AddCompilerRT.cmake =================================================================== --- cmake/Modules/AddCompilerRT.cmake +++ cmake/Modules/AddCompilerRT.cmake @@ -37,32 +37,58 @@ COMPILE_DEFINITIONS ${LIB_DEFS}) endmacro() -# Adds static runtime for a given architecture and puts it in the proper -# directory in the build and install trees. -# add_compiler_rt_static_runtime( -# SOURCES -# CFLAGS -# DEFS ) -macro(add_compiler_rt_static_runtime name arch) +# Helper for add_compiler_rt_{static,shared}_runtime. +# add_compiler_rt_runtime( +# SOURCES +# CFLAGS +# DEFS ) +macro(add_compiler_rt_runtime name arch type) if(CAN_TARGET_${arch}) parse_arguments(LIB "SOURCES;CFLAGS;DEFS" "" ${ARGN}) - add_library(${name} STATIC ${LIB_SOURCES}) + add_library(${name} ${type} ${LIB_SOURCES}) # Setup compile flags and definitions. set_target_compile_flags(${name} ${TARGET_${arch}_CFLAGS} ${LIB_CFLAGS}) + set_target_link_flags(${name} + ${TARGET_${arch}_CFLAGS} ${LIB_CFLAGS}) set_property(TARGET ${name} APPEND PROPERTY COMPILE_DEFINITIONS ${LIB_DEFS}) # Setup correct output directory in the build tree. set_target_properties(${name} PROPERTIES + LIBRARY_OUTPUT_DIRECTORY ${COMPILER_RT_LIBRARY_OUTPUT_DIR}) + set_target_properties(${name} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${COMPILER_RT_LIBRARY_OUTPUT_DIR}) - # Add installation command. - install(TARGETS ${name} - ARCHIVE DESTINATION ${COMPILER_RT_LIBRARY_INSTALL_DIR}) else() message(FATAL_ERROR "Archtecture ${arch} can't be targeted") endif() endmacro() +# Adds static runtime for a given architecture and puts it in the proper +# directory in the build and install trees. +# add_compiler_rt_static_runtime( +# SOURCES +# CFLAGS +# DEFS ) +macro(add_compiler_rt_static_runtime name arch) + add_compiler_rt_runtime(${name} ${arch} STATIC ${ARGN}) + # Add installation command. + install(TARGETS ${name} + ARCHIVE DESTINATION ${COMPILER_RT_LIBRARY_INSTALL_DIR}) +endmacro() + +# Adds shared runtime for a given architecture and puts it in the proper +# directory in the build and install trees. +# add_compiler_rt_shared_runtime( +# SOURCES +# CFLAGS +# DEFS ) +macro(add_compiler_rt_shared_runtime name arch) + add_compiler_rt_runtime(${name} ${arch} SHARED ${ARGN}) + # Add installation command. + install(TARGETS ${name} + LIBRARY DESTINATION ${COMPILER_RT_LIBRARY_INSTALL_DIR}) +endmacro() + # Same as add_compiler_rt_static_runtime, but creates a universal library # for several architectures. # add_compiler_rt_osx_static_runtime( ARCH Index: cmake/config-ix.cmake =================================================================== --- cmake/config-ix.cmake +++ cmake/config-ix.cmake @@ -15,6 +15,7 @@ check_cxx_compiler_flag(-ffreestanding COMPILER_RT_HAS_FFREESTANDING_FLAG) check_cxx_compiler_flag("-Werror -fno-function-sections" COMPILER_RT_HAS_FNO_FUNCTION_SECTIONS_FLAG) check_cxx_compiler_flag(-std=c++11 COMPILER_RT_HAS_STD_CXX11_FLAG) +check_cxx_compiler_flag(-ftls-model=initial-exec COMPILER_RT_HAS_FTLS_MODEL_INITIAL_EXEC) check_cxx_compiler_flag(/GR COMPILER_RT_HAS_GR_FLAG) check_cxx_compiler_flag(/GS COMPILER_RT_HAS_GS_FLAG) Index: lib/asan/CMakeLists.txt =================================================================== --- lib/asan/CMakeLists.txt +++ lib/asan/CMakeLists.txt @@ -21,7 +21,6 @@ asan_new_delete.cc asan_poisoning.cc asan_posix.cc - asan_preinit.cc asan_report.cc asan_rtl.cc asan_stack.cc @@ -29,6 +28,9 @@ asan_thread.cc asan_win.cc) +set(ASAN_PREINIT_SOURCES + asan_preinit.cc) + include_directories(..) if(ANDROID) @@ -46,6 +48,17 @@ ASAN_LOW_MEMORY=1) endif() +set(ASAN_DYNAMIC_DEFINITIONS + ${ASAN_COMMON_DEFINITIONS} ASAN_DYNAMIC=1) + +set(ASAN_DYNAMIC_CFLAGS ${ASAN_CFLAGS}) +append_if(COMPILER_RT_HAS_FTLS_MODEL_INITIAL_EXEC -ftls-model=initial-exec ASAN_DYNAMIC_CFLAGS) + +set(ASAN_DYNAMIC_LIBS stdc++) +append_if(COMPILER_RT_HAS_LIBPTHREAD pthread ASAN_DYNAMIC_LIBS) +append_if(COMPILER_RT_HAS_LIBPTHREAD dl ASAN_DYNAMIC_LIBS) +list(APPEND ASAN_DYNAMIC_LIBS m c) + if (NOT MSVC) set(ASAN_ASM_SOURCES asan_asm_instrumentation.S) set_source_files_properties(${ASAN_ASM_SOURCES} PROPERTIES LANGUAGE C) @@ -71,6 +84,14 @@ add_compiler_rt_object_library(RTAsan ${arch} SOURCES ${ASAN_SOURCES} CFLAGS ${ASAN_CFLAGS} DEFS ${ASAN_COMMON_DEFINITIONS}) + add_compiler_rt_object_library(RTAsan_preinit ${arch} + SOURCES ${ASAN_PREINIT_SOURCES} CFLAGS ${ASAN_CFLAGS} + DEFS ${ASAN_COMMON_DEFINITIONS}) + if (COMPILER_RT_BUILD_SHARED_ASAN) + add_compiler_rt_object_library(RTAsan_dynamic ${arch} + SOURCES ${ASAN_SOURCES} CFLAGS ${ASAN_DYNAMIC_CFLAGS} + DEFS ${ASAN_DYNAMIC_DEFINITIONS}) + endif() endforeach() endif() @@ -103,21 +124,36 @@ else() # Build separate libraries for each target. foreach(arch ${ASAN_SUPPORTED_ARCH}) - set(ASAN_RUNTIME_OBJECTS - $ + set(ASAN_COMMON_RUNTIME_OBJECTS $ $ $) if (NOT WIN32) # We can't build Leak Sanitizer on Windows yet. - list(APPEND ASAN_RUNTIME_OBJECTS $) + list(APPEND ASAN_COMMON_RUNTIME_OBJECTS $) endif() add_compiler_rt_static_runtime(clang_rt.asan-${arch} ${arch} - SOURCES ${ASAN_RUNTIME_OBJECTS} + SOURCES $ ${ASAN_COMMON_RUNTIME_OBJECTS} CFLAGS ${ASAN_CFLAGS} DEFS ${ASAN_COMMON_DEFINITIONS}) add_dependencies(asan clang_rt.asan-${arch}) + + add_compiler_rt_static_runtime(clang_rt.asan-preinit-${arch} ${arch} + SOURCES $ + CFLAGS ${ASAN_CFLAGS} + DEFS ${ASAN_COMMON_DEFINITIONS}) + add_dependencies(asan clang_rt.asan-preinit-${arch}) + + if (COMPILER_RT_BUILD_SHARED_ASAN) + add_compiler_rt_shared_runtime(clang_rt.asan-dynamic-${arch} ${arch} + SOURCES $ ${ASAN_COMMON_RUNTIME_OBJECTS} + CFLAGS ${ASAN_DYNAMIC_CFLAGS} + DEFS ${ASAN_DYNAMIC_DEFINITIONS}) + target_link_libraries(clang_rt.asan-dynamic-${arch} ${ASAN_DYNAMIC_LIBS}) + add_dependencies(asan clang_rt.asan-dynamic-${arch}) + endif() + if (UNIX AND NOT ${arch} STREQUAL "i386") add_sanitizer_rt_symbols(clang_rt.asan-${arch} asan.syms.extra) add_dependencies(asan clang_rt.asan-${arch}-symbols) Index: lib/asan/asan_internal.h =================================================================== --- lib/asan/asan_internal.h +++ lib/asan/asan_internal.h @@ -49,6 +49,10 @@ # define ASAN_USE_PREINIT_ARRAY (SANITIZER_LINUX && !SANITIZER_ANDROID) #endif +#ifndef ASAN_DYNAMIC +# define ASAN_DYNAMIC 0 +#endif + // All internal functions in asan reside inside the __asan namespace // to avoid namespace collisions with the user programs. // Seperate namespace also makes it simpler to distinguish the asan run-time @@ -70,6 +74,12 @@ // asan_linux.cc / asan_mac.cc / asan_win.cc void *AsanDoesNotSupportStaticLinkage(); +// asan_linux.cc / asan_mac.cc / asan_win.cc +void AsanCheckDynamicRTPrereqs(); + +// asan_linux.cc / asan_mac.cc / asan_win.cc +void AsanCheckIncompatibleRT(); + void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp); void AsanOnSIGSEGV(int, void *siginfo, void *context); Index: lib/asan/asan_linux.cc =================================================================== --- lib/asan/asan_linux.cc +++ lib/asan/asan_linux.cc @@ -35,8 +35,11 @@ #if SANITIZER_ANDROID #include +extern "C" void* _DYNAMIC; #else #include +#include +#include #endif // x86_64 FreeBSD 9.2 and older define 64-bit register names in both 64-bit @@ -50,8 +53,6 @@ # endif #endif -extern "C" void* _DYNAMIC; - namespace __asan { void MaybeReexec() { @@ -63,6 +64,52 @@ return &_DYNAMIC; // defined in link.h } +static int DsoCallback(struct dl_phdr_info *info, size_t size, + void *data) { + // Continue until the first dynamic library is found + if (!info->dlpi_name || info->dlpi_name[0] == 0) + return 0; + + *(const char **)data = info->dlpi_name; + return 1; +} + +void AsanCheckDynamicRTPrereqs() { + // FIXME: can we do something like this for Android? +#if !SANITIZER_ANDROID + // Asan dynamic RT should be the first dynamic library in the list + const char *first_library_name = 0; + dl_iterate_phdr(DsoCallback, &first_library_name); + if (first_library_name + && !internal_strstr(first_library_name, "libclang_rt.asan") + && !internal_strstr(first_library_name, "libasan.so")) { + Report( + "ASan runtime does not come first in initial library list; " + "you should either link to your application or " + "manually preload it with LD_PRELOAD.\n"); + Die(); + } + +#endif +} + +void AsanCheckIncompatibleRT() { +#if !SANITIZER_ANDROID + const char *badsym; +#if ASAN_DYNAMIC + badsym = "__asan_static"; +#else + badsym = "__asan_dynamic"; +#endif + if (dlsym(RTLD_DEFAULT, badsym)) { + Report("Your application is linked against " + "incompatible ASan runtimes.\n"); + Die(); + } + dlerror(); dlerror(); // Force glibc to release dlerror buffer +#endif +} + void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { #if defined(__arm__) ucontext_t *ucontext = (ucontext_t*)context; Index: lib/asan/asan_mac.cc =================================================================== --- lib/asan/asan_mac.cc +++ lib/asan/asan_mac.cc @@ -199,6 +199,14 @@ return 0; } +// Likewise. +void AsanCheckDynamicRTPrereqs() { +} + +// Likewise. +void AsanCheckIncompatibleRT() { +} + bool AsanInterceptsSignal(int signum) { return (signum == SIGSEGV || signum == SIGBUS) && common_flags()->handle_segv; Index: lib/asan/asan_malloc_linux.cc =================================================================== --- lib/asan/asan_malloc_linux.cc +++ lib/asan/asan_malloc_linux.cc @@ -61,38 +61,60 @@ // ---------------------- Replacement functions ---------------- {{{1 using namespace __asan; // NOLINT +static const uptr kCallocPoolSizeLog = 12; +static const uptr kCallocPoolSize = 1 << kCallocPoolSizeLog; +static u8 calloc_memory_for_dlsym[kCallocPoolSize] ALIGNED(kCallocPoolSize); + +// Hack: dlsym calls calloc +// before REAL(calloc) is retrieved from dlsym. +// Same for dlerror calling asprintf calling malloc. +static void *AsanPreinitAlloc(uptr size) { + static uptr allocated; + uptr size_aligned = (size + kWordSize - 1) & ~(kWordSize - 1); + void *mem = (void*)&calloc_memory_for_dlsym[allocated]; + allocated += size_aligned; + CHECK(allocated < kCallocPoolSize); + return mem; +} + +static inline bool AsanIsPreinitAllocated(void *p) { + return ((uptr)p >> kCallocPoolSizeLog) + == ((uptr)calloc_memory_for_dlsym >> kCallocPoolSizeLog); +} + INTERCEPTOR(void, free, void *ptr) { + if (AsanIsPreinitAllocated(ptr)) + return; GET_STACK_TRACE_FREE; asan_free(ptr, &stack, FROM_MALLOC); } INTERCEPTOR(void, cfree, void *ptr) { + if (AsanIsPreinitAllocated(ptr)) + return; GET_STACK_TRACE_FREE; asan_free(ptr, &stack, FROM_MALLOC); } INTERCEPTOR(void*, malloc, uptr size) { + if (!asan_inited) + return AsanPreinitAlloc(size); GET_STACK_TRACE_MALLOC; return asan_malloc(size, &stack); } INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) { - if (!asan_inited) { - // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym. - const uptr kCallocPoolSize = 1024; - static uptr calloc_memory_for_dlsym[kCallocPoolSize]; - static uptr allocated; - uptr size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize; - void *mem = (void*)&calloc_memory_for_dlsym[allocated]; - allocated += size_in_words; - CHECK(allocated < kCallocPoolSize); - return mem; - } + if (!asan_inited) + return AsanPreinitAlloc(size * nmemb); GET_STACK_TRACE_MALLOC; return asan_calloc(nmemb, size, &stack); } INTERCEPTOR(void*, realloc, void *ptr, uptr size) { + if (!asan_inited) + return AsanPreinitAlloc(size); + if (AsanIsPreinitAllocated(ptr)) + ptr = 0; GET_STACK_TRACE_MALLOC; return asan_realloc(ptr, size, &stack); } Index: lib/asan/asan_rtl.cc =================================================================== --- lib/asan/asan_rtl.cc +++ lib/asan/asan_rtl.cc @@ -636,6 +636,24 @@ AsanInitInternal(); } +#if ASAN_DYNAMIC +// Initialize runtime in case it's LD_PRELOAD-ed into unsanitized executable +// (and thus normal initializer from .preinit_array haven't run). + +class AsanInitializer { +public: // NOLINT + AsanInitializer() { + AsanCheckIncompatibleRT(); + if (!asan_inited) { + AsanCheckDynamicRTPrereqs(); + __asan_init(); + } + } +}; + +static AsanInitializer asan_initializer; +#endif + } // namespace __asan // ---------------------- Interface ---------------- {{{1 @@ -687,6 +705,17 @@ // Initialize as requested from instrumented application code. // We use this call as a trigger to wake up ASan from deactivated state. void __asan_init() { + AsanCheckIncompatibleRT(); AsanActivate(); AsanInitInternal(); } + +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE +#if ASAN_DYNAMIC +int __asan_dynamic; +#else +int __asan_static; +#endif +} + Index: lib/asan/asan_win.cc =================================================================== --- lib/asan/asan_win.cc +++ lib/asan/asan_win.cc @@ -70,6 +70,13 @@ return 0; } +void AsanCheckDynamicRTPrereqs() { + UNIMPLEMENTED(); +} + +void AsanCheckIncompatibleRT() { +} + void AsanPlatformThreadInit() { // Nothing here for now. } Index: lib/tsan/CMakeLists.txt =================================================================== --- lib/tsan/CMakeLists.txt +++ lib/tsan/CMakeLists.txt @@ -83,7 +83,7 @@ set_source_files_properties(${TSAN_ASM_SOURCES} PROPERTIES LANGUAGE C) set(arch "x86_64") - add_compiler_rt_static_runtime(clang_rt.tsan-${arch} ${arch} + add_compiler_rt_runtime(clang_rt.tsan-${arch} ${arch} STATIC SOURCES ${TSAN_SOURCES} ${TSAN_ASM_SOURCES} $ $ Index: lib/tsan/dd/CMakeLists.txt =================================================================== --- lib/tsan/dd/CMakeLists.txt +++ lib/tsan/dd/CMakeLists.txt @@ -26,7 +26,7 @@ # Deadlock detector is currently supported on 64-bit Linux only. if(CAN_TARGET_x86_64 AND UNIX AND NOT APPLE AND NOT ANDROID) set(arch "x86_64") - add_compiler_rt_static_runtime(clang_rt.dd-${arch} ${arch} + add_compiler_rt_runtime(clang_rt.dd-${arch} ${arch} STATIC SOURCES ${DD_SOURCES} $ $ Index: test/asan/CMakeLists.txt =================================================================== --- test/asan/CMakeLists.txt +++ test/asan/CMakeLists.txt @@ -21,29 +21,53 @@ set(ASAN_TEST_CONFIG_SUFFIX "64") set(ASAN_TEST_BITS "64") set(ASAN_TEST_TARGET_CFLAGS ${TARGET_64_BIT_CFLAGS}) + set(ASAN_TEST_DYNAMIC False) configure_lit_site_cfg( ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in ${CMAKE_CURRENT_BINARY_DIR}/64bitConfig/lit.site.cfg ) list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/64bitConfig) + if(COMPILER_RT_BUILD_SHARED_ASAN) + set(ASAN_TEST_DYNAMIC True) + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/64bitConfig-dynamic/lit.site.cfg) + list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/64bitConfig-dynamic) + endif() endif() if(CAN_TARGET_i386) set(ASAN_TEST_CONFIG_SUFFIX "32") set(ASAN_TEST_BITS "32") set(ASAN_TEST_TARGET_CFLAGS ${TARGET_32_BIT_CFLAGS}) + set(ASAN_TEST_DYNAMIC False) + set(ASAN_RT_SUFFIX "i386") configure_lit_site_cfg( ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig/lit.site.cfg ) list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig) + if(COMPILER_RT_BUILD_SHARED_ASAN) + set(ASAN_TEST_DYNAMIC True) + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig-dynamic/lit.site.cfg) + list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/32bitConfig-dynamic) + endif() + unset(ASAN_RT_SUFFIX) endif() if(COMPILER_RT_INCLUDE_TESTS) -configure_lit_site_cfg( - ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in - ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg - ) + set(ASAN_TEST_DYNAMIC False) + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/Unit/lit.site.cfg) + if(COMPILER_RT_BUILD_SHARED_ASAN) + set(ASAN_TEST_DYNAMIC True) + configure_lit_site_cfg( + ${CMAKE_CURRENT_SOURCE_DIR}/Unit/lit.site.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/Unit-dynamic/lit.site.cfg) + endif() endif() set(ASAN_TEST_DEPS ${SANITIZER_COMMON_LIT_TEST_DEPS}) @@ -55,6 +79,9 @@ if(COMPILER_RT_INCLUDE_TESTS AND NOT CAN_TARGET_arm_android) list(APPEND ASAN_TEST_DEPS AsanUnitTests) list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/Unit) + if(COMPILER_RT_BUILD_SHARED_ASAN) + list(APPEND ASAN_TESTSUITES ${CMAKE_CURRENT_BINARY_DIR}/Unit-dynamic) + endif() endif() add_lit_testsuite(check-asan "Running the AddressSanitizer tests" ${ASAN_TESTSUITES} Index: test/asan/TestCases/Linux/DynamicRT/asan_dlopen_test.cc =================================================================== --- test/asan/TestCases/Linux/DynamicRT/asan_dlopen_test.cc +++ test/asan/TestCases/Linux/DynamicRT/asan_dlopen_test.cc @@ -0,0 +1,14 @@ +// Test that dlopen of dynamic runtime is prohibited. +// +// RUN: %clangxx %s -DRT=\"%shared_libasan\" -o %t -ldl +// RUN: not %t 2>&1 | FileCheck %s + +#include + +int main(int argc, char **argv) { + dlopen(RT, RTLD_LAZY); + return 0; +} + +// CHECK: ASan runtime does not come first in initial library list + Index: test/asan/TestCases/Linux/DynamicRT/asan_preload_test-1.cc =================================================================== --- test/asan/TestCases/Linux/DynamicRT/asan_preload_test-1.cc +++ test/asan/TestCases/Linux/DynamicRT/asan_preload_test-1.cc @@ -0,0 +1,25 @@ +// Test that non-sanitized executables work with sanitized shared libs +// and preloaded runtime. +// +// RUN: %clangxx -DBUILD_SO=1 -fPIC -shared %s -o %t.so +// RUN: %clangxx %s %t.so -o %t +// +// RUN: %clangxx_asan -DBUILD_SO=1 -fPIC -shared %s -o %t.so +// RUN: LD_PRELOAD=%shared_libasan not %t 2>&1 | FileCheck %s + +#if BUILD_SO +char dummy; +void do_access(const void *p) { + // CHECK: AddressSanitizer: heap-buffer-overflow + dummy = ((const char *)p)[1]; +} +#else +#include +extern void do_access(const void *p); +int main(int argc, char **argv) { + void *p = malloc(1); + do_access(p); + free(p); + return 0; +} +#endif Index: test/asan/TestCases/Linux/DynamicRT/asan_preload_test-2.cc =================================================================== --- test/asan/TestCases/Linux/DynamicRT/asan_preload_test-2.cc +++ test/asan/TestCases/Linux/DynamicRT/asan_preload_test-2.cc @@ -0,0 +1,20 @@ +// Test that preloaded runtime works with unsanitized executables. +// +// RUN: %clangxx %s -o %t +// RUN: LD_PRELOAD=%shared_libasan not %t 2>&1 | FileCheck %s + +#include + +extern "C" void *memset(void *p, int val, size_t n); + +void do_access(void *p) { + // CHECK: AddressSanitizer: heap-buffer-overflow + memset(p, 0, 2); +} + +int main(int argc, char **argv) { + void *p = malloc(1); + do_access(p); + return 0; +} + Index: test/asan/TestCases/Linux/DynamicRT/asan_rt_confict_test-1.cc =================================================================== --- test/asan/TestCases/Linux/DynamicRT/asan_rt_confict_test-1.cc +++ test/asan/TestCases/Linux/DynamicRT/asan_rt_confict_test-1.cc @@ -0,0 +1,12 @@ +// Test that preloading dynamic runtime to statically sanitized +// executable is prohibited. +// +// RUN: %clangxx_asan_static %s -o %t +// RUN: LD_PRELOAD=%shared_libasan not %t 2>&1 | FileCheck %s + +#include +int main(int argc, char **argv) { + return 0; +} + +// CHECK: Your application is linked against incompatible ASan runtimes Index: test/asan/TestCases/Linux/DynamicRT/asan_rt_confict_test-2.cc =================================================================== --- test/asan/TestCases/Linux/DynamicRT/asan_rt_confict_test-2.cc +++ test/asan/TestCases/Linux/DynamicRT/asan_rt_confict_test-2.cc @@ -0,0 +1,24 @@ +// Test that mixed static/dynamic sanitization of program objects +// is prohibited. +// +// RUN: %clangxx_asan -DBUILD_SO=1 -fPIC -shared %s -o %t.so +// RUN: %clangxx_asan_static %s %t.so -o %t +// RUN: not %t 2>&1 | FileCheck %s + +#if BUILD_SO +char dummy; +void do_access(const void *p) { + dummy = ((const char *)p)[1]; +} +#else +#include +extern void do_access(const void *p); +int main(int argc, char **argv) { + void *p = malloc(1); + do_access(p); + free(p); + return 0; +} +#endif + +// CHECK: Your application is linked against incompatible ASan runtimes Index: test/asan/TestCases/Linux/DynamicRT/lit.local.cfg =================================================================== --- test/asan/TestCases/Linux/DynamicRT/lit.local.cfg +++ test/asan/TestCases/Linux/DynamicRT/lit.local.cfg @@ -0,0 +1,3 @@ +if not config.asan_dynamic: + config.unsupported = True + Index: test/asan/TestCases/Linux/StaticRT/interface_symbols_linux.c =================================================================== --- test/asan/TestCases/Linux/StaticRT/interface_symbols_linux.c +++ test/asan/TestCases/Linux/StaticRT/interface_symbols_linux.c @@ -0,0 +1,36 @@ +// Check the presense of interface symbols in compiled file. + +// RUN: %clang_asan -O2 %s -o %t.exe +// RUN: nm -D %t.exe | grep " T " | sed "s/.* T //" \ +// RUN: | grep "__asan_" | sed "s/___asan_/__asan_/" \ +// RUN: | grep -v "__asan_malloc_hook" \ +// RUN: | grep -v "__asan_free_hook" \ +// RUN: | grep -v "__asan_default_options" \ +// RUN: | grep -v "__asan_stack_" \ +// RUN: | grep -v "__asan_on_error" > %t.symbols +// RUN: cat %p/../../../../../lib/asan/asan_interface_internal.h \ +// RUN: | sed "s/\/\/.*//" | sed "s/typedef.*//" \ +// RUN: | grep -v "OPTIONAL" \ +// RUN: | grep "__asan_.*(" | sed "s/.* __asan_/__asan_/;s/(.*//" \ +// RUN: > %t.interface +// RUN: echo __asan_report_load1 >> %t.interface +// RUN: echo __asan_report_load2 >> %t.interface +// RUN: echo __asan_report_load4 >> %t.interface +// RUN: echo __asan_report_load8 >> %t.interface +// RUN: echo __asan_report_load16 >> %t.interface +// RUN: echo __asan_report_store1 >> %t.interface +// RUN: echo __asan_report_store2 >> %t.interface +// RUN: echo __asan_report_store4 >> %t.interface +// RUN: echo __asan_report_store8 >> %t.interface +// RUN: echo __asan_report_store16 >> %t.interface +// RUN: echo __asan_report_load_n >> %t.interface +// RUN: echo __asan_report_store_n >> %t.interface +// RUN: echo __asan_get_current_fake_stack >> %t.interface +// RUN: echo __asan_addr_is_in_fake_stack >> %t.interface +// RUN: cat %t.interface | sort -u | diff %t.symbols - + +// FIXME: nm -D on powerpc somewhy shows ASan interface symbols residing +// in "initialized data section". +// REQUIRES: x86_64-supported-target,i386-supported-target + +int main() { return 0; } Index: test/asan/TestCases/Linux/StaticRT/lit.local.cfg =================================================================== --- test/asan/TestCases/Linux/StaticRT/lit.local.cfg +++ test/asan/TestCases/Linux/StaticRT/lit.local.cfg @@ -0,0 +1,3 @@ +if config.asan_dynamic: + config.unsupported = True + Index: test/asan/TestCases/Linux/interception_malloc_test.cc =================================================================== --- test/asan/TestCases/Linux/interception_malloc_test.cc +++ test/asan/TestCases/Linux/interception_malloc_test.cc @@ -9,7 +9,9 @@ #include extern "C" void *__interceptor_malloc(size_t size); -extern "C" void *malloc(size_t size) { +extern "C" +__attribute__((no_sanitize_address)) // Malloc may be called from dlsym in __asan_init +void *malloc(size_t size) { write(2, "malloc call\n", sizeof("malloc call\n") - 1); return __interceptor_malloc(size); } Index: test/asan/TestCases/Linux/interface_symbols_linux.c =================================================================== --- test/asan/TestCases/Linux/interface_symbols_linux.c +++ test/asan/TestCases/Linux/interface_symbols_linux.c @@ -1,36 +0,0 @@ -// Check the presense of interface symbols in compiled file. - -// RUN: %clang_asan -O2 %s -o %t.exe -// RUN: nm -D %t.exe | grep " T " | sed "s/.* T //" \ -// RUN: | grep "__asan_" | sed "s/___asan_/__asan_/" \ -// RUN: | grep -v "__asan_malloc_hook" \ -// RUN: | grep -v "__asan_free_hook" \ -// RUN: | grep -v "__asan_default_options" \ -// RUN: | grep -v "__asan_stack_" \ -// RUN: | grep -v "__asan_on_error" > %t.symbols -// RUN: cat %p/../../../../lib/asan/asan_interface_internal.h \ -// RUN: | sed "s/\/\/.*//" | sed "s/typedef.*//" \ -// RUN: | grep -v "OPTIONAL" \ -// RUN: | grep "__asan_.*(" | sed "s/.* __asan_/__asan_/;s/(.*//" \ -// RUN: > %t.interface -// RUN: echo __asan_report_load1 >> %t.interface -// RUN: echo __asan_report_load2 >> %t.interface -// RUN: echo __asan_report_load4 >> %t.interface -// RUN: echo __asan_report_load8 >> %t.interface -// RUN: echo __asan_report_load16 >> %t.interface -// RUN: echo __asan_report_store1 >> %t.interface -// RUN: echo __asan_report_store2 >> %t.interface -// RUN: echo __asan_report_store4 >> %t.interface -// RUN: echo __asan_report_store8 >> %t.interface -// RUN: echo __asan_report_store16 >> %t.interface -// RUN: echo __asan_report_load_n >> %t.interface -// RUN: echo __asan_report_store_n >> %t.interface -// RUN: echo __asan_get_current_fake_stack >> %t.interface -// RUN: echo __asan_addr_is_in_fake_stack >> %t.interface -// RUN: cat %t.interface | sort -u | diff %t.symbols - - -// FIXME: nm -D on powerpc somewhy shows ASan interface symbols residing -// in "initialized data section". -// REQUIRES: x86_64-supported-target,i386-supported-target - -int main() { return 0; } Index: test/asan/TestCases/sanity_check_pure_c.c =================================================================== --- test/asan/TestCases/sanity_check_pure_c.c +++ test/asan/TestCases/sanity_check_pure_c.c @@ -3,7 +3,7 @@ // RUN: not %t 2>&1 | FileCheck %s // Sanity checking a test in pure C with -pie. -// RUN: %clang_asan -O2 %s -pie -o %t +// RUN: %clang_asan -O2 %s -pie -fPIE -o %t // RUN: not %t 2>&1 | FileCheck %s #include Index: test/asan/Unit/lit.site.cfg.in =================================================================== --- test/asan/Unit/lit.site.cfg.in +++ test/asan/Unit/lit.site.cfg.in @@ -12,6 +12,7 @@ # FIXME: De-hardcode this path. config.test_exec_root = "@COMPILER_RT_BINARY_DIR@/lib/asan/tests" config.test_source_root = config.test_exec_root +config.asan_dynamic = @ASAN_TEST_DYNAMIC@ # Enable leak detection in ASan unit tests on x86_64-linux. if config.host_os == 'Linux' and config.host_arch == 'x86_64': Index: test/asan/lit.cfg =================================================================== --- test/asan/lit.cfg +++ test/asan/lit.cfg @@ -11,6 +11,11 @@ "to lit.site.cfg " % attr_name) return attr_value +def push_ld_library_path(config, new_path): + new_ld_library_path = os.path.pathsep.join( + (new_path, config.environment['LD_LIBRARY_PATH'])) + config.environment['LD_LIBRARY_PATH'] = new_ld_library_path + # Setup config name. config.name = 'AddressSanitizer' + config.name_suffix @@ -34,6 +39,12 @@ "-g"] + target_cflags clang_asan_cxxflags = config.cxx_mode_flags + clang_asan_cflags +if config.asan_dynamic: + clang_asan_static_cflags = list(clang_asan_cflags) + clang_asan_cflags.append('-shared-libasan') + clang_asan_static_cxxflags = list(clang_asan_cxxflags) + clang_asan_cxxflags.append('-shared-libasan') + asan_lit_source_dir = get_required_attr(config, "asan_lit_source_dir") if config.android == "TRUE": config.available_features.add('android') @@ -45,10 +56,18 @@ def build_invocation(compile_flags): return " " + " ".join([clang_wrapper, config.clang] + compile_flags) + " " +asan_rt_suffix = config.asan_rt_suffix +if not asan_rt_suffix: + asan_rt_suffix = config.host_arch + config.substitutions.append( ("%clang ", build_invocation(target_cflags)) ) config.substitutions.append( ("%clangxx ", build_invocation(target_cxxflags)) ) config.substitutions.append( ("%clang_asan ", build_invocation(clang_asan_cflags)) ) config.substitutions.append( ("%clangxx_asan ", build_invocation(clang_asan_cxxflags)) ) +config.substitutions.append( ("%shared_libasan", "libclang_rt.asan-dynamic-%s.so" % asan_rt_suffix)) +if config.asan_dynamic: + config.substitutions.append( ("%clang_asan_static ", build_invocation(clang_asan_static_cflags)) ) + config.substitutions.append( ("%clangxx_asan_static ", build_invocation(clang_asan_static_cxxflags)) ) # FIXME: De-hardcode this path. asan_source_dir = os.path.join( @@ -69,14 +88,16 @@ if config.host_os == 'Linux' and config.bits == '64': config.environment['ASAN_OPTIONS'] = 'detect_leaks=1' -# GCC-ASan uses dynamic runtime by default, so we have to set LD_LIBRARY_PATH -# to pick it up properly. +# Set LD_LIBRARY_PATH to pick dynamic runtime up properly. +if config.asan_dynamic: + libasan_dir = config.compiler_rt_libdir + push_ld_library_path(config, libasan_dir) + +# GCC-ASan uses dynamic runtime by default. if config.compiler_id == 'GNU': gcc_dir = os.path.dirname(config.clang) libasan_dir = os.path.join(gcc_dir, "..", "lib" + config.bits) - new_ld_library_path = os.path.pathsep.join( - (libasan_dir, config.environment['LD_LIBRARY_PATH'])) - config.environment['LD_LIBRARY_PATH'] = new_ld_library_path + push_ld_library_path(config, libasan_dir) # Default test suffixes. config.suffixes = ['.c', '.cc', '.cpp'] Index: test/asan/lit.site.cfg.in =================================================================== --- test/asan/lit.site.cfg.in +++ test/asan/lit.site.cfg.in @@ -9,6 +9,8 @@ config.llvm_tools_dir = "@ASAN_TEST_LLVM_TOOLS_DIR@" config.bits = "@ASAN_TEST_BITS@" config.android = "@CAN_TARGET_arm_android@" +config.asan_dynamic = @ASAN_TEST_DYNAMIC@ +config.asan_rt_suffix = "@ASAN_RT_SUFFIX@" # Load common config for all compiler-rt lit tests. lit_config.load_config(config, "@COMPILER_RT_BINARY_DIR@/test/lit.common.configured") Index: test/lit.common.configured.in =================================================================== --- test/lit.common.configured.in +++ test/lit.common.configured.in @@ -20,6 +20,7 @@ set_default("compiler_rt_arch", "@COMPILER_RT_SUPPORTED_ARCH@") set_default("python_executable", "@PYTHON_EXECUTABLE@") set_default("compiler_rt_debug", @COMPILER_RT_DEBUG_PYBOOL@) +set_default("compiler_rt_libdir", "@COMPILER_RT_LIBRARY_OUTPUT_DIR@") # LLVM tools dir can be passed in lit parameters, so try to # apply substitution.