From 53c72ff57a5b745f47530b1652f6e38e85949d41 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Wed, 26 Aug 2026 18:56:37 +0800 Subject: [PATCH] =?UTF-8?q?fix(callgen):=20=E5=8C=85=E5=AD=90=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E9=99=90=E5=AE=9A=E8=B0=83=E7=94=A8=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E7=A5=96=E5=85=88=E5=8C=85=E5=AF=BC=E5=85=A5=E5=88=A4=E5=AE=9A?= =?UTF-8?q?=EF=BC=8C=E6=9D=9C=E7=BB=9D=E5=90=8C=E5=90=8D=E5=87=BD=E6=95=B0?= =?UTF-8?q?=E5=8A=AB=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit main.vp IR 显示 intr.gdt.init() 解析成 @3b3cc38844ed7ad9.init(serial.vp)、 intr.idt.init() 解析成 @c418c6220a9a9d2a.init(su8250.vp,还带伪造 i16 实参) ——intr 包限定的跨模块调用因 is_module_imported('intr.gdt')=0 跳过校验, find_func_in_module 后缀匹配劫持最先注册的同名 init → GDT/IDT 从未初始化, CPU 一直用 OVMF 的 IDT(int 0x20 → OVMF #GP 0xEB6810C,定时中断永不到达)。 新增 ImportsGen.is_module_imported_or_known:已导入 或 全局已登记且任一祖先 包已导入(import intr → intr.gdt 可访问;this.this() 未 import this 仍 fast-fail)。 替换 CallGenMixin 模块限定解析 5 处门控(构造单层/多层、found_func 校验、 跨模块调用 import 检查)。 --- App/lib/core/Gen/CallGenMixin.vp | 8 +-- App/lib/core/Gen/ImportsGenMixin.vp | 101 +++++++++++++++++++++++----- 2 files changed, 89 insertions(+), 20 deletions(-) diff --git a/App/lib/core/Gen/CallGenMixin.vp b/App/lib/core/Gen/CallGenMixin.vp index 956211c..9c4a418 100644 --- a/App/lib/core/Gen/CallGenMixin.vp +++ b/App/lib/core/Gen/CallGenMixin.vp @@ -242,7 +242,7 @@ class CallGenMixin(CallGenBase.CallGenBase): elif at_qc0.value is not None and at_qc0.value.kind() == ast.ASTKind.Attribute: mod_path0 = self.Gen.ExprGenAttr._attr_module_path_str(pool, at_qc0.value) if mod_path0 is not None: - if self.Gen.ImportsGen.is_module_imported(trans._imported_modules, mod_path0) != 0: + if self.Gen.ImportsGen.is_module_imported_or_known(trans._imported_modules, mod_path0) != 0: mod_sha1_0: str = self._lookup_module_sha1(mod_path0) if mod_sha1_0 is not None: gen_ctor_entry = self.Gen.StructGen.find_struct_by_module( @@ -260,7 +260,7 @@ class CallGenMixin(CallGenBase.CallGenBase): at_qc: ast.Attribute | t.CPtr = (ast.Attribute | t.CPtr)(cl.func) if at_qc.value is not None and at_qc.value.kind() == ast.ASTKind.Name: mod_nm_qc: ast.Name | t.CPtr = (ast.Name | t.CPtr)(at_qc.value) - imp_ok: int = self.Gen.ImportsGen.is_module_imported(trans._imported_modules, mod_nm_qc.id) + imp_ok: int = self.Gen.ImportsGen.is_module_imported_or_known(trans._imported_modules, mod_nm_qc.id) if imp_ok != 0: # 模块限定构造 Module.Class(...):按模块名解析 SHA1 精确命中类。 # 不能用 trans.ModuleSha1(那是当前文件 sha1,类在 gfx 模块)—— @@ -622,7 +622,7 @@ class CallGenMixin(CallGenBase.CallGenBase): xm_mod_path = self.Gen.ExprGenAttr._attr_module_path_str( pool, xm_at.value) if xm_mod_path is not None: - if self.Gen.ImportsGen.is_module_imported( + if self.Gen.ImportsGen.is_module_imported_or_known( trans._imported_modules, xm_mod_path) != 0: mod_sha1_chk: str = self._lookup_module_sha1(xm_mod_path) if mod_sha1_chk is None and trans._from_imports is not None: @@ -1064,7 +1064,7 @@ class CallGenMixin(CallGenBase.CallGenBase): # 静默生成 @{sha1}.func 调用而非报错——违反"找不到 fast-fail"原则。 is_xmod_imported: int = 1 if trans is not None and trans._imported_modules is not None: - is_xmod_imported = self.Gen.ImportsGen.is_module_imported( + is_xmod_imported = self.Gen.ImportsGen.is_module_imported_or_known( trans._imported_modules, xmod_path) if is_xmod_imported == 0: err_ni: str = pool.alloc(256) diff --git a/App/lib/core/Gen/ImportsGenMixin.vp b/App/lib/core/Gen/ImportsGenMixin.vp index 79208b4..8e4b7b6 100644 --- a/App/lib/core/Gen/ImportsGenMixin.vp +++ b/App/lib/core/Gen/ImportsGenMixin.vp @@ -117,6 +117,45 @@ class ImportsGenMixin: return 1 return 0 + # ============================================================ + # 检查模块是否已导入或全局已登记(包子模块限定解析用) + # ============================================================ + def is_module_imported_or_known(self, imported_modules: str, name: str) -> int: + """已导入或可访问的模块(包子模块限定解析用)。 + + 包子模块场景:`import intr` 只注册包名 "intr",但 intr.gdt / intr.idt + 等子模块由包 __init__.vp 的 from . import X 在全局注册——模块限定 + 调用/属性访问(intr.gdt.init())的 is_module_imported 校验必须放行, + 否则解析退化到函数名后缀匹配劫持其他模块同名函数 + (serial.init 劫持 intr.gdt.init → GDT/IDT 未初始化根因)。 + + 放行条件(保持单层名 fast-fail 语义不变): + 1. 名字已在 _imported_modules(直接 import); + 2. 名字在 Sha1Store 全局已登记,且其任一祖先包名已导入 + (import intr → intr.gdt 可访问;`this.this()` 但 import this + 被注释 → this 未导入 → 仍 fast-fail 报"模块未导入")。 + """ + if self.is_module_imported(imported_modules, name) != 0: + return 1 + if name is None: + return 0 + if StubMergerStore.LookupSha1ByModName(name) is None: + return 0 + nl: t.CSizeT = string.strlen(name) + dot_pos: t.CSizeT = 0 + while dot_pos < nl: + if name[dot_pos] == '.': + pfx: str = stdlib.malloc(dot_pos + 1) + if pfx is not None: + string.strncpy(pfx, name, dot_pos) + pfx[dot_pos] = '\0' + hit: int = self.is_module_imported(imported_modules, pfx) + stdlib.free(pfx) + if hit != 0: + return 1 + dot_pos += 1 + return 0 + # ============================================================ # 添加 from-import 名称映射 # ============================================================ @@ -565,7 +604,7 @@ class ImportsGenMixin: path_buf: bytes = stdlib.malloc(path_len + 1) if path_buf is None: return 0 - # .vp 文件 + # .vp 文件(Viper 源仅 .vp,不再回退 .py) string.strncpy(path_buf, cur, sep_pos) path_buf[sep_pos] = '\0' string.strcat(path_buf, mod_name) @@ -575,16 +614,6 @@ class ImportsGenMixin: fex.close() stdlib.free(path_buf) return 1 - # .py 文件(Python 模块可能无 .vp) - string.strncpy(path_buf, cur, sep_pos) - path_buf[sep_pos] = '\0' - string.strcat(path_buf, mod_name) - string.strcat(path_buf, ".py") - fex2: fileio.File | t.CPtr = fileio.File(path_buf, fileio.MODE.R) - if not fex2.closed: - fex2.close() - stdlib.free(path_buf) - return 1 stdlib.free(path_buf) return 0 @@ -670,7 +699,7 @@ class ImportsGenMixin: else: # 无别名时(import a.b.c),将顶层包名 "a" 也加入 _imported_modules, # 使 a.b.c 作为属性访问的 base 被翻译时能通过 is_module_imported("a") 检查 - dot_ptr: str = string.strstr(alias.name, '.') + dot_ptr: str = string.strstr(alias.name, ".") if dot_ptr is not None: pkg_len: t.CSizeT = t.CSizeT(t.CUInt64T(dot_ptr) - t.CUInt64T(alias.name)) if pkg_len > 0 and pkg_len < 256: @@ -706,6 +735,29 @@ class ImportsGenMixin: self._check_import_sha1(node, resolved) trans._imported_modules = self.add_imported_module( trans.Pool, trans._imported_modules, resolved) + # from . import Y(impf.module is None):resolved 是包名(vpsdk), + # 各 name 是包的子模块 vpsdk.Y。须把完整子模块名也加入 + # _imported_modules,否则别名 "syscall" 精确匹配不到包名 "vpsdk" + # → is_module_imported 返回 0 → syscall._syscall1 / syscall.LOAD_SO + # 跨模块解析失败(vpsdk/dynlib.vp 根因)。 + if impf.module is None: + names_m: list[ast.AST | t.CPtr] | t.CPtr = impf.names + if names_m is not None: + nn_m: t.CSizeT = len(names_m) + for ni_m in range(nn_m): + alias_m: ast.Alias | t.CPtr = (ast.Alias | t.CPtr)(names_m.get(ni_m)) + if alias_m is not None and alias_m.name is not None: + rl_m: t.CSizeT = string.strlen(resolved) + rn_m: t.CSizeT = string.strlen(alias_m.name) + jb_m: str = trans.Pool.alloc(rl_m + rn_m + 2) + if jb_m is not None: + string.strcpy(jb_m, resolved) + jb_m[rl_m] = '.' + string.strcpy(jb_m + rl_m + 1, alias_m.name) + if trans._declare_only == 0: + self._check_import_sha1(node, jb_m) + trans._imported_modules = self.add_imported_module( + trans.Pool, trans._imported_modules, jb_m) return 0 # ============================================================ @@ -737,15 +789,32 @@ class ImportsGenMixin: if alias.asname is not None: local_name = alias.asname orig_name = alias.name + # from . import Y(impf.module is None):_resolve_relative_module + # 返回的是包名(如 vpsdk),但 Y 是包的子模块 vpsdk.Y + # (vpsdk/syscall.vp)。此前直接映射 local_name → 包名, + # 导致 syscall.LOAD_SO 跨模块 CDefine / syscall._syscall1 + # 跨模块调用在包里找不到(vpsdk/dynlib.vp `from . import + # syscall as syscall` 根因)。 + fi_target: str = resolved + if impf.module is None and resolved is not None: + fl_t: t.CSizeT = string.strlen(resolved) + fn_t: t.CSizeT = string.strlen(alias.name) + jb_t: str = trans.Pool.alloc(fl_t + fn_t + 2) + if jb_t is not None: + string.strcpy(jb_t, resolved) + jb_t[fl_t] = '.' + string.strcpy(jb_t + fl_t + 1, alias.name) + fi_target = jb_t trans._from_imports = self.add_from_import( trans.Pool, trans._from_imports, - local_name, resolved, orig_name) + local_name, fi_target, orig_name) # 命名空间隔离:from-import 的名称标记为可见结构体 self.Gen.StructGen.add_visible_struct(trans.Pool, local_name) - # 注册 re-export 映射:当前模块 re-export 了 local_name,源模块为 resolved - # 使跨模块调用 module.func() 能解析到正确的源模块 SHA1 + # 注册 re-export 映射:当前模块 re-export 了 local_name, + # 源模块为 fi_target(from . import Y 时是子模块名 vpsdk.Y, + # 非包名)。使跨模块调用 module.func() 能解析到正确 SHA1 if trans.ModuleSha1 is not None: - self.register_reexport(trans.ModuleSha1, local_name, resolved) + self.register_reexport(trans.ModuleSha1, local_name, fi_target) return 0 def InitImportsGen(self) -> int: