summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/asan/asan_win.cc18
-rw-r--r--lib/asan/asan_win_dll_thunk.cc15
-rw-r--r--lib/asan/asan_win_dynamic_runtime_thunk.cc15
-rw-r--r--test/asan/TestCases/Windows/tls_init.cc51
4 files changed, 94 insertions, 5 deletions
diff --git a/lib/asan/asan_win.cc b/lib/asan/asan_win.cc
index c3f7943c2..54f622fb2 100644
--- a/lib/asan/asan_win.cc
+++ b/lib/asan/asan_win.cc
@@ -343,9 +343,23 @@ int __asan_set_seh_filter() {
// immediately after the CRT runs. This way, our exception filter is called
// 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;
+__declspec(allocate(".CRT$XCAB")) 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
diff --git a/lib/asan/asan_win_dll_thunk.cc b/lib/asan/asan_win_dll_thunk.cc
index 545a7d1d9..297a91aeb 100644
--- a/lib/asan/asan_win_dll_thunk.cc
+++ b/lib/asan/asan_win_dll_thunk.cc
@@ -456,4 +456,19 @@ static int call_asan_init() {
#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
diff --git a/lib/asan/asan_win_dynamic_runtime_thunk.cc b/lib/asan/asan_win_dynamic_runtime_thunk.cc
index 3b06e8bae..96ca47e57 100644
--- a/lib/asan/asan_win_dynamic_runtime_thunk.cc
+++ b/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 @@ static int InitializeClonedVariables() {
return 0;
}
-// Our cloned variables must be initialized before C/C++ constructors.
-__declspec(allocate(".CRT$XIB"))
-int (*__asan_initialize_cloned_variables)() = InitializeClonedVariables;
+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
diff --git a/test/asan/TestCases/Windows/tls_init.cc b/test/asan/TestCases/Windows/tls_init.cc
new file mode 100644
index 000000000..c29c4a377
--- /dev/null
+++ b/test/asan/TestCases/Windows/tls_init.cc
@@ -0,0 +1,51 @@
+// RUN: %clang_cl_asan %s -Fe%t.exe
+// RUN: %run %t.exe | FileCheck %s
+
+// CHECK: my_thread_callback
+// CHECK: ran_before_main: 1
+
+#include <windows.h>
+#include <stdio.h>
+#include <string.h>
+
+#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);
+}