refactor(asan): ASan 运行库目录改为按 PATH 自动探测,去除本机硬编码路径

- _FindAsanRuntimeDir() 重写为 _FindAsanRuntime():遍历 PATH 定位

- clang64/bin 由前置改为追加到 PATH,避免裸名 clang++/llc/opt 被解析到 clang64 的 libc++ 版本

- 缺失 ASan 运行库时的报错文案改用占位符路径
This commit is contained in:
TermiNexus committed 2026-10-07 11:25:37 +08:00
1 parent 305242aa8a
commit 64f3d79b64
1 file changed
+47 -25
+47 -25
View File
@@ -105,8 +105,11 @@ def _InvalidateTextCache(path: str) -> None:
_TextReadCacheBytes -= len(_old)
def _FindAsanRuntimeDir() -> str | None:
"""定位 clang64 的 ASan 运行库目录(按 LLVM 大版本取最新的那个)
_ASAN_DLL_A = 'libclang_rt.asan_dynamic-x86_64.dll.a'
def _FindAsanRuntime() -> tuple[str, str] | None:
"""通过 PATH 定位 clang64 的 ASan 运行库(按 LLVM 大版本取最新的那个)
mingw64 的 clang 只为 x86_64-w64-windows-gnu 目标提供 builtins / fuzzer /
profile / orc 运行库,**没有 ASan runtime**(<resource-dir>/lib/
@@ -116,27 +119,39 @@ def _FindAsanRuntimeDir() -> str | None:
clang64(MSVC ABI) 那边才有 libclang_rt.asan_dynamic-x86_64.dll.a 与
libclang_rt.asan_dynamic_runtime_thunk-x86_64.a,只能借用这两份。
路径**不再写死**:遍历 PATH 的每个目录 E,按 `E` 与 `dirname(E)` 两种可能取 base,
拼出 `<base>/lib/clang/<大版本>/lib/windows`,命中即为链接库目录;对应的运行期
DLL 目录取 `<base>/bin`。
Returns:
形如 D:\\msys64\\clang64\\lib\\clang\\22\\lib\\windows 的目录;找不到返回 None
(链接库目录, 运行期 DLL 目录);都找不到返回 None
"""
root = r'D:\msys64\clang64\lib\clang'
if not os.path.isdir(root):
return None
found: list[str] = []
for name in os.listdir(root):
cand = os.path.join(root, name, 'lib', 'windows')
if os.path.isfile(os.path.join(cand, 'libclang_rt.asan_dynamic-x86_64.dll.a')):
found.append(cand)
if not found:
hits: list[tuple[str, str]] = []
seen: set[str] = set()
for entry in os.environ.get('PATH', '').split(os.pathsep):
if not entry:
continue
for base in (entry, os.path.dirname(entry)):
if not base:
continue
root = os.path.join(base, 'lib', 'clang')
if root in seen or not os.path.isdir(root):
continue
seen.add(root)
for name in os.listdir(root):
cand = os.path.join(root, name, 'lib', 'windows')
if os.path.isfile(os.path.join(cand, _ASAN_DLL_A)):
hits.append((cand, os.path.join(base, 'bin')))
if not hits:
return None
def _Ver(path: str) -> int:
def _Ver(item: tuple[str, str]) -> int:
# 目录名是 LLVM 大版本号(如 "22")——按数值比,否则 "9" 会排在 "22" 之后
v = path.split(os.sep)[-3]
v = item[0].split(os.sep)[-3]
return int(v) if v.isdigit() else 0
found.sort(key=_Ver)
return found[-1]
hits.sort(key=_Ver)
return hits[-1]
# ---------- --traceback:地址→源行 旁文件(<exe>.stackmap)----------
@@ -1234,15 +1249,20 @@ class Phase2Translator:
# 环境变量在 worker 子进程/外部调用链下不可靠)。
# 检测 stdlib 堆/栈/全局越界与垃圾指针(MemBuddy pool 内部由 canary 覆盖)。
# 注意:mingw64 的 clang 不提供 x86_64-w64-windows-gnu 目标的 ASan runtime,
# 只能借用 clang64(MSVC ABI) 的那两份:PATH 前置 clang64/bin(运行期 DLL),
# 链接时显式 -l: 那两份 .a/.dll.a(见 _FindAsanRuntimeDir 的说明)。
# 只能借用 clang64(MSVC ABI) 的那两份:PATH 末尾追加 clang64/bin(运行期 DLL),
# 链接时显式 -l: 那两份 .a/.dll.a(见 _FindAsanRuntime 的说明)。
self.asan_mode = asan_mode
self.asan_rt_dir: str | None = None
if self.asan_mode:
_clang64_bin = r'D:\msys64\clang64\bin'
if os.path.isdir(_clang64_bin):
os.environ['PATH'] = _clang64_bin + os.pathsep + os.environ.get('PATH', '')
self.asan_rt_dir = _FindAsanRuntimeDir()
_rt = _FindAsanRuntime()
if _rt is not None:
self.asan_rt_dir, _asan_bin = _rt
# clang64/bin 必须**追加**到 PATH 末尾:既要让运行期能找到
# libclang_rt.asan_dynamic-x86_64.dll,又不能前置 —— 前置会让裸名
# clang++.exe / llc / opt 解析到 clang64 的 libc++ 版本,与 includes/
# 里 mingw64(libstdc++) 编的静态库链接不上。
if _asan_bin and os.path.isdir(_asan_bin):
os.environ['PATH'] = os.environ.get('PATH', '') + os.pathsep + _asan_bin
self.slice_level = 3
# 函数级 DWARF 调试元数据(--dwarf;--asan 时自动开启):False=未启用
# (零影响,不构造任何 DI 元数据);True=App 源模块的 .ll 携带
@@ -4905,9 +4925,11 @@ SECTIONS {
if self.asan_mode:
if self.asan_rt_dir is None:
_vlog().error(
"ASan 模式找不到 clang64 的 ASan 运行库目录 "
r"(D:\msys64\clang64\lib\clang\<ver>\lib\windows)\n"
" 请确认 msys2 的 mingw-w64-clang-x86_64-compiler-rt 已安装。")
"ASan 模式在 PATH 里找不到 clang64 的 ASan 运行库目录 "
"(<MSYS2>\\clang64\\lib\\clang\\<ver>\\lib\\windows\\"
"libclang_rt.asan_dynamic-x86_64.dll.a)\n"
" 请确认 msys2 的 mingw-w64-clang-x86_64-compiler-rt 已安装,"
"且 <MSYS2>\\clang64(或其 bin)已在 PATH 中。")
sys.exit(1)
lib_flags.append('-L' + self.asan_rt_dir)
lib_flags.append('-l:libclang_rt.asan_dynamic-x86_64.dll.a')