Files
xenia-rs/audit-runs/phase-c3-RtlImageXexHeaderField/fix.diff
MechaCat02 ef93a4fa14 handoff: VSync/event-wedge fixes + iterate 2.A–2.BC research notes
Source changes (dormant parity infra, retained from iterate 2.AI/2.AO):
- xenia-kernel/exports.rs: nt_create_event manual_reset polarity +
  related event wiring
- xenia-gpu/mmio_region.rs: D1MODE_VBLANK_VLINE_STATUS hardcode parity

Also lands the audit-runs/ analysis notes (.md/.txt/.json digests) for the
iterate 2.x VSync/0x10e8/0x1004 wedge investigation. Raw trace dumps
(.jsonl/.gz/.csv/.stdout) and agent worktrees (.claude/) are gitignored as
regenerable local artifacts — see memory + HANDOFF for the running findings.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 07:19:08 +02:00

152 lines
6.1 KiB
Diff

## Phase C+3 fix — RtlImageXexHeaderField
Three files changed; ~80 LOC net.
### `xenia-rs/crates/xenia-kernel/src/state.rs` (+13 LOC)
Add `xex_header_guest_ptr: u32` field to `KernelState`. Initialized to 0;
populated once at startup by `xenia-app` after copying raw XEX header
bytes into guest memory.
```diff
@@ -103,6 +103,17 @@
/// Image base of the loaded XEX (for XexExecutableModuleHandle etc.)
pub image_base: u32,
+ /// Guest VA of the raw XEX header bytes copied into guest memory at
+ /// startup (mirrors canary's `UserModule::guest_xex_header_`,
+ /// allocated in `user_module.cc:224`). Used by `RtlImageXexHeaderField`
+ /// to compute return values that are offsets into the in-guest header
+ /// copy (canary's `xboxkrnl_rtl.cc:501-514` calls `UserModule::Get
+ /// OptHeader(memory, header, key, &field_value)` which iterates
+ /// `header->headers[]` and returns `HostToGuestVirtual(header) +
+ /// opt_header.offset` for "else"-class keys, key low byte != 0/1). Zero
+ /// when the executable hasn't been installed yet. Set once by
+ /// `xenia-app` after `mem.write_bulk(base, &image_data)`.
+ pub xex_header_guest_ptr: u32,
/// `XEX_HEADER_SYSTEM_FLAGS` (key `0x00030000`) parsed from the loaded
/// XEX header. ...
@@ -330,6 +331,7 @@
image_base: 0,
+ xex_header_guest_ptr: 0,
xex_system_flags: 0,
```
### `xenia-rs/crates/xenia-kernel/src/exports.rs` (~50 LOC)
Replace stub `rtl_image_xex_header_field` (always returned 0) with a
proper implementation mirroring canary's `UserModule::GetOptHeader`
(`user_module.cc:335-369`). Walks the in-guest XEX header byte array
to find the matching key entry and returns the appropriate value per
the key's low-byte class (0x00 inline, 0x01 ptr-to-value, else
header-base+offset). Falls back to `state.xex_header_guest_ptr` when
the caller passes a NULL `xex_header` arg (the common ours-side case
because ours's `*XexExecutableModuleHandle = image_base` doesn't
resolve through a proper LDR_DATA_TABLE_ENTRY — see investigation.md
for why fixing that breaks Phase A alignment).
```diff
-fn rtl_image_xex_header_field(ctx: &mut PpcContext, _mem: &GuestMemory, _state: &mut KernelState) {
- // r3 = xex_header_ptr, r4 = field_id
- // Return 0 for all fields
- ctx.gpr[3] = 0;
-}
+fn rtl_image_xex_header_field(ctx: &mut PpcContext, mem: &GuestMemory, state: &mut KernelState) {
+ // r3 = xex_header_guest_ptr (may be NULL — game's CRT often passes 0
+ // because ours's `*XexExecutableModuleHandle = image_base` doesn't
+ // resolve to a real LDR_DATA_TABLE_ENTRY ...). When NULL, fall back
+ // to KernelState's recorded `xex_header_guest_ptr`.
+ // r4 = field_key (xex2_header_keys).
+ //
+ // Mirror of canary's `xboxkrnl_rtl.cc:501-514` →
+ // `UserModule::GetOptHeader(memory, header, key, &field_value)`.
+ let mut xex_header_ptr = ctx.gpr[3] as u32;
+ let field_key = ctx.gpr[4] as u32;
+ if xex_header_ptr == 0 {
+ xex_header_ptr = state.xex_header_guest_ptr;
+ }
+ if xex_header_ptr == 0 {
+ ctx.gpr[3] = 0;
+ return;
+ }
+ let header_count = mem.read_u32(xex_header_ptr.wrapping_add(0x14));
+ let entries_base = xex_header_ptr.wrapping_add(0x18);
+ let mut field_value: u32 = 0;
+ let mut found = false;
+ for i in 0..header_count {
+ let entry_addr = entries_base.wrapping_add(i.wrapping_mul(8));
+ let entry_key = mem.read_u32(entry_addr);
+ if entry_key != field_key {
+ continue;
+ }
+ found = true;
+ let entry_value_addr = entry_addr.wrapping_add(4);
+ match entry_key & 0xFF {
+ 0x00 => { field_value = mem.read_u32(entry_value_addr); }
+ 0x01 => { field_value = entry_value_addr; }
+ _ => {
+ let offset = mem.read_u32(entry_value_addr);
+ field_value = xex_header_ptr.wrapping_add(offset);
+ }
+ }
+ break;
+ }
+ if !found {
+ ctx.gpr[3] = 0;
+ return;
+ }
+ ctx.gpr[3] = field_value as u64;
+}
```
### `xenia-rs/crates/xenia-app/src/main.rs` (+15 LOC)
In the variable-export patcher for ordinal `0x0193`
(`XexExecutableModuleHandle`), keep `*XexExecutableModuleHandle = base`
(don't disturb the CRT's early branch) but additionally allocate
guest memory for the raw XEX header bytes, copy them in via
`mem.write_bulk`, and record the guest VA in
`kernel.xex_header_guest_ptr` for the new
`rtl_image_xex_header_field` implementation to use as a fallback.
```diff
("xboxkrnl.exe", 0x0193) => {
- // XexExecutableModuleHandle -> image base
- mem.write_u32(addr, base);
+ // (long comment block)
+ let header_size = header.header_size as usize;
+ if header_size > 0 && header_size <= data.len() {
+ let xex_va = alloc_zero(header.header_size, &mut mem, &mut kernel);
+ if xex_va != 0 {
+ mem.write_bulk(xex_va, &data[0..header_size]);
+ kernel.xex_header_guest_ptr = xex_va;
+ }
+ }
+ mem.write_u32(addr, base);
}
```
### `xenia-rs/tools/diff-events/diff_events.py` (+13 LOC)
Add `RtlImageXexHeaderField` to the `ALLOCATOR_RETURN_FNS`
canonicalization set. The function's return value for "else"-class keys
is a guest VA inside the engine's in-guest XEX header copy, which is
allocated at host-allocator-dependent addresses (canary's
`SystemHeapAlloc` lands in `0x30xxxxxx`; ours's `KernelState::heap_alloc`
lands in `0x4xxxxxxx`). Per-(tid, name) ordinal sentinels mask this VA
divergence (same pattern as Phase C+2's allocator canonicalization).
```diff
ALLOCATOR_RETURN_FNS = frozenset(
[
"MmAllocatePhysicalMemoryEx",
"MmAllocatePhysicalMemory",
"NtAllocateVirtualMemory",
"RtlAllocateHeap",
"MmCreateKernelStack",
+ # Phase C+3: `RtlImageXexHeaderField` returns ... (see source).
+ "RtlImageXexHeaderField",
]
)
```