Index: compiler-rt/trunk/lib/asan/CMakeLists.txt =================================================================== --- compiler-rt/trunk/lib/asan/CMakeLists.txt +++ compiler-rt/trunk/lib/asan/CMakeLists.txt @@ -205,14 +205,22 @@ endif() if (WIN32) + add_compiler_rt_object_libraries(AsanDllThunk + ${SANITIZER_COMMON_SUPPORTED_OS} + ARCHS ${ASAN_SUPPORTED_ARCH} + SOURCES asan_globals_win.cc + asan_win_dll_thunk.cc + CFLAGS ${ASAN_CFLAGS} -DSANITIZER_DLL_THUNK + DEFS ${ASAN_COMMON_DEFINITIONS}) + add_compiler_rt_runtime(clang_rt.asan_dll_thunk STATIC ARCHS ${arch} - SOURCES asan_win_dll_thunk.cc - asan_globals_win.cc - $ - CFLAGS ${ASAN_CFLAGS} -DASAN_DLL_THUNK - DEFS ${ASAN_COMMON_DEFINITIONS} + OBJECT_LIBS AsanDllThunk + UbsanDllThunk + SancovDllThunk + SanitizerCommonDllThunk + SOURCES $ PARENT_TARGET asan) set(DYNAMIC_RUNTIME_THUNK_CFLAGS "-DASAN_DYNAMIC_RUNTIME_THUNK") Index: compiler-rt/trunk/lib/asan/asan_globals_win.cc =================================================================== --- compiler-rt/trunk/lib/asan/asan_globals_win.cc +++ compiler-rt/trunk/lib/asan/asan_globals_win.cc @@ -29,7 +29,7 @@ __asan_global *end = &__asan_globals_end; uptr bytediff = (uptr)end - (uptr)start; if (bytediff % sizeof(__asan_global) != 0) { -#ifdef ASAN_DLL_THUNK +#ifdef SANITIZER_DLL_THUNK __debugbreak(); #else CHECK("corrupt asan global array"); Index: compiler-rt/trunk/lib/asan/asan_win_dll_thunk.cc =================================================================== --- compiler-rt/trunk/lib/asan/asan_win_dll_thunk.cc +++ compiler-rt/trunk/lib/asan/asan_win_dll_thunk.cc @@ -15,384 +15,41 @@ // See https://github.com/google/sanitizers/issues/209 for the details. //===----------------------------------------------------------------------===// -// Only compile this code when building asan_dll_thunk.lib -// Using #ifdef rather than relying on Makefiles etc. -// simplifies the build procedure. -#ifdef ASAN_DLL_THUNK +#ifdef SANITIZER_DLL_THUNK #include "asan_init_version.h" #include "interception/interception.h" -#include "sanitizer_common/sanitizer_platform_interceptors.h" #include "sanitizer_common/sanitizer_win_defs.h" +#include "sanitizer_common/sanitizer_win_dll_thunk.h" +#include "sanitizer_common/sanitizer_platform_interceptors.h" -#ifdef _M_IX86 -#define WINAPI __stdcall -#else -#define WINAPI -#endif - -// ---------- Function interception helper functions and macros ----------- {{{1 -extern "C" { -void *WINAPI GetModuleHandleA(const char *module_name); -void *WINAPI GetProcAddress(void *module, const char *proc_name); -void abort(); -} - -using namespace __sanitizer; - -static uptr getRealProcAddressOrDie(const char *name) { - uptr ret = - __interception::InternalGetProcAddress((void *)GetModuleHandleA(0), name); - if (!ret) - abort(); - return ret; -} - -// We need to intercept some functions (e.g. ASan interface, memory allocator -- -// let's call them "hooks") exported by the DLL thunk and forward the hooks to -// the runtime in the main module. -// However, we don't want to keep two lists of these hooks. -// To avoid that, the list of hooks should be defined using the -// INTERCEPT_WHEN_POSSIBLE macro. Then, all these hooks can be intercepted -// at once by calling INTERCEPT_HOOKS(). - -// Use macro+template magic to automatically generate the list of hooks. -// Each hook at line LINE defines a template class with a static -// FunctionInterceptor::Execute() method intercepting the hook. -// The default implementation of FunctionInterceptor is to call -// the Execute() method corresponding to the previous line. -template -struct FunctionInterceptor { - static void Execute() { FunctionInterceptor::Execute(); } -}; - -// There shouldn't be any hooks with negative definition line number. -template<> -struct FunctionInterceptor<0> { - static void Execute() {} -}; - -#define INTERCEPT_WHEN_POSSIBLE(main_function, dll_function) \ - template <> struct FunctionInterceptor<__LINE__> { \ - static void Execute() { \ - uptr wrapper = getRealProcAddressOrDie(main_function); \ - if (!__interception::OverrideFunction((uptr)dll_function, wrapper, 0)) \ - abort(); \ - FunctionInterceptor<__LINE__ - 1>::Execute(); \ - } \ - }; - -// Special case of hooks -- ASan own interface functions. Those are only called -// after __asan_init, thus an empty implementation is sufficient. -#define INTERFACE_FUNCTION(name) \ - extern "C" __declspec(noinline) void name() { \ - volatile int prevent_icf = (__LINE__ << 8); (void)prevent_icf; \ - __debugbreak(); \ - } \ - INTERCEPT_WHEN_POSSIBLE(#name, name) - -// INTERCEPT_HOOKS must be used after the last INTERCEPT_WHEN_POSSIBLE. -#define INTERCEPT_HOOKS FunctionInterceptor<__LINE__>::Execute - -// We can't define our own version of strlen etc. because that would lead to -// link-time or even type mismatch errors. Instead, we can declare a function -// just to be able to get its address. Me may miss the first few calls to the -// functions since it can be called before __asan_init, but that would lead to -// false negatives in the startup code before user's global initializers, which -// isn't a big deal. -#define INTERCEPT_LIBRARY_FUNCTION(name) \ - extern "C" void name(); \ - INTERCEPT_WHEN_POSSIBLE(WRAPPER_NAME(name), name) - -// Disable compiler warnings that show up if we declare our own version -// of a compiler intrinsic (e.g. strlen). -#pragma warning(disable: 4391) -#pragma warning(disable: 4392) - -static void InterceptHooks(); -// }}} - -// ---------- Function wrapping helpers ----------------------------------- {{{1 -#define WRAP_V_V(name) \ - extern "C" void name() { \ - typedef decltype(name) *fntype; \ - static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ - fn(); \ - } \ - INTERCEPT_WHEN_POSSIBLE(#name, name); - -#define WRAP_V_W(name) \ - extern "C" void name(void *arg) { \ - typedef decltype(name) *fntype; \ - static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ - fn(arg); \ - } \ - INTERCEPT_WHEN_POSSIBLE(#name, name); - -#define WRAP_V_WW(name) \ - extern "C" void name(void *arg1, void *arg2) { \ - typedef decltype(name) *fntype; \ - static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ - fn(arg1, arg2); \ - } \ - INTERCEPT_WHEN_POSSIBLE(#name, name); - -#define WRAP_V_WWW(name) \ - extern "C" void name(void *arg1, void *arg2, void *arg3) { \ - typedef decltype(name) *fntype; \ - static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ - fn(arg1, arg2, arg3); \ - } \ - INTERCEPT_WHEN_POSSIBLE(#name, name); - -#define WRAP_W_V(name) \ - extern "C" void *name() { \ - typedef decltype(name) *fntype; \ - static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ - return fn(); \ - } \ - INTERCEPT_WHEN_POSSIBLE(#name, name); - -#define WRAP_W_W(name) \ - extern "C" void *name(void *arg) { \ - typedef decltype(name) *fntype; \ - static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ - return fn(arg); \ - } \ - INTERCEPT_WHEN_POSSIBLE(#name, name); - -#define WRAP_W_WW(name) \ - extern "C" void *name(void *arg1, void *arg2) { \ - typedef decltype(name) *fntype; \ - static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ - return fn(arg1, arg2); \ - } \ - INTERCEPT_WHEN_POSSIBLE(#name, name); - -#define WRAP_W_WWW(name) \ - extern "C" void *name(void *arg1, void *arg2, void *arg3) { \ - typedef decltype(name) *fntype; \ - static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ - return fn(arg1, arg2, arg3); \ - } \ - INTERCEPT_WHEN_POSSIBLE(#name, name); - -#define WRAP_W_WWWW(name) \ - extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4) { \ - typedef decltype(name) *fntype; \ - static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ - return fn(arg1, arg2, arg3, arg4); \ - } \ - INTERCEPT_WHEN_POSSIBLE(#name, name); - -#define WRAP_W_WWWWW(name) \ - extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4, \ - void *arg5) { \ - typedef decltype(name) *fntype; \ - static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ - return fn(arg1, arg2, arg3, arg4, arg5); \ - } \ - INTERCEPT_WHEN_POSSIBLE(#name, name); - -#define WRAP_W_WWWWWW(name) \ - extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4, \ - void *arg5, void *arg6) { \ - typedef decltype(name) *fntype; \ - static fntype fn = (fntype)getRealProcAddressOrDie(#name); \ - return fn(arg1, arg2, arg3, arg4, arg5, arg6); \ - } \ - INTERCEPT_WHEN_POSSIBLE(#name, name); -// }}} - -// ----------------- ASan own interface functions -------------------- -// Don't use the INTERFACE_FUNCTION machinery for this function as we actually -// want to call it in the __asan_init interceptor. -WRAP_W_V(__asan_should_detect_stack_use_after_return) -WRAP_W_V(__asan_get_shadow_memory_dynamic_address) - -extern "C" { - int __asan_option_detect_stack_use_after_return; - uptr __asan_shadow_memory_dynamic_address; - - // Manually wrap __asan_init as we need to initialize - // __asan_option_detect_stack_use_after_return afterwards. - void __asan_init() { - typedef void (*fntype)(); - static fntype fn = 0; - // __asan_init is expected to be called by only one thread. - if (fn) return; - - fn = (fntype)getRealProcAddressOrDie("__asan_init"); - fn(); - __asan_option_detect_stack_use_after_return = - (__asan_should_detect_stack_use_after_return() != 0); - __asan_shadow_memory_dynamic_address = - (uptr)__asan_get_shadow_memory_dynamic_address(); - InterceptHooks(); - } -} - -extern "C" void __asan_version_mismatch_check() { - // Do nothing. -} - -INTERFACE_FUNCTION(__asan_handle_no_return) -INTERFACE_FUNCTION(__asan_unhandled_exception_filter) - -INTERFACE_FUNCTION(__asan_report_store1) -INTERFACE_FUNCTION(__asan_report_store2) -INTERFACE_FUNCTION(__asan_report_store4) -INTERFACE_FUNCTION(__asan_report_store8) -INTERFACE_FUNCTION(__asan_report_store16) -INTERFACE_FUNCTION(__asan_report_store_n) - -INTERFACE_FUNCTION(__asan_report_load1) -INTERFACE_FUNCTION(__asan_report_load2) -INTERFACE_FUNCTION(__asan_report_load4) -INTERFACE_FUNCTION(__asan_report_load8) -INTERFACE_FUNCTION(__asan_report_load16) -INTERFACE_FUNCTION(__asan_report_load_n) - -INTERFACE_FUNCTION(__asan_store1) -INTERFACE_FUNCTION(__asan_store2) -INTERFACE_FUNCTION(__asan_store4) -INTERFACE_FUNCTION(__asan_store8) -INTERFACE_FUNCTION(__asan_store16) -INTERFACE_FUNCTION(__asan_storeN) - -INTERFACE_FUNCTION(__asan_load1) -INTERFACE_FUNCTION(__asan_load2) -INTERFACE_FUNCTION(__asan_load4) -INTERFACE_FUNCTION(__asan_load8) -INTERFACE_FUNCTION(__asan_load16) -INTERFACE_FUNCTION(__asan_loadN) - -INTERFACE_FUNCTION(__asan_memcpy); -INTERFACE_FUNCTION(__asan_memset); -INTERFACE_FUNCTION(__asan_memmove); - -INTERFACE_FUNCTION(__asan_set_shadow_00); -INTERFACE_FUNCTION(__asan_set_shadow_f1); -INTERFACE_FUNCTION(__asan_set_shadow_f2); -INTERFACE_FUNCTION(__asan_set_shadow_f3); -INTERFACE_FUNCTION(__asan_set_shadow_f5); -INTERFACE_FUNCTION(__asan_set_shadow_f8); - -INTERFACE_FUNCTION(__asan_alloca_poison); -INTERFACE_FUNCTION(__asan_allocas_unpoison); - -INTERFACE_FUNCTION(__asan_register_globals) -INTERFACE_FUNCTION(__asan_unregister_globals) - -INTERFACE_FUNCTION(__asan_before_dynamic_init) -INTERFACE_FUNCTION(__asan_after_dynamic_init) - -INTERFACE_FUNCTION(__asan_poison_stack_memory) -INTERFACE_FUNCTION(__asan_unpoison_stack_memory) - -INTERFACE_FUNCTION(__asan_poison_memory_region) -INTERFACE_FUNCTION(__asan_unpoison_memory_region) - -INTERFACE_FUNCTION(__asan_address_is_poisoned) -INTERFACE_FUNCTION(__asan_region_is_poisoned) - -INTERFACE_FUNCTION(__asan_get_current_fake_stack) -INTERFACE_FUNCTION(__asan_addr_is_in_fake_stack) - -INTERFACE_FUNCTION(__asan_stack_malloc_0) -INTERFACE_FUNCTION(__asan_stack_malloc_1) -INTERFACE_FUNCTION(__asan_stack_malloc_2) -INTERFACE_FUNCTION(__asan_stack_malloc_3) -INTERFACE_FUNCTION(__asan_stack_malloc_4) -INTERFACE_FUNCTION(__asan_stack_malloc_5) -INTERFACE_FUNCTION(__asan_stack_malloc_6) -INTERFACE_FUNCTION(__asan_stack_malloc_7) -INTERFACE_FUNCTION(__asan_stack_malloc_8) -INTERFACE_FUNCTION(__asan_stack_malloc_9) -INTERFACE_FUNCTION(__asan_stack_malloc_10) - -INTERFACE_FUNCTION(__asan_stack_free_0) -INTERFACE_FUNCTION(__asan_stack_free_1) -INTERFACE_FUNCTION(__asan_stack_free_2) -INTERFACE_FUNCTION(__asan_stack_free_4) -INTERFACE_FUNCTION(__asan_stack_free_5) -INTERFACE_FUNCTION(__asan_stack_free_6) -INTERFACE_FUNCTION(__asan_stack_free_7) -INTERFACE_FUNCTION(__asan_stack_free_8) -INTERFACE_FUNCTION(__asan_stack_free_9) -INTERFACE_FUNCTION(__asan_stack_free_10) - -// FIXME: we might want to have a sanitizer_win_dll_thunk? -INTERFACE_FUNCTION(__sanitizer_annotate_contiguous_container) -INTERFACE_FUNCTION(__sanitizer_contiguous_container_find_bad_address) -INTERFACE_FUNCTION(__sanitizer_cov) -INTERFACE_FUNCTION(__sanitizer_cov_dump) -INTERFACE_FUNCTION(__sanitizer_dump_coverage) -INTERFACE_FUNCTION(__sanitizer_dump_trace_pc_guard_coverage) -INTERFACE_FUNCTION(__sanitizer_cov_indir_call16) -INTERFACE_FUNCTION(__sanitizer_cov_init) -INTERFACE_FUNCTION(__sanitizer_cov_module_init) -INTERFACE_FUNCTION(__sanitizer_cov_trace_basic_block) -INTERFACE_FUNCTION(__sanitizer_cov_trace_func_enter) -INTERFACE_FUNCTION(__sanitizer_cov_with_check) -INTERFACE_FUNCTION(__sanitizer_get_allocated_size) -INTERFACE_FUNCTION(__sanitizer_get_coverage_guards) -INTERFACE_FUNCTION(__sanitizer_get_current_allocated_bytes) -INTERFACE_FUNCTION(__sanitizer_get_estimated_allocated_size) -INTERFACE_FUNCTION(__sanitizer_get_free_bytes) -INTERFACE_FUNCTION(__sanitizer_get_heap_size) -INTERFACE_FUNCTION(__sanitizer_get_ownership) -INTERFACE_FUNCTION(__sanitizer_get_total_unique_caller_callee_pairs) -INTERFACE_FUNCTION(__sanitizer_get_total_unique_coverage) -INTERFACE_FUNCTION(__sanitizer_get_unmapped_bytes) -INTERFACE_FUNCTION(__sanitizer_maybe_open_cov_file) -INTERFACE_FUNCTION(__sanitizer_print_stack_trace) -INTERFACE_FUNCTION(__sanitizer_symbolize_pc) -INTERFACE_FUNCTION(__sanitizer_symbolize_global) -INTERFACE_FUNCTION(__sanitizer_ptr_cmp) -INTERFACE_FUNCTION(__sanitizer_ptr_sub) -INTERFACE_FUNCTION(__sanitizer_reset_coverage) -INTERFACE_FUNCTION(__sanitizer_get_number_of_counters) -INTERFACE_FUNCTION(__sanitizer_update_counter_bitset_and_clear_counters) -INTERFACE_FUNCTION(__sanitizer_set_death_callback) -INTERFACE_FUNCTION(__sanitizer_set_report_path) -INTERFACE_FUNCTION(__sanitizer_set_report_fd) -INTERFACE_FUNCTION(__sanitizer_unaligned_load16) -INTERFACE_FUNCTION(__sanitizer_unaligned_load32) -INTERFACE_FUNCTION(__sanitizer_unaligned_load64) -INTERFACE_FUNCTION(__sanitizer_unaligned_store16) -INTERFACE_FUNCTION(__sanitizer_unaligned_store32) -INTERFACE_FUNCTION(__sanitizer_unaligned_store64) -INTERFACE_FUNCTION(__sanitizer_verify_contiguous_container) -INTERFACE_FUNCTION(__sanitizer_install_malloc_and_free_hooks) -INTERFACE_FUNCTION(__sanitizer_start_switch_fiber) -INTERFACE_FUNCTION(__sanitizer_finish_switch_fiber) -INTERFACE_FUNCTION(__sanitizer_get_module_and_offset_for_pc) - -// TODO(timurrrr): Add more interface functions on the as-needed basis. - -// ----------------- Memory allocation functions --------------------- -WRAP_V_W(free) -WRAP_V_W(_free_base) -WRAP_V_WW(_free_dbg) - -WRAP_W_W(malloc) -WRAP_W_W(_malloc_base) -WRAP_W_WWWW(_malloc_dbg) - -WRAP_W_WW(calloc) -WRAP_W_WW(_calloc_base) -WRAP_W_WWWWW(_calloc_dbg) -WRAP_W_WWW(_calloc_impl) - -WRAP_W_WW(realloc) -WRAP_W_WW(_realloc_base) -WRAP_W_WWW(_realloc_dbg) -WRAP_W_WWW(_recalloc) -WRAP_W_WWW(_recalloc_base) - -WRAP_W_W(_msize) -WRAP_W_W(_expand) -WRAP_W_W(_expand_dbg) +// ASan own interface functions. +#define INTERFACE_FUNCTION(Name) INTERCEPT_SANITIZER_FUNCTION(Name) +#define INTERFACE_WEAK_FUNCTION(Name) +#include "asan_interface.inc" + +// Memory allocation functions. +INTERCEPT_WRAP_V_W(free) +INTERCEPT_WRAP_V_W(_free_base) +INTERCEPT_WRAP_V_WW(_free_dbg) + +INTERCEPT_WRAP_W_W(malloc) +INTERCEPT_WRAP_W_W(_malloc_base) +INTERCEPT_WRAP_W_WWWW(_malloc_dbg) + +INTERCEPT_WRAP_W_WW(calloc) +INTERCEPT_WRAP_W_WW(_calloc_base) +INTERCEPT_WRAP_W_WWWWW(_calloc_dbg) +INTERCEPT_WRAP_W_WWW(_calloc_impl) + +INTERCEPT_WRAP_W_WW(realloc) +INTERCEPT_WRAP_W_WW(_realloc_base) +INTERCEPT_WRAP_W_WWW(_realloc_dbg) +INTERCEPT_WRAP_W_WWW(_recalloc) +INTERCEPT_WRAP_W_WWW(_recalloc_base) + +INTERCEPT_WRAP_W_W(_msize) +INTERCEPT_WRAP_W_W(_expand) +INTERCEPT_WRAP_W_W(_expand_dbg) // TODO(timurrrr): Might want to add support for _aligned_* allocation // functions to detect a bit more bugs. Those functions seem to wrap malloc(). @@ -401,20 +58,6 @@ INTERCEPT_LIBRARY_FUNCTION(atoi); INTERCEPT_LIBRARY_FUNCTION(atol); - -#ifdef _WIN64 -INTERCEPT_LIBRARY_FUNCTION(__C_specific_handler); -#else -INTERCEPT_LIBRARY_FUNCTION(_except_handler3); - -// _except_handler4 checks -GS cookie which is different for each module, so we -// can't use INTERCEPT_LIBRARY_FUNCTION(_except_handler4). -INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) { - __asan_handle_no_return(); - return REAL(_except_handler4)(a, b, c, d); -} -#endif - INTERCEPT_LIBRARY_FUNCTION(frexp); INTERCEPT_LIBRARY_FUNCTION(longjmp); #if SANITIZER_INTERCEPT_MEMCHR @@ -442,32 +85,59 @@ INTERCEPT_LIBRARY_FUNCTION(strtol); INTERCEPT_LIBRARY_FUNCTION(wcslen); -// Must be after all the interceptor declarations due to the way INTERCEPT_HOOKS -// is defined. -void InterceptHooks() { - INTERCEPT_HOOKS(); +#ifdef _WIN64 +INTERCEPT_LIBRARY_FUNCTION(__C_specific_handler); +#else +INTERCEPT_LIBRARY_FUNCTION(_except_handler3); +// _except_handler4 checks -GS cookie which is different for each module, so we +// can't use INTERCEPT_LIBRARY_FUNCTION(_except_handler4). +INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) { + __asan_handle_no_return(); + return REAL(_except_handler4)(a, b, c, d); +} +#endif + +// Window specific functions not included in asan_interface.inc. +INTERCEPT_WRAP_W_V(__asan_should_detect_stack_use_after_return) +INTERCEPT_WRAP_W_V(__asan_get_shadow_memory_dynamic_address) + +using namespace __sanitizer; + +extern "C" { +int __asan_option_detect_stack_use_after_return; +uptr __asan_shadow_memory_dynamic_address; +} // extern "C" + +static int asan_dll_thunk_init() { + typedef void (*fntype)(); + static fntype fn = 0; + // asan_dll_thunk_init is expected to be called by only one thread. + if (fn) return 0; + + // Ensure all interception was executed. + __dll_thunk_init(); + + fn = (fntype) dllThunkGetRealAddrOrDie("__asan_init"); + fn(); + __asan_option_detect_stack_use_after_return = + (__asan_should_detect_stack_use_after_return() != 0); + __asan_shadow_memory_dynamic_address = + (uptr)__asan_get_shadow_memory_dynamic_address(); + #ifndef _WIN64 INTERCEPT_FUNCTION(_except_handler4); #endif -} - -// We want to call __asan_init before C/C++ initializers/constructors are -// executed, otherwise functions like memset might be invoked. -// For some strange reason, merely linking in asan_preinit.cc doesn't work -// as the callback is never called... Is link.exe doing something too smart? - -// In DLLs, the callbacks are expected to return 0, -// otherwise CRT initialization fails. -static int call_asan_init() { - __asan_init(); + // In DLLs, the callbacks are expected to return 0, + // otherwise CRT initialization fails. return 0; } + #pragma section(".CRT$XIB", long, read) // NOLINT -__declspec(allocate(".CRT$XIB")) int (*__asan_preinit)() = call_asan_init; +__declspec(allocate(".CRT$XIB")) int (*__asan_preinit)() = asan_dll_thunk_init; static void WINAPI asan_thread_init(void *mod, unsigned long reason, - void *reserved) { - if (reason == /*DLL_PROCESS_ATTACH=*/1) __asan_init(); + void *reserved) { + if (reason == /*DLL_PROCESS_ATTACH=*/1) asan_dll_thunk_init(); } #pragma section(".CRT$XLAB", long, read) // NOLINT @@ -476,4 +146,4 @@ WIN_FORCE_LINK(__asan_dso_reg_hook) -#endif // ASAN_DLL_THUNK +#endif // SANITIZER_DLL_THUNK Index: compiler-rt/trunk/lib/sanitizer_common/CMakeLists.txt =================================================================== --- compiler-rt/trunk/lib/sanitizer_common/CMakeLists.txt +++ compiler-rt/trunk/lib/sanitizer_common/CMakeLists.txt @@ -128,6 +128,8 @@ sanitizer_syscall_linux_aarch64.inc sanitizer_thread_registry.h) +include_directories(..) + set(SANITIZER_COMMON_DEFINITIONS) include(CheckIncludeFile) @@ -184,6 +186,21 @@ CFLAGS ${SANITIZER_CFLAGS} DEFS ${SANITIZER_COMMON_DEFINITIONS}) +if(MSVC) + add_compiler_rt_object_libraries(SanitizerCommonDllThunk + ${SANITIZER_COMMON_SUPPORTED_OS} + ARCHS ${SANITIZER_COMMON_SUPPORTED_ARCH} + SOURCES sanitizer_win_dll_thunk.cc + CFLAGS ${SANITIZER_CFLAGS} -DSANITIZER_DLL_THUNK + DEFS ${SANITIZER_COMMON_DEFINITIONS}) + add_compiler_rt_object_libraries(SancovDllThunk + ${SANITIZER_COMMON_SUPPORTED_OS} + ARCHS ${SANITIZER_COMMON_SUPPORTED_ARCH} + SOURCES sanitizer_coverage_win_dll_thunk.cc + CFLAGS ${SANITIZER_CFLAGS} -DSANITIZER_DLL_THUNK + DEFS ${SANITIZER_COMMON_DEFINITIONS}) +endif() + # Unit tests for common sanitizer runtime. if(COMPILER_RT_INCLUDE_TESTS) add_subdirectory(tests) Index: compiler-rt/trunk/lib/sanitizer_common/sanitizer_coverage_win_dll_thunk.cc =================================================================== --- compiler-rt/trunk/lib/sanitizer_common/sanitizer_coverage_win_dll_thunk.cc +++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_coverage_win_dll_thunk.cc @@ -0,0 +1,21 @@ +//===-- sanitizer_coverage_win_dll_thunk.cc -------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a family of thunks that should be statically linked into +// the DLLs that have instrumentation in order to delegate the calls to the +// shared runtime that lives in the main binary. +// See https://github.com/google/sanitizers/issues/209 for the details. +//===----------------------------------------------------------------------===// +#ifdef SANITIZER_DLL_THUNK +#include "sanitizer_win_dll_thunk.h" +// Sanitizer Coverage interface functions. +#define INTERFACE_FUNCTION(Name) INTERCEPT_SANITIZER_FUNCTION(Name) +#define INTERFACE_WEAK_FUNCTION(Name) +#include "sanitizer_coverage_interface.inc" +#endif // SANITIZER_DLL_THUNK Index: compiler-rt/trunk/lib/sanitizer_common/sanitizer_win_defs.h =================================================================== --- compiler-rt/trunk/lib/sanitizer_common/sanitizer_win_defs.h +++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_win_defs.h @@ -16,6 +16,12 @@ #include "sanitizer_platform.h" #if SANITIZER_WINDOWS +#ifdef _M_IX86 +#define WINAPI __stdcall +#else +#define WINAPI +#endif + #if defined(_WIN64) #define WIN_SYM_PREFIX #else Index: compiler-rt/trunk/lib/sanitizer_common/sanitizer_win_dll_thunk.h =================================================================== --- compiler-rt/trunk/lib/sanitizer_common/sanitizer_win_dll_thunk.h +++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_win_dll_thunk.h @@ -0,0 +1,149 @@ +//===-- sanitizer_win_dll_thunk.h -----------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// This header provide helper macros to delegate calls to the shared runtime +// that lives in the main executable. It should be included to dll_thunks that +// will be linked to the dlls, when the sanitizer is a static library included +// in the main executable. +//===----------------------------------------------------------------------===// +#ifndef SANITIZER_WIN_DLL_THUNK_H +#define SANITIZER_WIN_DLL_THUNK_H +#include "sanitizer_internal_defs.h" + +namespace __sanitizer { +uptr dllThunkGetRealAddrOrDie(const char *name); + +int dllThunkIntercept(const char* main_function, uptr dll_function); +} + +extern "C" int __dll_thunk_init(); + +// ----------------- Function interception helper macros -------------------- // +// Override dll_function with main_function from main executable. +#define INTERCEPT_OR_DIE(main_function, dll_function) \ + static int intercept_##dll_function() { \ + return __sanitizer::dllThunkIntercept(main_function, (__sanitizer::uptr) \ + dll_function); \ + } \ + __pragma(section(".DLLTH$M", long, read)) \ + __declspec(allocate(".DLLTH$M")) int (*__dll_thunk_##dll_function)() = \ + intercept_##dll_function; + +// -------------------- Function interception macros ------------------------ // +// Special case of hooks -- ASan own interface functions. Those are only called +// after __asan_init, thus an empty implementation is sufficient. +#define INTERCEPT_SANITIZER_FUNCTION(name) \ + extern "C" __declspec(noinline) void name() { \ + volatile int prevent_icf = (__LINE__ << 8); (void)prevent_icf; \ + __debugbreak(); \ + } \ + INTERCEPT_OR_DIE(#name, name) + +// We can't define our own version of strlen etc. because that would lead to +// link-time or even type mismatch errors. Instead, we can declare a function +// just to be able to get its address. Me may miss the first few calls to the +// functions since it can be called before __dll_thunk_init, but that would lead +// to false negatives in the startup code before user's global initializers, +// which isn't a big deal. +#define INTERCEPT_LIBRARY_FUNCTION(name) \ + extern "C" void name(); \ + INTERCEPT_OR_DIE(WRAPPER_NAME(name), name) + +// Use these macros for functions that could be called before __dll_thunk_init() +// is executed and don't lead to errors if defined (free, malloc, etc). +#define INTERCEPT_WRAP_V_V(name) \ + extern "C" void name() { \ + typedef decltype(name) *fntype; \ + static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \ + fn(); \ + } \ + INTERCEPT_OR_DIE(#name, name); + +#define INTERCEPT_WRAP_V_W(name) \ + extern "C" void name(void *arg) { \ + typedef decltype(name) *fntype; \ + static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \ + fn(arg); \ + } \ + INTERCEPT_OR_DIE(#name, name); + +#define INTERCEPT_WRAP_V_WW(name) \ + extern "C" void name(void *arg1, void *arg2) { \ + typedef decltype(name) *fntype; \ + static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \ + fn(arg1, arg2); \ + } \ + INTERCEPT_OR_DIE(#name, name); + +#define INTERCEPT_WRAP_V_WWW(name) \ + extern "C" void name(void *arg1, void *arg2, void *arg3) { \ + typedef decltype(name) *fntype; \ + static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \ + fn(arg1, arg2, arg3); \ + } \ + INTERCEPT_OR_DIE(#name, name); + +#define INTERCEPT_WRAP_W_V(name) \ + extern "C" void *name() { \ + typedef decltype(name) *fntype; \ + static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \ + return fn(); \ + } \ + INTERCEPT_OR_DIE(#name, name); + +#define INTERCEPT_WRAP_W_W(name) \ + extern "C" void *name(void *arg) { \ + typedef decltype(name) *fntype; \ + static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \ + return fn(arg); \ + } \ + INTERCEPT_OR_DIE(#name, name); + +#define INTERCEPT_WRAP_W_WW(name) \ + extern "C" void *name(void *arg1, void *arg2) { \ + typedef decltype(name) *fntype; \ + static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \ + return fn(arg1, arg2); \ + } \ + INTERCEPT_OR_DIE(#name, name); + +#define INTERCEPT_WRAP_W_WWW(name) \ + extern "C" void *name(void *arg1, void *arg2, void *arg3) { \ + typedef decltype(name) *fntype; \ + static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \ + return fn(arg1, arg2, arg3); \ + } \ + INTERCEPT_OR_DIE(#name, name); + +#define INTERCEPT_WRAP_W_WWWW(name) \ + extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4) { \ + typedef decltype(name) *fntype; \ + static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \ + return fn(arg1, arg2, arg3, arg4); \ + } \ + INTERCEPT_OR_DIE(#name, name); + +#define INTERCEPT_WRAP_W_WWWWW(name) \ + extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4, \ + void *arg5) { \ + typedef decltype(name) *fntype; \ + static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \ + return fn(arg1, arg2, arg3, arg4, arg5); \ + } \ + INTERCEPT_OR_DIE(#name, name); + +#define INTERCEPT_WRAP_W_WWWWWW(name) \ + extern "C" void *name(void *arg1, void *arg2, void *arg3, void *arg4, \ + void *arg5, void *arg6) { \ + typedef decltype(name) *fntype; \ + static fntype fn = (fntype)__sanitizer::dllThunkGetRealAddrOrDie(#name); \ + return fn(arg1, arg2, arg3, arg4, arg5, arg6); \ + } \ + INTERCEPT_OR_DIE(#name, name); + +#endif // SANITIZER_WIN_DLL_THUNK_H Index: compiler-rt/trunk/lib/sanitizer_common/sanitizer_win_dll_thunk.cc =================================================================== --- compiler-rt/trunk/lib/sanitizer_common/sanitizer_win_dll_thunk.cc +++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_win_dll_thunk.cc @@ -0,0 +1,91 @@ +//===-- sanitizer_win_dll_thunk.cc ----------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// This file defines a family of thunks that should be statically linked into +// the DLLs that have instrumentation in order to delegate the calls to the +// shared runtime that lives in the main binary. +// See https://github.com/google/sanitizers/issues/209 for the details. +//===----------------------------------------------------------------------===// + +#ifdef SANITIZER_DLL_THUNK +#include "sanitizer_win_defs.h" +#include "sanitizer_win_dll_thunk.h" +#include "interception/interception.h" + +extern "C" { +void *WINAPI GetModuleHandleA(const char *module_name); +void abort(); +} + +namespace __sanitizer { +uptr dllThunkGetRealAddrOrDie(const char *name) { + uptr ret = + __interception::InternalGetProcAddress((void *)GetModuleHandleA(0), name); + if (!ret) + abort(); + return ret; +} + +int dllThunkIntercept(const char* main_function, uptr dll_function) { + uptr wrapper = dllThunkGetRealAddrOrDie(main_function); + if (!__interception::OverrideFunction(dll_function, wrapper, 0)) + abort(); + return 0; +} +} // namespace __sanitizer + +// Include Sanitizer Common interface. +#define INTERFACE_FUNCTION(Name) INTERCEPT_SANITIZER_FUNCTION(Name) +#define INTERFACE_WEAK_FUNCTION(Name) +#include "sanitizer_common_interface.inc" + +#pragma section(".DLLTH$A", read) // NOLINT +#pragma section(".DLLTH$Z", read) // NOLINT + +typedef void (*DllThunkCB)(); +extern "C" { +__declspec(allocate(".DLLTH$A")) DllThunkCB __start_dll_thunk; +__declspec(allocate(".DLLTH$Z")) DllThunkCB __stop_dll_thunk; +} + +// Disable compiler warnings that show up if we declare our own version +// of a compiler intrinsic (e.g. strlen). +#pragma warning(disable: 4391) +#pragma warning(disable: 4392) + +extern "C" int __dll_thunk_init() { + static bool flag = false; + // __dll_thunk_init is expected to be called by only one thread. + if (flag) return 0; + flag = true; + + for (DllThunkCB *it = &__start_dll_thunk; it < &__stop_dll_thunk; ++it) + if (*it) + (*it)(); + + // In DLLs, the callbacks are expected to return 0, + // otherwise CRT initialization fails. + return 0; +} + +// We want to call dll_thunk_init before C/C++ initializers / constructors are +// executed, otherwise functions like memset might be invoked. +#pragma section(".CRT$XIB", long, read) // NOLINT +__declspec(allocate(".CRT$XIB")) int (*__dll_thunk_preinit)() = + __dll_thunk_init; + +static void WINAPI dll_thunk_thread_init(void *mod, unsigned long reason, + void *reserved) { + if (reason == /*DLL_PROCESS_ATTACH=*/1) __dll_thunk_init(); +} + +#pragma section(".CRT$XLAB", long, read) // NOLINT +__declspec(allocate(".CRT$XLAB")) void (WINAPI *__dll_thunk_tls_init)(void *, + unsigned long, void *) = dll_thunk_thread_init; + +#endif // SANITIZER_DLL_THUNK Index: compiler-rt/trunk/lib/ubsan/CMakeLists.txt =================================================================== --- compiler-rt/trunk/lib/ubsan/CMakeLists.txt +++ compiler-rt/trunk/lib/ubsan/CMakeLists.txt @@ -90,6 +90,15 @@ ARCHS ${UBSAN_COMMON_SUPPORTED_ARCH} SOURCES ${UBSAN_CXX_SOURCES} CFLAGS ${UBSAN_CXXFLAGS}) + if (WIN32) + add_compiler_rt_object_libraries(UbsanDllThunk + ${SANITIZER_COMMON_SUPPORTED_OS} + ARCHS ${UBSAN_SUPPORTED_ARCH} + SOURCES ubsan_win_dll_thunk.cc + CFLAGS ${UBSAN_CFLAGS} -DSANITIZER_DLL_THUNK + DEFS ${UBSAN_COMMON_DEFINITIONS}) + endif() + if(COMPILER_RT_HAS_UBSAN) # Initializer of standalone UBSan runtime. add_compiler_rt_object_libraries(RTUbsan_standalone Index: compiler-rt/trunk/lib/ubsan/ubsan_win_dll_thunk.cc =================================================================== --- compiler-rt/trunk/lib/ubsan/ubsan_win_dll_thunk.cc +++ compiler-rt/trunk/lib/ubsan/ubsan_win_dll_thunk.cc @@ -0,0 +1,21 @@ +//===-- ubsan_win_dll_thunk.cc --------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a family of thunks that should be statically linked into +// the DLLs that have instrumentation in order to delegate the calls to the +// shared runtime that lives in the main binary. +// See https://github.com/google/sanitizers/issues/209 for the details. +//===----------------------------------------------------------------------===// +#ifdef SANITIZER_DLL_THUNK +#include "sanitizer_common/sanitizer_win_dll_thunk.h" +// Ubsan interface functions. +#define INTERFACE_FUNCTION(Name) INTERCEPT_SANITIZER_FUNCTION(Name) +#define INTERFACE_WEAK_FUNCTION(Name) +#include "ubsan_interface.inc" +#endif // SANITIZER_DLL_THUNK