Round-21 pivot of the audit-059 synth-spawn module. Round 20 made the
silph::WorkerCtx workers run by attaching a 32-slot stub sub-vtable
where every entry was a `li r3, 0; blr` stub — workers spawned but
spun forever because slots 15/17 short-circuited to NULL ("no work").
Round 21 reads canary's real sub-vtable VA out of the XEX `.rdata` —
`0x8200A168` — and points `[sub_object + 0]` at it directly. The
vtable bytes live in the static image both engines map, so no guest
memory is consumed and slot 15 (= `sub_824FCCC8`) and slot 17
(= `sub_824FCE38`) — the only slots `sub_82506B08` ever calls —
become working game methods.
Discovery method (canary probes in
`audit-runs/audit-059-handle-disambiguation/round21-subvtable-canary/`):
1. `--audit_jit_prolog_pc=0x82506B08` to catch the first WorkerCtx
virtual-dispatch entry; `[r3+0x2C]` revealed the sub-object VA.
2. Re-run with `--audit_jit_prolog_mem_dump=<sub-obj VA>` to deref
`[sub-object + 0]` = sub-vtable VA = 0x8200A168.
3. PE inspection (`xex-text/xex-rdata` is the static image) reads
all 31 slots; slot 15 -> sub_824FCCC8, slot 17 -> sub_824FCE38.
Smoke metrics (50M instructions, `XENIA_CACHE_PERSIST=1
XENIA_SILPH_SYNTH=1`, audit-runs/audit-059-handle-disambiguation/
round21-real-vtable/):
* 4/4 workers spawned, no crash, no new fault
* KeSetEvent 633885 -> 431860 (-32%)
* KeWaitForSingleObject 258441 -> 185762 (-28%)
* Per-handle state unchanged on the focused stalled set
(0x1020/0x1090 still `<NO_SIGNALS_DESPITE_WAITS>`,
0x12a4/0x12ac/0x1218/0x1224 still `<UNCREATED>`).
* No VdSwap/draws progression observed in this window.
Verdict: B (partial). The workers no longer spin in a stub-loop —
internal call density shifted — but the focused wedge handles still
don't get signalled. Likely root cause: workers may now be waiting
on the WorkerCtx's own KEVENTs (which we synthesised at
+0x54/+0x94) for upstream work that no producer is enqueuing.
Net LOC: 29 ins / 31 del. Tests: workspace passes (lockstep app
tests, kernel 127/127, hir 288/288, scheduler 38/38).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Audit-059 round 19 isolated the round-18 worker fault: the four silph::
WorkerCtx worker bodies all execute the sequence
lwz r3, 44(rN) ; r3 = [ctx+0x2C] — sub-object pointer
lwz r11, 0(r3) ; r11 = sub-object vtable
lwz r11, 60(r11) ; r11 = sub-object vtable[15]
mtctr r11
bctrl
Ours left [ctx+0x2C] NULL → PC=0 fault on first virtual dispatch. Round 19
recommended materialising a sub-object whose vtable points entirely at an
existing trivial-return stub so workers idle live, returning NULL work,
without crashing.
Changes (silph_synth.rs only, +63/-6):
- Grow SILPH_CTX_SIZE 0x500 → 0x800 to embed sub-object at +0x300 and a
32-slot sub-vtable at +0x500 in the same heap_alloc.
- After ctx header init, write sub-object pointer at [ctx+0x2C], the XEX-
resident wrapper constant 0xBE568F00 (round-7 finding) at [ctx+0x30],
and leave [ctx+0x28] NULL (matches canary first-fire snapshot).
- Populate every slot of the 32-entry sub-vtable with VA 0x8216CAA4, the
first 4-byte-aligned standalone `li r3, 0; blr` stub located by a fresh
PE-text scan (preceded by a `blr` terminating the previous function).
- Sub-object body itself is zero-filled apart from the [+0]=vtable_ptr
write; round-19 disassembly confirms workers only touch slots 15/17.
Smoke (XENIA_SILPH_SYNTH=1, persistent cache, 5e7 instr):
- Lockstep: no crash, all 4 workers (tid=6/7/8/9) reach Ready in deep
worker-body PCs (0x825067xx/0x825089xx/0x825091xx). Verdict (D) —
workers run their idle loop returning NULL; existing silph waiters
(0x1020, 0x1090) remain <NO_SIGNALS_DESPITE_WAITS> because we
deliberately neutered productive work.
- Parallel: identical picture, no PC=0/PC=garbage fault anywhere.
No regression in 765-test suite.
Next round: feed real work-items into the intrusive ring at ctx+0x210
so workers' returned-NULL idle becomes returned-work productive; or
discover which sub-vtable slots actually need real callees (slot 15
worker drain, slot 17 producer).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds infrastructure to synthesise the silph::WorkerCtx that AUDIT-058/059
identified as never reached by ours' static-init chain (real chain entry
sits in audit-059 round 9's wrong-vtable wedge at sub_82172BA0+0x1E8).
Ctx layout follows round 5's live hexdump from canary:
+0x00 vtable = 0x8200A1E8
+0x04 self
+0x08 intrusive list head -> self
+0x0C init flag = 1
+0x10 packed byte field
+0x18 2x float ~1.0 (UI rates)
+0x24 flag = 1
+0x28..+0x30 3x foreign-arena pointers (left NULL — see below)
+0x54..+0x84 4x X_KEVENT auto-reset, state=0
+0x94..+0xC4 4x X_KEVENT manual-reset, state=1 (pre-signaled)
+0x210..+0x250 4-entry intrusive work-ring, empty
Worker spawn mirrors AUDIT-048's audio-worker pattern in
xaudio_register_render_driver: per-worker allocate_thread_image +
state.scheduler.spawn with r3 = ctx_ptr. Trigger fires at the first
dat/* VFS open (ours' earliest is dat/files.tbl), which is when canary
runs the equivalent chain.
ROUND 18 OUTCOME — opt-in only:
With workers spawned Ready (XENIA_SILPH_SYNTH=1), boot CRASHES at
cycle ~5.5M with PC=0 on hw=1, just after worker_3 (entry 0x825065B8)
spawns. Per task constraints this is STOP-and-report: the ctx fields
+0x28/+0x2C/+0x30 (foreign heap pointers — canary's 0x30057018,
0xBCE25640, 0xBE568F00, distinct arenas per audit-059 round 7) are
left NULL, and the worker bodies plausibly dereference one of them.
Synthesising those is a fresh investigation (round 19+).
With workers spawned Suspended (XENIA_SILPH_SYNTH=suspend), boot
completes normally (11 spawns, VdSwap=1, KeSetEvent=2,
KeReleaseSemaphore=1 — matches default baseline). The ctx remains
materialised in guest memory at the logged VA for downstream probing.
Default (env var unset): no synth, no regression.
Files:
crates/xenia-kernel/src/silph_synth.rs (new, 225 LOC)
crates/xenia-kernel/src/lib.rs (+1 LOC, register module)
crates/xenia-kernel/src/exports.rs (+37 LOC, hook in open_vfs_file)
crates/xenia-kernel/src/state.rs (+18 LOC, 4 silph_synth_* fields)
Tests: cargo test --release --workspace = 765 pass / 0 fail.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-07 20:44:29 +02:00
4 changed files with 336 additions and 0 deletions
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.