Index: lib/asan/asan_win.cc =================================================================== --- lib/asan/asan_win.cc +++ lib/asan/asan_win.cc @@ -344,8 +344,22 @@ // first and we can delegate to their filter if appropriate. #pragma section(".CRT$XCAB", long, read) // NOLINT __declspec(allocate(".CRT$XCAB")) - int (*__intercept_seh)() = __asan_set_seh_filter; +int (*__intercept_seh)() = __asan_set_seh_filter; + +// Piggyback on the TLS initialization callback directory to initialize asan as +// early as possible. Initializers in .CRT$XL* are called directly by ntdll, +// which run before the CRT. Users also add code to .CRT$XLC, so it's important +// to run our initializers first. +static void NTAPI asan_thread_init(void *module, DWORD reason, void *reserved) { + if (reason == DLL_PROCESS_ATTACH) __asan_init(); +} + +#pragma section(".CRT$XLAB", long, read) // NOLINT +__declspec(allocate(".CRT$XLAB")) void (NTAPI *__asan_tls_init)( + void *, unsigned long, void *) = asan_thread_init; #endif + + // }}} } // namespace __asan Index: lib/asan/asan_win_dll_thunk.cc =================================================================== --- lib/asan/asan_win_dll_thunk.cc +++ lib/asan/asan_win_dll_thunk.cc @@ -456,4 +456,19 @@ #pragma section(".CRT$XIB", long, read) // NOLINT __declspec(allocate(".CRT$XIB")) int (*__asan_preinit)() = call_asan_init; +#ifdef _M_IX86 +#define NTAPI __stdcall +#else +#define NTAPI +#endif + +static void NTAPI asan_thread_init(void *mod, unsigned long reason, + void *reserved) { + if (reason == /*DLL_PROCESS_ATTACH=*/1) __asan_init(); +} + +#pragma section(".CRT$XLAB", long, read) // NOLINT +__declspec(allocate(".CRT$XLAB")) void (NTAPI *__asan_tls_init)( + void *, unsigned long, void *) = asan_thread_init; + #endif // ASAN_DLL_THUNK Index: lib/asan/asan_win_dynamic_runtime_thunk.cc =================================================================== --- lib/asan/asan_win_dynamic_runtime_thunk.cc +++ lib/asan/asan_win_dynamic_runtime_thunk.cc @@ -33,6 +33,7 @@ #pragma section(".CRT$XCAB", long, read) // NOLINT #pragma section(".CRT$XTW", long, read) // NOLINT #pragma section(".CRT$XTY", long, read) // NOLINT +#pragma section(".CRT$XLAB", long, read) // NOLINT //////////////////////////////////////////////////////////////////////////////// // Define a copy of __asan_option_detect_stack_use_after_return that should be @@ -61,9 +62,17 @@ return 0; } -// Our cloned variables must be initialized before C/C++ constructors. +static void NTAPI asan_thread_init(void *mod, unsigned long reason, void *reserved) { + if (reason == DLL_PROCESS_ATTACH) InitializeClonedVariables(); +} + +// Our cloned variables must be initialized before C/C++ constructors. If TLS +// is used, our .CRT$XLAB initializer will run first. If not, our .CRT$XIB +// initializer is needed as a backup. __declspec(allocate(".CRT$XIB")) int (*__asan_initialize_cloned_variables)() = InitializeClonedVariables; +__declspec(allocate(".CRT$XLAB")) void (NTAPI *__asan_tls_init)( + void *, unsigned long, void *) = asan_thread_init; //////////////////////////////////////////////////////////////////////////////// // For some reason, the MD CRT doesn't call the C/C++ terminators during on DLL Index: test/asan/TestCases/Windows/tls_init.cc =================================================================== --- /dev/null +++ test/asan/TestCases/Windows/tls_init.cc @@ -0,0 +1,51 @@ +// RUN: %clang_cl_asan %s -Fetest.exe +// RUN: %run ./test.exe | FileCheck %s + +// CHECK: my_thread_callback +// CHECK: ran_before_main: 1 + +#include +#include +#include + +#pragma comment (lib, "dbghelp") + +static bool ran_before_main = false; + +extern "C" void __asan_init(void); + +static void NTAPI /*__attribute__((no_sanitize_address))*/ +my_thread_callback(PVOID module, DWORD reason, PVOID reserved) { + ran_before_main = true; + static const char str[] = "my_thread_callback\n"; + + // Fail the test if we aren't called for the expected reason or we can't write + // stdout. + if (reason != DLL_PROCESS_ATTACH) + return; + HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); + if (!out || out == INVALID_HANDLE_VALUE) + return; + + DWORD written = 0; + WriteFile(out, &str[0], sizeof(str), &written, NULL); +} + +extern "C" { +#pragma const_seg(".CRT$XLC") +extern const PIMAGE_TLS_CALLBACK p_thread_callback; +const PIMAGE_TLS_CALLBACK p_thread_callback = my_thread_callback; +#pragma const_seg() +} + +#ifdef _WIN64 +#pragma comment(linker, "/INCLUDE:_tls_used") +#pragma comment(linker, "/INCLUDE:p_thread_callback") +#else +#pragma comment(linker, "/INCLUDE:__tls_used") +#pragma comment(linker, "/INCLUDE:_p_thread_callback") +#endif + +int main() { + printf("ran_before_main: %d\n", ran_before_main); +}