Skip to content

[asan][windows] Eliminate the static asan runtime on windows #93770

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
May 30, 2024

Conversation

barcharcraz
Copy link
Contributor

@barcharcraz barcharcraz commented May 30, 2024

Originally #81677

That PR was reverted due to breaking the MSVC build. The breakage in question was just msvc not understanding the "optnone" attribute, which has been corrected.

I had applied this fix to our internal fork already, but didn't bring it over as for some reason I figured clang-specific code was OK (no idea why I thought this, at least the build should pass).

I have further changes that make the tests pass when building/running with msvc as well, but as the failures are pre-existing there's no need to include them here and make this diff even bigger.

@llvmbot
Copy link
Member

llvmbot commented May 30, 2024

@llvm/pr-subscribers-platform-windows

Author: Charlie Barto (barcharcraz)

Changes

Originally #81677

That PR was reverted due to breaking the MSVC build. The breakage in question was just msvc not understanding the "optnone" attribute, which has been corrected.


Patch is 130.66 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/93770.diff

56 Files Affected:

  • (modified) clang/lib/Driver/SanitizerArgs.cpp (+10-4)
  • (modified) clang/lib/Driver/ToolChains/MSVC.cpp (+11-15)
  • (modified) clang/test/Driver/cl-link.c (+4-6)
  • (modified) compiler-rt/CMakeLists.txt (+6-2)
  • (modified) compiler-rt/lib/asan/CMakeLists.txt (+82-77)
  • (modified) compiler-rt/lib/asan/asan_flags.cpp (+85-11)
  • (modified) compiler-rt/lib/asan/asan_globals_win.cpp (+3-1)
  • (modified) compiler-rt/lib/asan/asan_malloc_win.cpp (+42-55)
  • (added) compiler-rt/lib/asan/asan_malloc_win_thunk.cpp (+229)
  • (added) compiler-rt/lib/asan/asan_win_common_runtime_thunk.cpp (+112)
  • (added) compiler-rt/lib/asan/asan_win_common_runtime_thunk.h (+38)
  • (removed) compiler-rt/lib/asan/asan_win_dll_thunk.cpp (-165)
  • (modified) compiler-rt/lib/asan/asan_win_dynamic_runtime_thunk.cpp (+14-90)
  • (added) compiler-rt/lib/asan/asan_win_static_runtime_thunk.cpp (+110)
  • (modified) compiler-rt/lib/asan/tests/CMakeLists.txt (+1-1)
  • (modified) compiler-rt/lib/profile/CMakeLists.txt (+6)
  • (modified) compiler-rt/lib/sanitizer_common/CMakeLists.txt (+11-44)
  • (modified) compiler-rt/lib/sanitizer_common/sanitizer_common_interface.inc (+6)
  • (removed) compiler-rt/lib/sanitizer_common/sanitizer_coverage_win_dll_thunk.cpp (-20)
  • (renamed) compiler-rt/lib/sanitizer_common/sanitizer_coverage_win_runtime_thunk.cpp (+12-9)
  • (removed) compiler-rt/lib/sanitizer_common/sanitizer_coverage_win_weak_interception.cpp (-23)
  • (removed) compiler-rt/lib/sanitizer_common/sanitizer_win_dll_thunk.cpp (-101)
  • (removed) compiler-rt/lib/sanitizer_common/sanitizer_win_dll_thunk.h (-181)
  • (removed) compiler-rt/lib/sanitizer_common/sanitizer_win_dynamic_runtime_thunk.cpp (-26)
  • (added) compiler-rt/lib/sanitizer_common/sanitizer_win_immortalize.h (+71)
  • (added) compiler-rt/lib/sanitizer_common/sanitizer_win_interception.cpp (+156)
  • (added) compiler-rt/lib/sanitizer_common/sanitizer_win_interception.h (+32)
  • (added) compiler-rt/lib/sanitizer_common/sanitizer_win_thunk_interception.cpp (+110)
  • (added) compiler-rt/lib/sanitizer_common/sanitizer_win_thunk_interception.h (+85)
  • (removed) compiler-rt/lib/sanitizer_common/sanitizer_win_weak_interception.cpp (-94)
  • (removed) compiler-rt/lib/sanitizer_common/sanitizer_win_weak_interception.h (-32)
  • (modified) compiler-rt/lib/ubsan/CMakeLists.txt (+3-24)
  • (removed) compiler-rt/lib/ubsan/ubsan_win_dll_thunk.cpp (-20)
  • (renamed) compiler-rt/lib/ubsan/ubsan_win_runtime_thunk.cpp (+7-4)
  • (removed) compiler-rt/lib/ubsan/ubsan_win_weak_interception.cpp (-23)
  • (modified) compiler-rt/test/asan/TestCases/Darwin/interface_symbols_darwin.cpp (+3)
  • (modified) compiler-rt/test/asan/TestCases/Linux/interface_symbols_linux.cpp (+3)
  • (modified) compiler-rt/test/asan/TestCases/Windows/double_free.cpp (+3-3)
  • (modified) compiler-rt/test/asan/TestCases/Windows/free_hook_realloc.cpp (-3)
  • (modified) compiler-rt/test/asan/TestCases/Windows/malloc_left_oob.cpp (+1-1)
  • (modified) compiler-rt/test/asan/TestCases/Windows/malloc_right_oob.cpp (+1-1)
  • (modified) compiler-rt/test/asan/TestCases/Windows/malloc_uaf.cpp (+2-2)
  • (modified) compiler-rt/test/asan/TestCases/Windows/msvc/dll_and_lib.cpp (+2-3)
  • (modified) compiler-rt/test/asan/TestCases/Windows/msvc/dll_large_function.cpp (+1-2)
  • (modified) compiler-rt/test/asan/TestCases/Windows/realloc_left_oob.cpp (+1-1)
  • (modified) compiler-rt/test/asan/TestCases/Windows/realloc_right_oob.cpp (+1-1)
  • (modified) compiler-rt/test/asan/TestCases/Windows/realloc_uaf.cpp (+2-2)
  • (modified) compiler-rt/test/asan/TestCases/Windows/symbols_path.cpp (+1-1)
  • (modified) compiler-rt/test/asan/TestCases/Windows/unsymbolized.cpp (+1-1)
  • (modified) compiler-rt/test/asan/TestCases/Windows/use_after_realloc.cpp (+2-2)
  • (modified) compiler-rt/test/asan/TestCases/debug_double_free.cpp (-3)
  • (modified) compiler-rt/test/asan/TestCases/debug_report.cpp (-3)
  • (modified) compiler-rt/test/asan/TestCases/default_options.cpp (-4)
  • (modified) compiler-rt/test/asan/TestCases/on_error_callback.cpp (-3)
  • (modified) compiler-rt/test/asan/TestCases/report_error_summary.cpp (-3)
  • (modified) compiler-rt/test/asan/lit.cfg.py (+21-2)
diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp
index 273f215ca94a8..7b7fd2d9d4742 100644
--- a/clang/lib/Driver/SanitizerArgs.cpp
+++ b/clang/lib/Driver/SanitizerArgs.cpp
@@ -909,10 +909,16 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC,
         DiagnoseErrors);
   }
 
-  SharedRuntime =
-      Args.hasFlag(options::OPT_shared_libsan, options::OPT_static_libsan,
-                   TC.getTriple().isAndroid() || TC.getTriple().isOSFuchsia() ||
-                       TC.getTriple().isOSDarwin());
+  SharedRuntime = Args.hasFlag(
+      options::OPT_shared_libsan, options::OPT_static_libsan,
+      TC.getTriple().isAndroid() || TC.getTriple().isOSFuchsia() ||
+          TC.getTriple().isOSDarwin() || TC.getTriple().isOSWindows());
+  if (!SharedRuntime && TC.getTriple().isOSWindows()) {
+    Arg *A =
+        Args.getLastArg(options::OPT_shared_libsan, options::OPT_static_libsan);
+    D.Diag(clang::diag::err_drv_unsupported_opt_for_target)
+        << A->getSpelling() << TC.getTriple().str();
+  }
 
   ImplicitCfiRuntime = TC.getTriple().isAndroid();
 
diff --git a/clang/lib/Driver/ToolChains/MSVC.cpp b/clang/lib/Driver/ToolChains/MSVC.cpp
index b7021d4b996dd..bf54f04363851 100644
--- a/clang/lib/Driver/ToolChains/MSVC.cpp
+++ b/clang/lib/Driver/ToolChains/MSVC.cpp
@@ -201,10 +201,10 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA,
   if (TC.getSanitizerArgs(Args).needsAsanRt()) {
     CmdArgs.push_back(Args.MakeArgString("-debug"));
     CmdArgs.push_back(Args.MakeArgString("-incremental:no"));
-    if (TC.getSanitizerArgs(Args).needsSharedRt() ||
-        Args.hasArg(options::OPT__SLASH_MD, options::OPT__SLASH_MDd)) {
-      for (const auto &Lib : {"asan_dynamic", "asan_dynamic_runtime_thunk"})
-        CmdArgs.push_back(TC.getCompilerRTArgString(Args, Lib));
+    CmdArgs.push_back(TC.getCompilerRTArgString(Args, "asan_dynamic"));
+    auto defines = Args.getAllArgValues(options::OPT_D);
+    if (Args.hasArg(options::OPT__SLASH_MD, options::OPT__SLASH_MDd) ||
+        find(begin(defines), end(defines), "_DLL") != end(defines)) {
       // Make sure the dynamic runtime thunk is not optimized out at link time
       // to ensure proper SEH handling.
       CmdArgs.push_back(Args.MakeArgString(
@@ -213,19 +213,15 @@ void visualstudio::Linker::ConstructJob(Compilation &C, const JobAction &JA,
               : "-include:__asan_seh_interceptor"));
       // Make sure the linker consider all object files from the dynamic runtime
       // thunk.
-      CmdArgs.push_back(Args.MakeArgString(std::string("-wholearchive:") +
+      CmdArgs.push_back(Args.MakeArgString(
+          std::string("-wholearchive:") +
           TC.getCompilerRT(Args, "asan_dynamic_runtime_thunk")));
-    } else if (DLL) {
-      CmdArgs.push_back(TC.getCompilerRTArgString(Args, "asan_dll_thunk"));
     } else {
-      for (const auto &Lib : {"asan", "asan_cxx"}) {
-        CmdArgs.push_back(TC.getCompilerRTArgString(Args, Lib));
-        // Make sure the linker consider all object files from the static lib.
-        // This is necessary because instrumented dlls need access to all the
-        // interface exported by the static lib in the main executable.
-        CmdArgs.push_back(Args.MakeArgString(std::string("-wholearchive:") +
-            TC.getCompilerRT(Args, Lib)));
-      }
+      // Make sure the linker consider all object files from the static runtime
+      // thunk.
+      CmdArgs.push_back(Args.MakeArgString(
+          std::string("-wholearchive:") +
+          TC.getCompilerRT(Args, "asan_static_runtime_thunk")));
     }
   }
 
diff --git a/clang/test/Driver/cl-link.c b/clang/test/Driver/cl-link.c
index ffd0b5ac4bade..f526044276045 100644
--- a/clang/test/Driver/cl-link.c
+++ b/clang/test/Driver/cl-link.c
@@ -13,10 +13,8 @@
 // ASAN: link.exe
 // ASAN: "-debug"
 // ASAN: "-incremental:no"
-// ASAN: "{{[^"]*}}clang_rt.asan.lib"
-// ASAN: "-wholearchive:{{.*}}clang_rt.asan.lib"
-// ASAN: "{{[^"]*}}clang_rt.asan_cxx.lib"
-// ASAN: "-wholearchive:{{.*}}clang_rt.asan_cxx.lib"
+// ASAN: "{{[^"]*}}clang_rt.asan_dynamic.lib"
+// ASAN: "-wholearchive:{{.*}}clang_rt.asan_static_runtime_thunk.lib"
 // ASAN: "{{.*}}cl-link{{.*}}.obj"
 
 // RUN: %clang_cl -m32 -arch:IA32 --target=i386-pc-win32 /MD /Tc%s -fuse-ld=link -### -fsanitize=address 2>&1 | FileCheck --check-prefix=ASAN-MD %s
@@ -24,7 +22,6 @@
 // ASAN-MD: "-debug"
 // ASAN-MD: "-incremental:no"
 // ASAN-MD: "{{.*}}clang_rt.asan_dynamic.lib"
-// ASAN-MD: "{{[^"]*}}clang_rt.asan_dynamic_runtime_thunk.lib"
 // ASAN-MD: "-include:___asan_seh_interceptor"
 // ASAN-MD: "-wholearchive:{{.*}}clang_rt.asan_dynamic_runtime_thunk.lib"
 // ASAN-MD: "{{.*}}cl-link{{.*}}.obj"
@@ -40,7 +37,8 @@
 // ASAN-DLL: "-dll"
 // ASAN-DLL: "-debug"
 // ASAN-DLL: "-incremental:no"
-// ASAN-DLL: "{{.*}}clang_rt.asan_dll_thunk.lib"
+// ASAN-DLL: "{{.*}}clang_rt.asan_dynamic.lib"
+// ASAN-DLL: "-wholearchive:{{.*}}clang_rt.asan_static_runtime_thunk.lib"
 // ASAN-DLL: "{{.*}}cl-link{{.*}}.obj"
 
 // RUN: %clang_cl /Zi /Tc%s -fuse-ld=link -### 2>&1 | FileCheck --check-prefix=DEBUG %s
diff --git a/compiler-rt/CMakeLists.txt b/compiler-rt/CMakeLists.txt
index 6ce451e3cac2e..158fa270c3f15 100644
--- a/compiler-rt/CMakeLists.txt
+++ b/compiler-rt/CMakeLists.txt
@@ -378,8 +378,12 @@ if("${COMPILER_RT_DEFAULT_TARGET_ARCH}" MATCHES "s390x")
 endif()
 
 if(MSVC)
-  # FIXME: In fact, sanitizers should support both /MT and /MD, see PR20214.
-  set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreaded)
+
+  # asan on windows only supports the release dll version of the runtimes, in the interest of
+  # only having one asan dll to support/test. Having asan statically linked
+  # with the runtime might be possible, but it multiplies the number of scenerios to test.
+  # the program USING sanitizers can use whatever version of the runtime it wants to.
+  set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreadedDLL)
 
   # Remove any /M[DT][d] flags, and strip any definitions of _DEBUG.
   # Since we're using CMAKE_MSVC_RUNTIME_LIBRARY (CMP0091 set to NEW),
diff --git a/compiler-rt/lib/asan/CMakeLists.txt b/compiler-rt/lib/asan/CMakeLists.txt
index 463ea233b37aa..f992419c6d982 100644
--- a/compiler-rt/lib/asan/CMakeLists.txt
+++ b/compiler-rt/lib/asan/CMakeLists.txt
@@ -32,6 +32,20 @@ set(ASAN_SOURCES
   asan_win.cpp
   )
 
+if(WIN32)
+  set(ASAN_DYNAMIC_RUNTIME_THUNK_SOURCES
+    asan_globals_win.cpp
+    asan_win_common_runtime_thunk.cpp
+    asan_win_dynamic_runtime_thunk.cpp
+    )
+  set(ASAN_STATIC_RUNTIME_THUNK_SOURCES
+    asan_globals_win.cpp
+    asan_malloc_win_thunk.cpp
+    asan_win_common_runtime_thunk.cpp
+    asan_win_static_runtime_thunk.cpp
+    )
+endif()
+
 if (NOT WIN32 AND NOT APPLE)
   list(APPEND ASAN_SOURCES
     asan_interceptors_vfork.S
@@ -136,7 +150,7 @@ append_list_if(MINGW "${MINGW_LIBRARIES}" ASAN_DYNAMIC_LIBS)
 add_compiler_rt_object_libraries(RTAsan_dynamic
   OS ${SANITIZER_COMMON_SUPPORTED_OS}
   ARCHS ${ASAN_SUPPORTED_ARCH}
-  SOURCES ${ASAN_SOURCES} ${ASAN_CXX_SOURCES}
+  SOURCES ${ASAN_SOURCES}
   ADDITIONAL_HEADERS ${ASAN_HEADERS}
   CFLAGS ${ASAN_DYNAMIC_CFLAGS}
   DEFS ${ASAN_DYNAMIC_DEFINITIONS})
@@ -221,46 +235,52 @@ else()
     RTSanitizerCommonSymbolizerInternal
     RTLSanCommon
     RTUbsan)
+  if (NOT WIN32)
+    add_compiler_rt_runtime(clang_rt.asan
+      STATIC
+      ARCHS ${ASAN_SUPPORTED_ARCH}
+      OBJECT_LIBS RTAsan_preinit
+                  RTAsan
+                  ${ASAN_COMMON_RUNTIME_OBJECT_LIBS}
+      CFLAGS ${ASAN_CFLAGS}
+      DEFS ${ASAN_COMMON_DEFINITIONS}
+      PARENT_TARGET asan)
 
-  add_compiler_rt_runtime(clang_rt.asan
-    STATIC
-    ARCHS ${ASAN_SUPPORTED_ARCH}
-    OBJECT_LIBS RTAsan_preinit
-                RTAsan
-                ${ASAN_COMMON_RUNTIME_OBJECT_LIBS}
-    CFLAGS ${ASAN_CFLAGS}
-    DEFS ${ASAN_COMMON_DEFINITIONS}
-    PARENT_TARGET asan)
-
-  add_compiler_rt_runtime(clang_rt.asan_cxx
-    STATIC
-    ARCHS ${ASAN_SUPPORTED_ARCH}
-    OBJECT_LIBS RTAsan_cxx
-                RTUbsan_cxx
-    CFLAGS ${ASAN_CFLAGS}
-    DEFS ${ASAN_COMMON_DEFINITIONS}
-    PARENT_TARGET asan)
+    add_compiler_rt_runtime(clang_rt.asan_cxx
+      STATIC
+      ARCHS ${ASAN_SUPPORTED_ARCH}
+      OBJECT_LIBS RTAsan_cxx
+                  RTUbsan_cxx
+      CFLAGS ${ASAN_CFLAGS}
+      DEFS ${ASAN_COMMON_DEFINITIONS}
+      PARENT_TARGET asan)
 
-  add_compiler_rt_runtime(clang_rt.asan_static
-    STATIC
-    ARCHS ${ASAN_SUPPORTED_ARCH}
-    OBJECT_LIBS RTAsan_static
-    CFLAGS ${ASAN_CFLAGS}
-    DEFS ${ASAN_COMMON_DEFINITIONS}
-    PARENT_TARGET asan)
+    add_compiler_rt_runtime(clang_rt.asan_static
+      STATIC
+      ARCHS ${ASAN_SUPPORTED_ARCH}
+      OBJECT_LIBS RTAsan_static
+      CFLAGS ${ASAN_CFLAGS}
+      DEFS ${ASAN_COMMON_DEFINITIONS}
+      PARENT_TARGET asan)
 
-  add_compiler_rt_runtime(clang_rt.asan-preinit
-    STATIC
-    ARCHS ${ASAN_SUPPORTED_ARCH}
-    OBJECT_LIBS RTAsan_preinit
-    CFLAGS ${ASAN_CFLAGS}
-    DEFS ${ASAN_COMMON_DEFINITIONS}
-    PARENT_TARGET asan)
+    add_compiler_rt_runtime(clang_rt.asan-preinit
+      STATIC
+      ARCHS ${ASAN_SUPPORTED_ARCH}
+      OBJECT_LIBS RTAsan_preinit
+      CFLAGS ${ASAN_CFLAGS}
+      DEFS ${ASAN_COMMON_DEFINITIONS}
+      PARENT_TARGET asan)
+  endif()
 
   foreach(arch ${ASAN_SUPPORTED_ARCH})
     if (COMPILER_RT_HAS_VERSION_SCRIPT)
+      if(WIN32)
+        set(SANITIZER_RT_VERSION_LIST_LIBS clang_rt.asan-${arch})
+      else()
+        set(SANITIZER_RT_VERSION_LIST_LIBS clang_rt.asan-${arch} clang_rt.asan_cxx-${arch})
+      endif()
       add_sanitizer_rt_version_list(clang_rt.asan-dynamic-${arch}
-                                    LIBS clang_rt.asan-${arch} clang_rt.asan_cxx-${arch}
+                                    LIBS ${SANITIZER_RT_VERSION_LIST_LIBS}
                                     EXTRA asan.syms.extra)
       set(VERSION_SCRIPT_FLAG
            -Wl,--version-script,${CMAKE_CURRENT_BINARY_DIR}/clang_rt.asan-dynamic-${arch}.vers)
@@ -278,25 +298,11 @@ else()
     endif()
 
     set(ASAN_DYNAMIC_WEAK_INTERCEPTION)
-    if (WIN32)
-      add_compiler_rt_object_libraries(AsanWeakInterception
-        ${SANITIZER_COMMON_SUPPORTED_OS}
-        ARCHS ${arch}
-        SOURCES
-          asan_win_weak_interception.cpp
-        CFLAGS ${ASAN_CFLAGS} -DSANITIZER_DYNAMIC
-        DEFS ${ASAN_COMMON_DEFINITIONS})
-      set(ASAN_DYNAMIC_WEAK_INTERCEPTION
-          AsanWeakInterception
-          UbsanWeakInterception
-          SancovWeakInterception
-          SanitizerCommonWeakInterception)
-    endif()
-
     add_compiler_rt_runtime(clang_rt.asan
       SHARED
       ARCHS ${arch}
       OBJECT_LIBS ${ASAN_COMMON_RUNTIME_OBJECT_LIBS}
+              RTAsan_cxx
               RTAsan_dynamic
               # The only purpose of RTAsan_dynamic_version_script_dummy is to
               # carry a dependency of the shared runtime on the version script.
@@ -324,36 +330,12 @@ else()
     endif()
 
     if (WIN32)
-      add_compiler_rt_object_libraries(AsanDllThunk
-        ${SANITIZER_COMMON_SUPPORTED_OS}
-        ARCHS ${arch}
-        SOURCES asan_globals_win.cpp
-                asan_win_dll_thunk.cpp
-        CFLAGS ${ASAN_CFLAGS} -DSANITIZER_DLL_THUNK
-        DEFS ${ASAN_COMMON_DEFINITIONS})
-
-      add_compiler_rt_runtime(clang_rt.asan_dll_thunk
-        STATIC
-        ARCHS ${arch}
-        OBJECT_LIBS AsanDllThunk
-                    UbsanDllThunk
-                    SancovDllThunk
-                    SanitizerCommonDllThunk
-        SOURCES $<TARGET_OBJECTS:RTInterception.${arch}>
-        PARENT_TARGET asan)
-
       set(DYNAMIC_RUNTIME_THUNK_CFLAGS "-DSANITIZER_DYNAMIC_RUNTIME_THUNK")
-      if(MSVC)
-        list(APPEND DYNAMIC_RUNTIME_THUNK_CFLAGS "-Zl")
-      elseif(CMAKE_C_COMPILER_ID MATCHES Clang)
-        list(APPEND DYNAMIC_RUNTIME_THUNK_CFLAGS "-nodefaultlibs")
-      endif()
 
       add_compiler_rt_object_libraries(AsanDynamicRuntimeThunk
         ${SANITIZER_COMMON_SUPPORTED_OS}
         ARCHS ${arch}
-        SOURCES asan_globals_win.cpp
-                asan_win_dynamic_runtime_thunk.cpp
+        SOURCES ${ASAN_DYNAMIC_RUNTIME_THUNK_SOURCES}
         CFLAGS ${ASAN_CFLAGS} ${DYNAMIC_RUNTIME_THUNK_CFLAGS}
         DEFS ${ASAN_COMMON_DEFINITIONS})
 
@@ -361,12 +343,35 @@ else()
         STATIC
         ARCHS ${arch}
         OBJECT_LIBS AsanDynamicRuntimeThunk
-                    UbsanDynamicRuntimeThunk
-                    SancovDynamicRuntimeThunk
-                    SanitizerCommonDynamicRuntimeThunk
+                    UbsanRuntimeThunk
+                    SancovRuntimeThunk
+                    SanitizerRuntimeThunk
         CFLAGS ${ASAN_CFLAGS} ${DYNAMIC_RUNTIME_THUNK_CFLAGS}
         DEFS ${ASAN_COMMON_DEFINITIONS}
         PARENT_TARGET asan)
+
+      # mingw does not support static linkage of the CRT
+      if(NOT MINGW)
+        set(STATIC_RUNTIME_THUNK_CFLAGS "-DSANITIZER_STATIC_RUNTIME_THUNK")
+
+        add_compiler_rt_object_libraries(AsanStaticRuntimeThunk
+          ${SANITIZER_COMMON_SUPPORTED_OS}
+          ARCHS ${arch}
+          SOURCES ${ASAN_STATIC_RUNTIME_THUNK_SOURCES}
+          CFLAGS ${ASAN_DYNAMIC_CFLAGS} ${STATIC_RUNTIME_THUNK_CFLAGS}
+          DEFS ${ASAN_DYNAMIC_DEFINITIONS})
+
+        add_compiler_rt_runtime(clang_rt.asan_static_runtime_thunk
+          STATIC
+          ARCHS ${arch}
+          OBJECT_LIBS AsanStaticRuntimeThunk
+                      UbsanRuntimeThunk
+                      SancovRuntimeThunk
+                      SanitizerRuntimeThunk
+          CFLAGS ${ASAN_DYNAMIC_CFLAGS} ${STATIC_RUNTIME_THUNK_CFLAGS}
+          DEFS ${ASAN_DYNAMIC_DEFINITIONS}
+          PARENT_TARGET asan)
+      endif()
     endif()
   endforeach()
 endif()
diff --git a/compiler-rt/lib/asan/asan_flags.cpp b/compiler-rt/lib/asan/asan_flags.cpp
index 2398984332321..56deb1b0d082b 100644
--- a/compiler-rt/lib/asan/asan_flags.cpp
+++ b/compiler-rt/lib/asan/asan_flags.cpp
@@ -11,14 +11,16 @@
 // ASan flag parsing logic.
 //===----------------------------------------------------------------------===//
 
-#include "asan_activation.h"
 #include "asan_flags.h"
+
+#include "asan_activation.h"
 #include "asan_interface_internal.h"
 #include "asan_stack.h"
 #include "lsan/lsan_common.h"
 #include "sanitizer_common/sanitizer_common.h"
-#include "sanitizer_common/sanitizer_flags.h"
 #include "sanitizer_common/sanitizer_flag_parser.h"
+#include "sanitizer_common/sanitizer_flags.h"
+#include "sanitizer_common/sanitizer_win_interception.h"
 #include "ubsan/ubsan_flags.h"
 #include "ubsan/ubsan_platform.h"
 
@@ -47,7 +49,21 @@ static void RegisterAsanFlags(FlagParser *parser, Flags *f) {
 #undef ASAN_FLAG
 }
 
-void InitializeFlags() {
+static void DisplayHelpMessages(FlagParser *parser) {
+  // TODO(eugenis): dump all flags at verbosity>=2?
+  if (Verbosity()) {
+    ReportUnrecognizedFlags();
+  }
+
+  if (common_flags()->help) {
+    parser->PrintFlagDescriptions();
+  }
+}
+
+static void InitializeDefaultFlags() {
+  Flags *f = flags();
+  FlagParser asan_parser;
+
   // Set the default values and prepare for parsing ASan and common flags.
   SetCommonFlagsDefaults();
   {
@@ -60,10 +76,8 @@ void InitializeFlags() {
     cf.exitcode = 1;
     OverrideCommonFlags(cf);
   }
-  Flags *f = flags();
   f->SetDefaults();
 
-  FlagParser asan_parser;
   RegisterAsanFlags(&asan_parser, f);
   RegisterCommonFlags(&asan_parser);
 
@@ -126,13 +140,12 @@ void InitializeFlags() {
 
   InitializeCommonFlags();
 
-  // TODO(eugenis): dump all flags at verbosity>=2?
-  if (Verbosity()) ReportUnrecognizedFlags();
+  // TODO(samsonov): print all of the flags (ASan, LSan, common).
+  DisplayHelpMessages(&asan_parser);
+}
 
-  if (common_flags()->help) {
-    // TODO(samsonov): print all of the flags (ASan, LSan, common).
-    asan_parser.PrintFlagDescriptions();
-  }
+static void ProcessFlags() {
+  Flags *f = flags();
 
   // Flag validation:
   if (!CAN_SANITIZE_LEAKS && common_flags()->detect_leaks) {
@@ -199,6 +212,67 @@ void InitializeFlags() {
   }
 }
 
+void InitializeFlags() {
+  InitializeDefaultFlags();
+  ProcessFlags();
+
+#if SANITIZER_WINDOWS
+  // On Windows, weak symbols are emulated by having the user program
+  // register which weak functions are defined.
+  // The ASAN DLL will initialize flags prior to user module initialization,
+  // so __asan_default_options will not point to the user definition yet.
+  // We still want to ensure we capture when options are passed via
+  // __asan_default_options, so we add a callback to be run
+  // when it is registered with the runtime.
+
+  // There is theoretically time between the initial ProcessFlags and
+  // registering the weak callback where a weak function could be added and we
+  // would miss it, but in practice, InitializeFlags will always happen under
+  // the loader lock (if built as a DLL) and so will any calls to
+  // __sanitizer_register_weak_function.
+  AddRegisterWeakFunctionCallback(
+      reinterpret_cast<uptr>(__asan_default_options), []() {
+        FlagParser asan_parser;
+
+        RegisterAsanFlags(&asan_parser, flags());
+        RegisterCommonFlags(&asan_parser);
+        asan_parser.ParseString(__asan_default_options());
+
+        DisplayHelpMessages(&asan_parser);
+        ProcessFlags();
+      });
+
+#  if CAN_SANITIZE_UB
+  AddRegisterWeakFunctionCallback(
+      reinterpret_cast<uptr>(__ubsan_default_options), []() {
+        FlagParser ubsan_parser;
+
+        __ubsan::RegisterUbsanFlags(&ubsan_parser, __ubsan::flags());
+        RegisterCommonFlags(&ubsan_parser);
+        ubsan_parser.ParseString(__ubsan_default_options());
+
+        // To match normal behavior, do not print UBSan help.
+        ProcessFlags();
+      });
+#  endif
+
+#  if CAN_SANITIZE_LEAKS
+  AddRegisterWeakFunctionCallback(
+      reinterpret_cast<uptr>(__lsan_default_options), []() {
+        FlagParser lsan_parser;
+
+        __lsan::RegisterLsanFlags(&lsan_parser, __lsan::flags());
+        RegisterCommonFlags(&lsan_parser);
+        lsan_parser.ParseString(__lsan_default_options());
+
+        // To match normal behavior, do not print LSan help.
+        ProcessFlags();
+      });
+#  endif
+
+#endif
+}
+
 }  // namespace __asan
 
 SANITIZER_INTERFACE_WEAK_DEF(const char*, __asan_default_options, void) {
diff --git a/compiler-rt/lib/asan/asan_globals_win.cpp b/compiler-rt/lib/asan/asan_globals_win.cpp
index 19af88ab12b40..8267f07b9cce4 100644
--- a/compiler-rt/lib/asan/asan_globals_win.cpp
+++ b/compiler-rt/lib/asan/asan_globals_win.cpp
@@ -28,7 +28,9 @@ static void call_on_globals(void (*hook)(__asan_global *, uptr)) {
   __asan_global *end = &__asan_globals_end;
   uptr bytediff = (uptr)end - (uptr)start;
   if (bytediff % sizeof(__asan_global) != 0) {
-#if defined(SANITIZER_DLL_THUNK) || defined(SANITIZER_DYNAMIC_RUNTIME_THUNK)
+#  if defined(SANITIZER_DLL_THUNK) ||             \
+      defined(SANITIZER_DYNAMIC_RUNTIME_THUNK) || \
+      defined(SANITIZER_STATIC_RUNTIME_THUNK)
     __debugbreak();
 #else
     CHECK("corrupt asan global array");
diff --git a/compiler-rt/lib/asan/asan_malloc_win.cpp b/compiler-rt/lib/asan/asan_malloc_win.cpp
index 7e1d04c36dd58..3278f07219876 100644
--- a/compiler-rt/lib/asan/asan_malloc_win.cpp
+++ b/compiler-rt/lib/asan/asan_malloc_win.cpp
@@ -58,97 +58,69 @@ using namespace __asan;
 // MD: Memory allocation functions are defined in the CRT .dll,
 // so we have to intercept them before they are called for the first time.
 
-#if ASAN_DYNAMIC
-# define ALLOCATION_FUNCTION_ATTRIBUTE
-#else
-# define ALLOCATION_FUNCTION_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE
-#endif
-
 extern "C" {
-ALLOCATION_FUNCTION_ATTRIBUTE
-size_t _msize(void *ptr) {
+__declspec(noinline) size_t _msize(void *ptr) {
   GET_CURRENT_PC_BP_SP;
   (void)sp;
   return asan_malloc_usable_size(ptr, pc, bp);
 }
 
-ALLOCATION_FUNCTION_ATTRIBUTE
-size_t _msize_base(void *ptr) {
-  return _msize(ptr);
-}
+__declspec(noinline) size_t _msize_base(void *ptr) { return _msize(ptr); }
 
-ALLOCATION_FUNCTION_ATTRIBUTE
-void free(void *ptr) {
+__declspec(noinline) void free(void *ptr) {
   GET_STACK_TRACE_FREE;
   return asan_free(ptr, &stack, FROM_MALLOC);
 }
 
-ALLOCATION_FUNCTION_ATTRIBUTE
-void _free_dbg(void *ptr, int) {
-  free(ptr);
-}
+__declspec(noinline) void _free_dbg(void *ptr, int) { free(ptr); }
 
-ALLOCATION_FUNCTION_ATTRIBUTE
-void _free_base(void *ptr) {
-  free(ptr);
-}
+__declspec(noinlin...
[truncated]

Copy link
Contributor

@amyw-msft amyw-msft left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

latest commit LGTM

barcharcraz and others added 20 commits May 30, 2024 08:58
The profiling runtime is still built with /MT, as it does not work with
/MD (and not well supported on windows)

Co-authored-by: Amy Wishnousky <[email protected]>
Instead build only ONE asan runtime on windows.

File Overview:

* asan_malloc_win_thunk.cpp
	Contains interceptors for malloc/free for applications using the static CRT.
	These are intercepted with an oldnames style library that takes precedence
	over the CRT because of linker search order. This is used instead of
	the library interception routines used for other functions so that we
	can intercept mallocs that happen before our interceptors run. Some
	other CRT functions are also included here, because they are provided by the same
	objects as allocation functions in the debug CRT.
* asan_win_common_runtime_thunk.cpp
	just the common init code for both static and dynamic CRTs
* asan_win_static_runtime_thunk.cpp
	static CRT specific initialization
* asan_win_dynamic_runtime_thunk.cpp
	dynamic crt initialization, most of the content that was here
	has been moved to the common runtime thunk
* asan_win_dll_thunk.cpp
	This used to provide functionality to redirect
	calls from DLLs to asan instrumented functions in the main library,
	but this never worked that well and was a nightmare. It's gone now
* sanitizer_common/sanitizer_common_interface.inc:
	The added functions are for the thunks to be able to delegate to the
	asan runtime DLL in order to override functions that live in the application
	executable at initialization. The ASAN dll can't do this itself because it
	doesn't have a way to get the addresses of these functions.
* sanitizer_common/sanitizer_win_immortalize:
	This is just an implementation of call_once that doens't require the CRT
	or C++ stdlib. We need this because we need to do this sort of thing
	before the CRT has initialized. This infrastructure is kinda ugly, we're sorry.
* sanitizer_common/sanitizer_win_interception.cpp:
	Provides the interface inside the sanitizer runtime DLL that instramented apps
	call to intercept stuff.
* sanitizer_common/sanitizer_win_thunk_interception.cpp:
	this is the code to setup and run the static initializers and/or TLS
	initializers, implemented basically how any initializers are on windows,
	these ones are registered before all the CRT initializers.
* sanitizer_common/sanitizer_win_thunk_interception.h
	INTERCEPT_LIBRARY_FUNCTION and REGISTER_WEAK_FUNCTION
	are the called initializers for each relevant function inside the instrumented
	binary. Note the optimizer is disabled for weak function registration routines
	because it detects that the two functions being compared have different names
	and deduces they must be the same, and no actual codegen for the if is required,
	causing an infinite loop. Clang does this in debug mode as well as release mode,
	and the cast to uintptr_t is required to suppress it in debug mode.

Co-Authored-By: Amy Wishnousky <[email protected]>
Now that the static runtime is gone the required librares are different.

Note that /MD or -D_DLL causes the dynamic runtime to get linked,
this is a little gross but emulates the behavior of MSVC.

"Real" msvc will link the debug build of asan if you specify /DEBUG or _DEBUG,
but that's not really necessary and requires building multiple
configurations from the same tree.
weak functions are registered after the asan runtime initializes.

This means __asan_default_options isn't available during asan
runtime initialization. Thus we split asan flag processing into
two parts and register a callback for the second part that's
executed after __asan_default_options is registered.

Co-Authored-By: Amy Wishnousky <[email protected]>
ALLOCATION_FUNCTION_ATTRIBUTE wasn't used elsewhere, and was just one
attribute, so abstracting it through a macro wasn't doing much good now that it's
not conditional on runtime type.

We're always in the dynamic runtime now so eliminate the preprocessor conditional.

The new exported functions are the interface used by the intercepted malloc/free family
in the instrumented binary to call the asan versions inside the dll runtime.

Co-authored-by: Amy Wishnousky <[email protected]>
…runtime thunks

and the fact we're "always" using the dynamic asan runtime.

python formatting
These test changes are seperated from the test changes in
0360f32 because they require
various functional changes to asan.

This reverts commit f54e0b4.
… config

After the windows static runtime is removed these static=static CRT and dynamic=dynamic CRT, both using the dynamic asan runtime.
This is required because now that there's only one asan runtime dll
the dynamic/static CRT wholearchived runtime thunk "flavor"
 is determined by passing the /MD or /MDd options, or defining -D_DLL.
@barcharcraz barcharcraz force-pushed the dev/chbarto/static_with_dynamic branch from 089fc39 to 3cea849 Compare May 30, 2024 15:58
@barcharcraz barcharcraz merged commit 8fa66c6 into llvm:main May 30, 2024
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants