Files
WeirdUtils/.github/workflows/test-safe-core-v8.yml
T

236 lines
9.1 KiB
YAML

name: Test safe standalone core V8
on:
push:
branches:
- test/safe-core-v8
workflow_dispatch:
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: mlugg/setup-zig@v2
with:
version: 0.16.0
- name: Build safe standalone variants
shell: bash
run: |
set -euo pipefail
zig build all-variants -Doptimize=ReleaseSmall \
-Dweirdperformance=false \
-Doutline=true
- name: Verify minimal core layout
shell: bash
run: |
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
root = Path("zig-out/variants")
addr = {
"lua_protection": 0x42A320,
"register_commands": 0x490250,
"glue_commands": 0x46ABB0,
"engine_init": 0x46A400,
"logout": 0x491180,
"shutdown": 0x490BD0,
"file_fallback": 0x648620,
"open_file": 0x6477C0,
"file_size": 0x6487F0,
"read_file": 0x648460,
"cleanup_file": 0x648730,
"model_load": 0x71D4E0,
"check_file": 0x654DD0,
}
def has(data, value):
return value.to_bytes(4, "little") in data
outline = (root / "outline.dll").read_bytes()
custom = (root / "customassets.dll").read_bytes()
transmog = (root / "transmogfix.dll").read_bytes()
# Outline: only its Lua registration + engine init core hooks are allowed.
required_outline = ("register_commands", "engine_init")
forbidden_outline = tuple(k for k in addr if k not in required_outline)
for k in required_outline:
if not has(outline, addr[k]):
raise SystemExit(f"outline.dll missing required {k}")
bad = [k for k in forbidden_outline if has(outline, addr[k])]
if bad:
raise SystemExit(f"outline.dll still contains unwanted core hooks: {bad}")
print("outline.dll: minimal core verified")
# CustomAssets: only CheckFileExistence is allowed from the generic core.
if not has(custom, addr["check_file"]):
raise SystemExit("customassets.dll missing CheckFileExistence")
bad = [k for k,v in addr.items() if k != "check_file" and has(custom, v)]
if bad:
raise SystemExit(f"customassets.dll still contains unwanted core hooks: {bad}")
print("customassets.dll: minimal core verified")
# TransmogFix: none of the generic core hook target addresses should remain.
bad = [k for k,v in addr.items() if has(transmog, v)]
if bad:
raise SystemExit(f"transmogfix.dll still contains unwanted core hooks: {bad}")
print("transmogfix.dll: minimal core verified")
# Compatibility repair must exist in customassets and outline.
for name, data in (("outline.dll", outline), ("customassets.dll", custom)):
for gate in (0x654B5C, 0x654B6A):
if not has(data, gate):
raise SystemExit(f"{name} missing MPQ gate restore address 0x{gate:X}")
for seq in (bytes([0x74,0x25]), bytes([0x75,0x17])):
if seq not in data:
raise SystemExit(f"{name} missing original MPQ branch bytes {seq.hex()}")
print("legacy MPQ gate repair verified")
PY
- name: Verify Outline registration + Lua ABI
shell: bash
run: |
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
main = Path("src/main.zig").read_text()
outline = Path("src/outline/outline.zig").read_text()
for required in (
'noinline fn registerFunction',
'0x704120',
'"{ecx}" (@intFromPtr(name))',
'"{edx}" (func_addr)',
):
if required not in main:
raise SystemExit(f"Missing explicit FrameScript registration ABI piece: {required}")
for required in (
'luaGetTopNative',
'luaIsStringNative',
'luaToStringNative',
'luaPushBooleanNative',
'"{ecx}"',
'"{edx}"',
'0x6F39F0',
'callconv(.{ .x86_thiscall = .{} })',
):
if required not in outline:
raise SystemExit(f"Missing Outline callback/native wrapper ABI piece: {required}")
print("source: FrameScript registration uses ECX/EDX; Outline callback receives L in ECX")
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()
reg_block = None
for i, line in enumerate(lines):
if re.search(r"mov\s+eax,0x0*704120\b", line, re.I):
block = lines[max(0, i - 8):i + 4]
text = "\n".join(block)
# The helper must explicitly prepare both fastcall registers
# before calling FrameScript_RegisterFunction.
if (re.search(r"mov\s+ecx,", text, re.I)
and re.search(r"mov\s+edx,", text, re.I)
and re.search(r"call\s+eax\b", text, re.I)):
reg_block = text
break
if reg_block is None:
raise SystemExit("BAD ABI: no FrameScript_RegisterFunction call with ECX=name and EDX=func found")
print("FrameScript_RegisterFunction machine code:")
print(reg_block)
# Regression guard for the exact broken V7/V8 codegen:
# mov eax,0x704120
# push callback
# push name
# call eax
for i, line in enumerate(lines):
if re.search(r"mov\s+eax,0x0*704120\b", line, re.I):
after = "\n".join(lines[i:i + 5])
if len(re.findall(r"\bpush\s+", after, re.I)) >= 2:
raise SystemExit("BAD ABI: FrameScript registration still passes name/func on the stack")
# OutlineCommand must capture the Lua state from ECX. Search function
# prologues for the characteristic saved-ESI form and reject the old
# [ebp+8] capture used by the cdecl experiment.
good_callback = False
for i, line in enumerate(lines):
if re.search(r"push\s+ebp\b", line, re.I):
block = "\n".join(lines[i:i + 12])
if re.search(r"mov\s+esi,ecx\b", block, re.I):
good_callback = True
break
if not good_callback:
raise SystemExit("BAD ABI: no callback body captures Lua state from ECX")
print("outline.dll machine code: registration and callback ABIs verified")
PY
- name: Stage package
shell: bash
run: |
set -euo pipefail
mkdir -p package/DLL
mkdir -p package/Interface/AddOns/WeirdUtils_Outline
cp zig-out/variants/outline.dll package/DLL/
cp zig-out/variants/customassets.dll package/DLL/
cp zig-out/variants/transmogfix.dll package/DLL/
cp src/outline/addon/Outline.lua package/Interface/AddOns/WeirdUtils_Outline/
cp src/outline/addon/Bindings.xml package/Interface/AddOns/WeirdUtils_Outline/
cp src/outline/addon/WeirdUtils_Outline.toc package/Interface/AddOns/WeirdUtils_Outline/
cat > package/README_TEST.txt <<'EOF'
WeirdUtils Safe Standalone Core Test V8
This build fixes both x86 ABIs: FrameScript_RegisterFunction gets name in ECX + callback in EDX, and OutlineCommand receives the Lua state in ECX. WoW Lua API calls stay on explicit native register wrappers.
outline.dll
- Keeps only the Player_LoadScriptFunctions hook required to register OutlineCommand.
- Keeps only the GameEngine_MainInitialize hook required to initialise the renderer.
- Does not register the shared WeirdUtils version table.
- Does not install generic MPQ/file hooks.
- Uses the external WeirdUtils_Outline addon included here.
customassets.dll
- Keeps only CheckFileExistence from the shared file core.
- No shared Lua registration hooks.
- No shared engine/logout/shutdown hooks.
- Repairs legacy File_FindInArchive NOPs if present.
transmogfix.dll
- Installs only TransmogFix's own module hooks.
- No shared Lua/file/engine/logout/shutdown hooks.
weirdperformance.dll
- Not built here.
- The final ZIP will use the user's original binary unchanged.
EOF
sha256sum package/DLL/*.dll > package/SHA256SUMS.txt
- uses: actions/upload-artifact@v4
with:
name: safe-core-v8
path: package/
if-no-files-found: error