From a55f43a5d5f0aa3c2a3f98b6eff284a47564ded4 Mon Sep 17 00:00:00 2001 From: Dusk-92 Date: Fri, 28 Aug 2026 17:27:43 +0200 Subject: [PATCH] Verify V8 OutlineCommand ECX ABI in machine code --- .github/workflows/test-safe-core-v8.yml | 54 +++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test-safe-core-v8.yml b/.github/workflows/test-safe-core-v8.yml index 235be49..c13ec78 100644 --- a/.github/workflows/test-safe-core-v8.yml +++ b/.github/workflows/test-safe-core-v8.yml @@ -95,7 +95,7 @@ jobs: print("legacy MPQ gate repair verified") PY - - name: Verify Outline native Lua asm wrappers + - name: Verify Outline Lua ABI and native wrappers shell: bash run: | set -euo pipefail @@ -110,10 +110,56 @@ jobs: '"{ecx}"', '"{edx}"', '0x6F39F0', + 'callconv(.{ .x86_thiscall = .{} })', ): if required not in src: - raise SystemExit(f"Missing native register wrapper piece: {required}") - print("outline.dll source: native x86 register wrappers present") + raise SystemExit(f"Missing Outline ABI/native wrapper piece: {required}") + print("outline.dll source: ECX callback ABI + native x86 register wrappers present") + PY + + objdump -d -Mintel zig-out/variants/outline.dll > /tmp/outline.disasm + python3 - <<'PY' + import re + from pathlib import Path + + lines = Path("/tmp/outline.disasm").read_text(errors="replace").splitlines() + + # registerFunction("OutlineCommand", &outlineCommand) compiles near a + # mov eax,0x704120 followed by push ; push ; call eax. + callback = None + for i, line in enumerate(lines): + if re.search(r"mov\s+eax,0x0*704120\b", line, re.I): + pushes = [] + for nxt in lines[i + 1:i + 8]: + m = re.search(r"\bpush\s+0x([0-9a-f]+)\b", nxt, re.I) + if m: + pushes.append(int(m.group(1), 16)) + if len(pushes) >= 2: + callback = pushes[0] + break + + if callback is None: + raise SystemExit("Could not resolve OutlineCommand callback address from registration code") + + start = None + addr_re = re.compile(rf"^\s*{callback:x}:\s", re.I) + for i, line in enumerate(lines): + if addr_re.search(line): + start = i + break + if start is None: + raise SystemExit(f"Could not find callback body at 0x{callback:X}") + + block = "\n".join(lines[start:start + 14]) + print(f"OutlineCommand callback @ 0x{callback:X}") + print(block) + + if not re.search(r"mov\s+esi,ecx\b", block, re.I): + raise SystemExit("BAD ABI: OutlineCommand does not capture Lua state from ECX") + if re.search(r"mov\s+esi,(?:DWORD PTR )?\[ebp\+0x8\]", block, re.I): + raise SystemExit("BAD ABI: OutlineCommand still reads Lua state from [ebp+8]") + + print("outline.dll machine code: Lua state is captured from ECX") PY - name: Stage package @@ -134,7 +180,7 @@ jobs: cat > package/README_TEST.txt <<'EOF' WeirdUtils Safe Standalone Core Test V8 - This build forces OutlineCommand's WoW Lua calls through x86 register assembly wrappers. + This build receives OutlineCommand's Lua state from ECX (x86 thiscall ABI) and keeps the native x86 register wrappers for WoW Lua calls. outline.dll - Keeps only the Player_LoadScriptFunctions hook required to register OutlineCommand.