diff options
| author | star9029 <[email protected]> | 2025-12-20 23:30:23 +0800 |
|---|---|---|
| committer | star9029 <[email protected]> | 2025-12-20 23:30:23 +0800 |
| commit | d3a3a4d45e92a4b6af0438e0df81dce9d0bd4dc4 (patch) | |
| tree | 837e489367a9b546737b9e4dda2d98857985f790 | |
| parent | 95363af8cefe003ed1599b124081a0d009f9fb8d (diff) | |
Refactor windows asan
| -rw-r--r-- | xmake/core/package/package.lua | 40 | ||||
| -rw-r--r-- | xmake/modules/package/tools/cmake.lua | 4 | ||||
| -rw-r--r-- | xmake/rules/c++/config/sanitizer.lua | 53 | ||||
| -rw-r--r-- | xmake/toolchains/clang-cl/load.lua | 4 |
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") |
