From 64f3d79b64cfd950672dffffa9b6931b5017f25e Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Wed, 7 Oct 2026 11:25:37 +0800 Subject: [PATCH] =?UTF-8?q?refactor(asan):=20ASan=20=E8=BF=90=E8=A1=8C?= =?UTF-8?q?=E5=BA=93=E7=9B=AE=E5=BD=95=E6=94=B9=E4=B8=BA=E6=8C=89=20PATH?= =?UTF-8?q?=20=E8=87=AA=E5=8A=A8=E6=8E=A2=E6=B5=8B=EF=BC=8C=E5=8E=BB?= =?UTF-8?q?=E9=99=A4=E6=9C=AC=E6=9C=BA=E7=A1=AC=E7=BC=96=E7=A0=81=E8=B7=AF?= =?UTF-8?q?=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - _FindAsanRuntimeDir() 重写为 _FindAsanRuntime():遍历 PATH 定位 - clang64/bin 由前置改为追加到 PATH,避免裸名 clang++/llc/opt 被解析到 clang64 的 libc++ 版本 - 缺失 ASan 运行库时的报错文案改用占位符路径 --- lib/Projectrans/Phase2Translator.py | 72 +++++++++++++++++++---------- 1 file changed, 47 insertions(+), 25 deletions(-) diff --git a/lib/Projectrans/Phase2Translator.py b/lib/Projectrans/Phase2Translator.py index 41a6850..3e99ba1 100644 --- a/lib/Projectrans/Phase2Translator.py +++ b/lib/Projectrans/Phase2Translator.py @@ -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**(/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, + 拼出 `/lib/clang/<大版本>/lib/windows`,命中即为链接库目录;对应的运行期 + DLL 目录取 `/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:地址→源行 旁文件(.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\\lib\windows)\n" - " 请确认 msys2 的 mingw-w64-clang-x86_64-compiler-rt 已安装。") + "ASan 模式在 PATH 里找不到 clang64 的 ASan 运行库目录 " + "(\\clang64\\lib\\clang\\\\lib\\windows\\" + "libclang_rt.asan_dynamic-x86_64.dll.a)\n" + " 请确认 msys2 的 mingw-w64-clang-x86_64-compiler-rt 已安装," + "且 \\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')