summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorstar9029 <[email protected]>2025-12-20 23:30:23 +0800
committerstar9029 <[email protected]>2025-12-20 23:30:23 +0800
commitd3a3a4d45e92a4b6af0438e0df81dce9d0bd4dc4 (patch)
tree837e489367a9b546737b9e4dda2d98857985f790
parent95363af8cefe003ed1599b124081a0d009f9fb8d (diff)
Refactor windows asan
-rw-r--r--xmake/core/package/package.lua40
-rw-r--r--xmake/modules/package/tools/cmake.lua4
-rw-r--r--xmake/rules/c++/config/sanitizer.lua53
-rw-r--r--xmake/toolchains/clang-cl/load.lua4
4 files changed, 83 insertions, 18 deletions
diff --git a/xmake/core/package/package.lua b/xmake/core/package/package.lua
index 4a32bbdf7..75e134b2a 100644
--- a/xmake/core/package/package.lua
+++ b/xmake/core/package/package.lua
@@ -2460,17 +2460,51 @@ function _instance:_generate_sanitizer_configs(checkmode, sourcekind)
-- add cflags
local configs = {}
- if sourcekind and self:has_tool(sourcekind, "cl", "clang", "clangxx", "gcc", "gxx") then
+ if sourcekind and self:has_tool(sourcekind, "cl", "clang", "clangxx", "clang_cl", "gcc", "gxx") then
local cflag = sourcekind == "cxx" and "cxxflags" or "cflags"
configs[cflag] = "-fsanitize=" .. checkmode
end
+ local ldflags = {}
-- add ldflags and shflags
-- msvc does not have an fsanitize linker flag, so the 'link' tool is excluded
if self:has_tool("ld", "clang", "clangxx", "gcc", "gxx") then
- configs.ldflags = "-fsanitize=" .. checkmode
- configs.shflags = "-fsanitize=" .. checkmode
+ table.insert(ldflags, "-fsanitize=" .. checkmode)
end
+
+ if self:is_plat("windows") and checkmode == "address" and not self:has_tool("cxx", "cl") then
+ assert(self:has_runtime("MD", "MT"), "clang asan only support MD/MT runtime on windows")
+ if self:has_tool("cxx", "clang", "clangxx") then
+ if self:has_runtime("MT") then
+ table.insert(ldflags, "-D_MT")
+ elseif self:has_runtime("MD") then
+ table.join2(ldflags, {"-D_MT", "-D_DLL"})
+ end
+ elseif self:has_tool("cxx", "clang_cl") then
+ -- TODO: This is hack, try to find a way to let cmake use clang++ for link
+ -- @see https://gitlab.kitware.com/cmake/cmake/-/issues/26430
+ local outdata, errdata = assert(os.iorunv(self:build_getenv("cc"), {"-print-resource-dir"}))
+ local libdir = path.join(errdata:trim(), "lib/windows")
+
+ local kind
+ if self:has_runtime("MD") then
+ kind = "dynamic"
+ elseif self:has_runtime("MT") then
+ kind = "static"
+ end
+
+ local driver = self:has_tool("ld", "lld-link", "link") and "" or "-Wl,"
+ local thunk = path.join(libdir, string.format("clang_rt.asan_%s_runtime_thunk-x86_64.lib", kind))
+ table.join2(ldflags, {
+ path.unix(path.join(libdir, "clang_rt.asan_dynamic-x86_64.lib")),
+ driver .. "/WHOLEARCHIVE:" .. path.unix(thunk),
+ driver .. "/INFERASANLIBS:NO",
+ })
+ end
+ end
+
+ configs.ldflags = ldflags
+ configs.shflags = ldflags
return configs
end
diff --git a/xmake/modules/package/tools/cmake.lua b/xmake/modules/package/tools/cmake.lua
index 6745ee5bb..7bf9b8b31 100644
--- a/xmake/modules/package/tools/cmake.lua
+++ b/xmake/modules/package/tools/cmake.lua
@@ -426,6 +426,10 @@ function _get_configs_for_generic(package, configs, opt)
if package:is_library() then
envs.BUILD_SHARED_LIBS = package:config("shared") and "ON" or "OFF"
end
+ -- https://cmake.org/cmake/help/latest/variable/CMAKE_LINKER_TYPE.html
+ if package:has_tool("ld", "link") then
+ envs.CMAKE_LINKER_TYPE = "MSVC"
+ end
_fix_zigcc_linker_cmake(package, envs)
_insert_configs_from_envs(configs, envs, opt)
end
diff --git a/xmake/rules/c++/config/sanitizer.lua b/xmake/rules/c++/config/sanitizer.lua
index aeea8492f..e6606efe9 100644
--- a/xmake/rules/c++/config/sanitizer.lua
+++ b/xmake/rules/c++/config/sanitizer.lua
@@ -22,6 +22,12 @@
import("core.project.project")
import("lib.detect.find_tool")
import("core.base.semver")
+import("private.utils.toolchain", {alias = "toolchain_utils"})
+
+function _get_clang_asan_library_dir(target)
+ local toolchain = target:toolchain("clang-cl") or target:toolchain("clang")
+ return path.join(toolchain_utils.get_llvm_resourcedir(toolchain), "lib/windows")
+end
-- add build sanitizer
function _add_build_sanitizer(target, sourcekind, checkmode)
@@ -44,6 +50,39 @@ function _add_build_sanitizer(target, sourcekind, checkmode)
target:add("ldflags", "-fsanitize=" .. checkmode, {force = true})
target:add("shflags", "-fsanitize=" .. checkmode, {force = true})
end
+
+ if target:is_plat("windows") and checkmode == "address" and not target:has_tool("cxx", "cl") then
+ assert(target:has_runtime("MD", "MT"), "clang asan only support MD/MT runtime on windows")
+
+ local ldflags = {}
+ if target:has_tool("ld", "clang", "clangxx") then
+ if target:has_runtime("MT") then
+ table.insert(ldflags, "-D_MT")
+ elseif target:has_runtime("MD") then
+ table.join2(ldflags, {"-D_MT", "-D_DLL"})
+ end
+ else
+ -- cmake unsupported use clang++ as linker with clang-cl compiler, so we keep using lld-link/link as linker
+ -- @see https://gitlab.kitware.com/cmake/cmake/-/issues/26430
+ local kind
+ if target:has_runtime("MD") then
+ kind = "dynamic"
+ elseif target:has_runtime("MT") then
+ kind = "static"
+ end
+
+ local libdir = _get_clang_asan_library_dir(target)
+ local driver = target:has_tool("ld", "lld-link", "link") and "" or "-Wl,"
+ local thunk = path.join(libdir, string.format("clang_rt.asan_%s_runtime_thunk-x86_64.lib", kind))
+ table.join2(ldflags, {
+ path.unix(path.join(libdir, "clang_rt.asan_dynamic-x86_64.lib")),
+ driver .. "/WHOLEARCHIVE:" .. path.unix(thunk),
+ driver .. "/INFERASANLIBS:NO",
+ })
+ end
+ target:add("ldflags", ldflags, {force = true})
+ target:add("shflags", ldflags, {force = true})
+ end
end
function main(target, sourcekind)
@@ -68,17 +107,7 @@ function main(target, sourcekind)
-- we need to load runenvs for msvc
-- @see https://github.com/xmake-io/xmake/issues/4176
if target:is_plat("windows") and target:is_binary() then
- if target:has_tool("cxx", "clang_cl") then
- local clang_cl = target:toolchain("clang-cl")
- if clang_cl then
- local envs = clang_cl:runenvs()
- local vscmd_ver = envs and envs.VSCMD_VER
- if vscmd_ver and semver.match(vscmd_ver):ge("17.7") then
- local clang_cl_tool = assert(find_tool("clang-cl", {envs = envs}), "clang-cl not found!")
- target:add("runenvs", "PATH", path.directory(clang_cl_tool.program))
- end
- end
- else
+ if target:has_tool("cxx", "cl") then
local msvc = target:toolchain("msvc")
if msvc then
local envs = msvc:runenvs()
@@ -88,6 +117,8 @@ function main(target, sourcekind)
target:add("runenvs", "PATH", path.directory(cl.program))
end
end
+ else
+ target:add("runenvs", "PATH", _get_clang_asan_library_dir(target))
end
end
end
diff --git a/xmake/toolchains/clang-cl/load.lua b/xmake/toolchains/clang-cl/load.lua
index 8dab94d4a..fb95724b9 100644
--- a/xmake/toolchains/clang-cl/load.lua
+++ b/xmake/toolchains/clang-cl/load.lua
@@ -41,10 +41,6 @@ function main(toolchain)
toolchain:set("toolset", "ld", "lld-link")
toolchain:set("toolset", "sh", "lld-link")
toolchain:set("toolset", "ar", "llvm-ar")
- elseif project.policy("build.sanitizer.address") then
- toolchain:set("toolset", "ld", "clang++")
- toolchain:set("toolset", "sh", "clang++")
- toolchain:set("toolset", "ar", "llvm-ar")
else
toolchain:set("toolset", "ld", "link.exe")
toolchain:set("toolset", "sh", "link.exe")