From 0120677722b66d12e44b3a4961524636bcf3f38d Mon Sep 17 00:00:00 2001 From: Sylpheed RE agent Date: Tue, 25 Aug 2026 07:36:56 +0000 Subject: [PATCH] =?UTF-8?q?re:=20freeze=20frontier=20=E2=80=94=20wait=20co?= =?UTF-8?q?nfirmed=20by=20ordinal,=20but=20info=20args=20will=20not=20work?= =?UTF-8?q?=20here?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two corrections to the plan the freeze file leaves for the next pass, both cheap to establish and both worth having before a run is spent discovering them. The export in the frozen frame is confirmed. The shim frame carries its ordinal as a template parameter, 176 decimal, which is 0xB0, and xboxkrnl_table.inc line 190 maps that to KeWaitForSingleObject. The identification was previously read off frame names; it is now pinned by ordinal. The file's stated next step does not work on this binary. It says the wait target is one info-args-equivalent away now that a debugger can be attached, but readelf shows zero debug sections and only a symtab of 27807 entries. The Release build is not stripped, which is why frames carry function names, but it has no debug info at all, so gdb can name a frame and nothing inside it: no info args, no p *this, no member access. The earlier note conflated "not stripped" with "has debug info". Two routes remain and their costs are now recorded. A prologue-guided stack read can recover XObject::Wait's spilled this pointer from a known stack slot, needing no rebuild and keeping the oracle byte-identical to the one every other measurement in this corpus used, at the price of per-frame archaeology that must be redone if the binary changes. Alternatively a RelWithDebInfo build via build-canary makes this and every future freeze question straightforward, at the price of a full compile and a binary that differs from the measured one. Neither is attempted here. Recorded so the next pass picks with the costs visible. --- docs/re/BACKLOG.md | 12 ++++++ docs/re/mission-freeze-resume-spin.md | 57 +++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/docs/re/BACKLOG.md b/docs/re/BACKLOG.md index 8b36e1f..543921a 100644 --- a/docs/re/BACKLOG.md +++ b/docs/re/BACKLOG.md @@ -696,6 +696,18 @@ search cannot find a *schedule*. nothing. ✅ **Fixed: `INDEX.md` now has a generated table of all 59 notes** with title + status and says to search it before starting. Regenerate when notes are added. +* ✅🔴 **(2026-08-25) Freeze frontier: wait confirmed, but `info args` won't work.** + ✅ **Verified** the frozen frame's export by ordinal: the shim's + `(unsigned short)176` = `0xB0` = **`KeWaitForSingleObject`** + (`xboxkrnl_table.inc:190`) — previously read off frame names, now pinned. + 🔴 **Corrected:** the doc's "the handle is one `info args` away" is wrong for + this binary — `readelf` shows **0 debug sections**, symtab only (27 807 entries). + Not stripped ≠ has debug info; gdb can name frames and nothing inside them. + **Two routes, costs recorded:** (1) prologue-guided stack read of `XObject::Wait`'s + spilled `this` — no rebuild, keeps the oracle identical, but per-frame + archaeology; (2) a `RelWithDebInfo` build via `build-canary` — makes this and all + future freeze questions easy, at a full compile and a binary differing from the + one every other measurement used. * ~~🚧 BLOCKER: t=210/240 unreachable in one turn~~ — **superseded, see above**; it rested on an untested assumption that a turn is one shell call. 595 s shell cap − ~220 s boot (a ~190 s title movie that cannot be tapped through) − ~25 s startup = **~350 s observation ≈ 193 game-seconds**. diff --git a/docs/re/mission-freeze-resume-spin.md b/docs/re/mission-freeze-resume-spin.md index c8cab18..a142471 100644 --- a/docs/re/mission-freeze-resume-spin.md +++ b/docs/re/mission-freeze-resume-spin.md @@ -671,3 +671,60 @@ was simply blind. `docs/re/`, 59 entries with title and status, and says outright that it should be searched before starting an investigation. Regenerating it is a few lines of Python and should be redone whenever notes are added. + +--- + +# 2026-08-25 — the wait is confirmed as `KeWaitForSingleObject`, but `info args` will not work + +Two corrections to the plan this file leaves for the next pass, both cheap to +establish and both worth having before a run is spent on them. + +## ✅ Verified: the export in the frozen frame is `KeWaitForSingleObject` + +The backtrace's shim frame carries its ordinal as a template parameter: + +``` +xe::kernel::shim::ExportRegistrerHelper<(KernelModuleId)0, (unsigned short)176, …> +``` + +`176` decimal is `0xB0`, and `xboxkrnl_table.inc:190` reads: + +``` +XE_EXPORT(xboxkrnl, 0x000000B0, KeWaitForSingleObject, kFunction), +``` + +So the two hot threads really are in `KeWaitForSingleObject` — previously read +off the frame names, now pinned by ordinal. + +## 🔴 Corrected: there is no DWARF, so the handle is not one `info args` away + +This file's next step says the wait target is "one `info args`-equivalent away +now that a debugger can be attached at will". That is **not true of this binary**: + +``` +readelf -S xenia_canary | grep -c 'debug_info|debug_line' -> 0 +.debug* sections -> none +symtab entries -> 27 807 +``` + +The Release build is **not stripped**, which is why frames carry function names, +but it has **no debug info at all**. gdb can therefore name a frame and nothing +inside it — no `info args`, no `p *this`, no struct members. The earlier note +conflated "not stripped" with "has debug info"; they are different things. + +## The two routes that remain, with their real costs + +1. **Prologue-guided stack read.** `XObject::Wait`'s `this` arrives in `RDI` and + is almost certainly spilled to a known stack slot. Disassembling the prologue + (`x/20i`) locates the slot, and the frozen frame's `RBP`/`RSP` then give the + pointer, from which the object's vtable identifies its type. No rebuild, but + it is per-frame archaeology and must be redone if the binary changes. +2. **A `RelWithDebInfo` build** via `build-canary`. Makes `info args`, `p *this` + and member access work as the note assumed, at the price of a full compile and + a binary that is not the one every other measurement in this corpus was taken + against. + +Route 1 is cheaper and keeps the oracle identical; route 2 is what makes this and +every future freeze question easy. Neither is attempted here — recorded so the +next pass picks with the costs visible instead of discovering the missing DWARF +mid-run.