Index: CMakeLists.txt =================================================================== --- CMakeLists.txt +++ CMakeLists.txt @@ -230,6 +230,8 @@ # COMPILER_RT_DEBUG_PYBOOL is used by lit.common.configured.in. pythonize_bool(COMPILER_RT_DEBUG) +option(COMPILER_RT_INCLUDE_ASAN_SHARED "Build shared version of AddressSanitizer runtime" OFF) + #================================ # Setup Compiler Flags #================================ Index: cmake/Modules/AddCompilerRT.cmake =================================================================== --- cmake/Modules/AddCompilerRT.cmake +++ cmake/Modules/AddCompilerRT.cmake @@ -37,6 +37,32 @@ COMPILE_DEFINITIONS ${LIB_DEFS}) 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) + if(CAN_TARGET_${arch}) + parse_arguments(LIB "SOURCES;CFLAGS;DEFS" "" ${ARGN}) + add_library(${name} SHARED ${LIB_SOURCES}) + # Setup compile flags and definitions. + set_target_compile_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}) + # Add installation command. + install(TARGETS ${name} + LIBRARY 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( 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) @@ -69,8 +71,18 @@ else() foreach(arch ${ASAN_SUPPORTED_ARCH}) add_compiler_rt_object_library(RTAsan ${arch} - SOURCES ${ASAN_SOURCES} CFLAGS ${ASAN_CFLAGS} + SOURCES ${ASAN_SOURCES} ${ASAN_PREINIT_SOURCES} CFLAGS ${ASAN_CFLAGS} DEFS ${ASAN_COMMON_DEFINITIONS}) + if (COMPILER_RT_INCLUDE_ASAN_DYNAMIC) + set(ASAN_DYNAMIC_DEFINITIONS + ${ASAN_COMMON_DEFINITIONS} ASAN_DYNAMIC) + add_compiler_rt_object_library(RTAsan_preinit ${arch} + SOURCES ${ASAN_PREINIT_SOURCES} CFLAGS ${ASAN_CFLAGS} + DEFS ${ASAN_COMMON_DEFINITIONS}) + add_compiler_rt_object_library(RTAsan_dynamic ${arch} + SOURCES ${ASAN_SOURCES} CFLAGS ${ASAN_CFLAGS} + DEFS ${ASAN_DYNAMIC_DEFINITIONS}) + endif() endforeach() endif() @@ -108,24 +120,53 @@ 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() + set(ASAN_RUNTIME_OBJECTS + $ + ${ASAN_COMMON_RUNTIME_OBJECTS}) + add_compiler_rt_static_runtime(clang_rt.asan-${arch} ${arch} SOURCES ${ASAN_RUNTIME_OBJECTS} CFLAGS ${ASAN_CFLAGS} DEFS ${ASAN_COMMON_DEFINITIONS}) - add_dependencies(asan clang_rt.asan-${arch}) + add_dependencies(asan clang_rt.asan-${arch} clang_rt.asan-${arch}) + + if (COMPILER_RT_INCLUDE_ASAN_DYNAMIC) + set(ASAN_DYNAMIC_RUNTIME_OBJECTS + $ + ${ASAN_COMMON_RUNTIME_OBJECTS}) + add_compiler_rt_shared_runtime(clang_rt.asan-${arch}-dynamic ${arch} + SOURCES ${ASAN_DYNAMIC_RUNTIME_OBJECTS} + CFLAGS ${ASAN_CFLAGS} + DEFS ${ASAN_DYNAMIC_DEFINITIONS}) + target_link_libraries(clang_rt.asan-${arch}-dynamic pthread stdc++ dl m c) + add_dependencies(asan clang_rt.asan-${arch}-dynamic) + + set(ASAN_PREINIT_RUNTIME_OBJECTS + $) + add_compiler_rt_static_runtime(clang_rt.asan-${arch}-preinit ${arch} + SOURCES ${ASAN_PREINIT_RUNTIME_OBJECTS} + CFLAGS ${ASAN_CFLAGS} + DEFS ${ASAN_DYNAMIC_DEFINITIONS}) + add_dependencies(asan clang_rt.asan-${arch}-preinit) + 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) + + if (COMPILER_RT_INCLUDE_ASAN_DYNAMIC) + add_sanitizer_rt_symbols(clang_rt.asan-${arch}-dynamic asan.syms.extra) + add_dependencies(asan clang_rt.asan-${arch}-dynamic-symbols) + endif() endif() if (WIN32) Index: lib/asan/asan_rtl.cc =================================================================== --- lib/asan/asan_rtl.cc +++ lib/asan/asan_rtl.cc @@ -553,6 +553,21 @@ AsanInitInternal(); } +#ifdef 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() { + if (!asan_inited) + __asan_init(); + } +}; + +static AsanInitializer asan_initializer; +#endif + } // namespace __asan // ---------------------- Interface ---------------- {{{1 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,12 @@ # define FORMAT(f, a) __attribute__((format(printf, f, a))) # define NOINLINE __attribute__((noinline)) # define NORETURN __attribute__((noreturn)) -# define THREADLOCAL __thread +# ifdef ASAN_DYNAMIC +# define THREADLOCAL __thread __attribute__((tls_model("initial-exec"))) +# else +// FIXME: mark as local-exec? +# define THREADLOCAL __thread +# endif # define NOTHROW throw() # define LIKELY(x) __builtin_expect(!!(x), 1) # define UNLIKELY(x) __builtin_expect(!!(x), 0) Index: test/asan/CMakeLists.txt =================================================================== --- test/asan/CMakeLists.txt +++ test/asan/CMakeLists.txt @@ -21,29 +21,51 @@ 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_INCLUDE_ASAN_DYNAMIC) + 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) 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_INCLUDE_ASAN_DYNAMIC) + 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() 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_INCLUDE_ASAN_DYNAMIC) + 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 +77,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_INCLUDE_ASAN_DYNAMIC) + 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/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 @@ -32,6 +37,8 @@ "-fno-omit-frame-pointer", "-fno-optimize-sibling-calls", "-g"] + target_cflags +if config.asan_dynamic: + clang_asan_cflags.append('-shared-libasan') clang_asan_cxxflags = config.cxx_mode_flags + clang_asan_cflags asan_lit_source_dir = get_required_attr(config, "asan_lit_source_dir") @@ -49,6 +56,7 @@ 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-%s-dynamic.so" % config.host_arch)) # FIXME: De-hardcode this path. asan_source_dir = os.path.join( @@ -69,14 +77,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,7 @@ 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@ # 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.