diff --git a/App/lib/Projectrans/Config.vp b/App/lib/Projectrans/Config.vp index 1dedb5b..2d845da 100644 --- a/App/lib/Projectrans/Config.vp +++ b/App/lib/Projectrans/Config.vp @@ -40,6 +40,17 @@ StrictMode: t.CInt = 0 IncludesDir: str = None # includes 目录路径(从 includes 数组首元素,兼容单目录场景) IncludesDirs: str = None # 全部 includes 目录(空格分隔,多 includes 目录支持) +# includes 预编译缓存目录后缀(asan 模式用 .binary_asan 隔离,避免 +# asan 插桩 obj 与非 asan obj 混链:asan obj 引用 __asan_init 等符号, +# 非 asan 链接不提供 asan 库 → undefined reference) +_inc_bin_suffix: str = ".binary" + + +def set_inc_bin_suffix(suffix: str) -> None: + """设置 includes.binary 缓存目录后缀(如 ".binary_asan")""" + global _inc_bin_suffix + _inc_bin_suffix = suffix + def Load_project_config(path: str) -> int: """加载工程配置文件(project.vpj) @@ -451,17 +462,20 @@ def resolve_paths(project_dir: str) -> int: def get_includes_binary_dir() -> str: - """获取 includes.binary 目录路径(includes 目录 + .binary 后缀) + """获取 includes.binary 目录路径(includes 目录 + 缓存后缀) - includes 目录如 ../includes,则 includes.binary 为 ../includes.binary + includes 目录如 ../includes,默认返回 ../includes.binary; + asan 模式(set_inc_bin_suffix(".binary_asan"))返回 ../includes.binary_asan, + 隔离 asan 插桩 obj,避免与非 asan obj 混链。 """ if IncludesDir is None: return None inc_len: t.CSizeT = string.strlen(IncludesDir) - buf: str = _mbuddy.alloc(inc_len + 8) + suf_len: t.CSizeT = string.strlen(_inc_bin_suffix) + buf: str = _mbuddy.alloc(inc_len + suf_len + 1) if buf is None: return None - viperlib.snprintf(buf, inc_len + 8, "%s.binary", IncludesDir) + viperlib.snprintf(buf, inc_len + suf_len + 1, "%s%s", IncludesDir, _inc_bin_suffix) return buf diff --git a/App/lib/core/BuildPipeline.vp b/App/lib/core/BuildPipeline.vp index c131fef..d53a060 100644 --- a/App/lib/core/BuildPipeline.vp +++ b/App/lib/core/BuildPipeline.vp @@ -552,14 +552,18 @@ def link_obj_to_exe(output_dir: str, module_name: str, linker_cmd: str, linker_f # 构造命令: clang++ {obj_path} extra_objs -o {output_dir}/{linker_output} linker_flags # 注意: .obj 文件必须在 -l 库标志之前,否则链接器无法解析符号依赖 - # ASan 模式:追加 -fsanitize=address 链接 libasan(配合 opt 插桩) + # ASan 模式:mingw64 clang 的 -fsanitize=address 在 mingw64/lib 下找不到 + # asan 运行库(实际在 clang64),改为显式链接 clang64 的 libclang_rt + # (DLL 版,运行时需 D:/msys64/clang64/bin 在 PATH) use_flags: str = linker_flags if _asan_mode != 0: - fl_len: t.CSizeT = string.strlen(linker_flags) + 32 + fl_len: t.CSizeT = string.strlen(linker_flags) + 256 use_flags = _mbuddy.alloc(fl_len) if use_flags is None: return 1 - viperlib.snprintf(use_flags, fl_len, "%s -fsanitize=address", linker_flags) + viperlib.snprintf(use_flags, fl_len, + "%s -L D:/msys64/clang64/lib/clang/22/lib/windows -l:libclang_rt.asan_dynamic-x86_64.dll.a -l:libclang_rt.asan_dynamic_runtime_thunk-x86_64.a", + linker_flags) cmd_len: t.CSizeT = string.strlen(linker_cmd) + string.strlen(obj_path) + string.strlen(use_flags) + string.strlen(output_dir) + string.strlen(linker_output) + extra_len + 128 cmd: bytes = _mbuddy.alloc(cmd_len) if cmd is None: @@ -671,14 +675,16 @@ def link_objs_to_exe(obj_paths: str, obj_paths_len: t.CSizeT, extra_len = collect_obj_files(includes_binary_dir, extra_objs, EXTRA_BUF_SIZE) # 构造命令: clang++ obj_paths extra_objs -o linker_output linker_flags - # ASan 模式:追加 -fsanitize=address 链接 libasan(配合 opt 插桩) + # ASan 模式:显式链接 clang64 的 libclang_rt(同 link_obj_to_exe) use_flags: str = linker_flags if _asan_mode != 0: - fl_len: t.CSizeT = string.strlen(linker_flags) + 32 + fl_len: t.CSizeT = string.strlen(linker_flags) + 256 use_flags = _mbuddy.alloc(fl_len) if use_flags is None: return 1 - viperlib.snprintf(use_flags, fl_len, "%s -fsanitize=address", linker_flags) + viperlib.snprintf(use_flags, fl_len, + "%s -L D:/msys64/clang64/lib/clang/22/lib/windows -l:libclang_rt.asan_dynamic-x86_64.dll.a -l:libclang_rt.asan_dynamic_runtime_thunk-x86_64.a", + linker_flags) cmd_len: t.CSizeT = string.strlen(linker_cmd) + obj_paths_len + string.strlen(use_flags) + string.strlen(linker_output) + extra_len + 128 cmd: bytes = _mbuddy.alloc(cmd_len) if cmd is None: diff --git a/App/main.vp b/App/main.vp index 8267b3b..5bb9175 100644 --- a/App/main.vp +++ b/App/main.vp @@ -236,23 +236,26 @@ def main() -> int: stdio.printf("\n参数解析完成。\n") - # ASan 模式:设置 BuildPipeline 全局标志(compile_ll_to_obj 先 opt 插桩,链接加 -fsanitize=address) + # ASan 模式:设置 BuildPipeline 全局标志(compile_ll_to_obj 先 opt 插桩,链接 asan 库) + # includes 缓存用独立目录 .binary_asan 隔离 asan 插桩 obj,避免与非 asan obj 混链 + # (asan obj 引用 __asan_init 等符号,非 asan 链接不提供 asan 库 → undefined reference) if args.get_bool("asan"): BuildPipeline.set_asan_mode(1) + Config.set_inc_bin_suffix(".binary_asan") if log is not None: - log.info("ASan 模式已启用(opt -passes=asan + -fsanitize=address)", "asan") - # ASan 模式强制重建 includes.binary:缓存 obj 未做 asan 插桩, - # 与非 asan obj 混链会导致 ASan 运行时崩溃 + log.info("ASan 模式已启用(opt -passes=asan + libclang_rt)", "asan") + # ASan 模式清理 asan includes 缓存(强制重建 asan 插桩 obj) mf_inc_bin_asan: str = Config.get_includes_binary_dir() if mf_inc_bin_asan is not None: n_asan: int = Utils.CleanDir(mf_inc_bin_asan) if log is not None: fb_asan: t.CChar | t.CPtr = VLogger.fmt_buf() if fb_asan is not None: - viperlib.snprintf(fb_asan, 1024, "ASan 模式清理 includes.binary: %d 个文件(将重建 asan obj)", n_asan) + viperlib.snprintf(fb_asan, 1024, "ASan 模式清理 includes 缓存: %d 个文件(将重建 asan obj)", n_asan) log.info(fb_asan, "asan") else: BuildPipeline.set_asan_mode(0) + Config.set_inc_bin_suffix(".binary") # 加载 project.vpj 配置 proj_loaded: int = 0