re: two trigger appenders -- built-in 19's node carries the handler routine
Resolves the puzzle the previous commit left open: payload+28 is a 1-12 index, yet
the drain hands it to a spawner that wants a code offset. The cause is that built-in
25 is not the only thing that appends a trigger node.
Searching for functions that write a double at +16 AND a word at +28 of the same
register finds exactly two: sub_8226A0D8 (built-in 25, vtable slot 28) which fills the
payload inline, and sub_8226E160, a free-list recycling variant whose sole caller
sub_82269ED0 is vtable slot 68 = BUILT-IN 19.
Built-in 19 passes local[4] -> node+0 (the unit), local[12] -> +4, local[24] -> +16 as
a double, and local[32] -> +28. That last one is the field the drain spawns from, and
it is a genuine code offset:
over all 79 call sites disc-wide
land on the instruction stream 79 / 79 = 100.0%
control, random 4-aligned offsets 27.8%
values 12164 .. 111080, 73 distinct
Large, distinct, and every one resolves against a control that resolves barely a
quarter of the time. So built-in 19 registers a trigger whose handler is a routine,
and the drain's `base + payload+28` spawn is that handler. The two builders simply
put different things in the same slot.
Observed alongside: built-in 25 writes +24 = 1 and built-in 19 writes +24 = 0, which
is kind-tag shaped -- but the drain was not shown branching on it, so that is recorded
as an observation and not a reading.
All artefacts regenerate byte-identical; documentation only.
Still open: whether +24 selects between the node kinds, and this STILL does not
explain the unreached code -- 0 of the 79 handler offsets are unreached run-starts.
This commit is contained in:
@@ -119,6 +119,22 @@ unknown, what evidence exists, and what the first step would be. Move an item in
|
||||
earlier. The drain does operate on the container — what `sub_8226E458` does to
|
||||
it stays unread. 🟡 What starts the ~15 % of unreached code: still open, not this.
|
||||
|
||||
* ✅ **(2026-08-27) RESOLVED — TWO trigger appenders, and built-in 19's carries the
|
||||
handler routine. [structures/isl-trigger-node](structures/isl-trigger-node.md).**
|
||||
Last iteration's puzzle (`payload+28` is a 1–12 index, yet the drain spawns
|
||||
`base + payload+28`) had a simple cause: **built-in 25 is not the only appender**.
|
||||
Searching for functions writing a double at `+16` AND a word at `+28` of the same
|
||||
register finds exactly two — `sub_8226A0D8` (built-in 25, slot 28) and
|
||||
`sub_8226E160`, whose sole caller `sub_82269ED0` is **vtable slot 68 =
|
||||
built-in 19**. Built-in 19 passes `local[4]`→+0, `local[12]`→+4, `local[24]`→+16
|
||||
(double), **`local[32]`→+28**. 🔑 **Those ARE code offsets: 79/79 land on the
|
||||
instruction stream vs a 27.8 % control**, values 12 164–111 080, 73 distinct. So
|
||||
**built-in 19 registers a trigger whose handler is a routine**, and the drain's
|
||||
spawn is that handler. Observed: built-in 25 writes `+24 = 1`, built-in 19 writes
|
||||
`+24 = 0` — kind-tag shaped, 🟡 but the drain was not shown branching on it.
|
||||
🟡 **Still does not explain the unreached code** — 0 of the 79 handler offsets are
|
||||
unreached run-starts.
|
||||
|
||||
## ✅✅ SOLVED — the mission freeze was a modal sign-in dialog (2026-08-26)
|
||||
|
||||
`XamShowSigninUI` opens a modal dialog and `xeXamDispatchDialog` blocks the
|
||||
|
||||
@@ -85,13 +85,59 @@ argument. `sub_8226D740` does operate on the trigger container; what
|
||||
`sub_8226E458` does *to* it is still unread, so only the argument is corrected
|
||||
here, not the conclusion.
|
||||
|
||||
## ✅ RESOLVED: there are TWO appenders, and only one carries a routine
|
||||
|
||||
The puzzle above — `payload+28` holding a 1–12 index while the drain hands it to
|
||||
a spawner that wants a code offset — had a simple cause: **built-in 25 is not the
|
||||
only thing that appends a trigger node.**
|
||||
|
||||
Searching for functions that write a **double at +16** and a **word at +28** of
|
||||
the same register finds exactly two:
|
||||
|
||||
| builder | how |
|
||||
|---|---|
|
||||
| `sub_8226A0D8` — **built-in 25**, vtable slot 28 | fills the payload inline |
|
||||
| `sub_8226E160` | a free-list recycling variant, called from `sub_82269ED0` |
|
||||
|
||||
`sub_82269ED0` is **vtable slot 68 = built-in 19**, and it calls the builder as
|
||||
|
||||
```
|
||||
8226A034 addi r3, r28, 272 ; the trigger container
|
||||
8226A040 lwz r4, 4(r30) ; local[4] -> node+0 (the unit)
|
||||
8226A03C lwz r5, 12(r30) ; local[12] -> node+4
|
||||
8226A038 lfd f1, 24(r30) ; local[24] -> node+16 (double)
|
||||
8226A030 lwz r8, 32(r30) ; local[32] -> node+28
|
||||
```
|
||||
|
||||
### ✅ Built-in 19's `local[32]` IS a code offset
|
||||
|
||||
| over all 79 call sites disc-wide | |
|
||||
|---|---|
|
||||
| land on the instruction stream | **79 / 79 = 100.0 %** |
|
||||
| control, random 4-aligned offsets | 27.8 % |
|
||||
| value range | 12 164 … 111 080, **73 distinct** |
|
||||
|
||||
Large, distinct, and every one resolves — against a control that resolves barely
|
||||
a quarter of the time. So **built-in 19 registers a trigger whose handler is a
|
||||
routine**, and the drain's `base + payload+28` spawn is exactly that handler.
|
||||
|
||||
Built-in 25's `payload+28` really is a small index; the two builders simply put
|
||||
different things in the same slot. Observed alongside it: built-in 25 writes
|
||||
**`+24 = 1`** and built-in 19 writes **`+24 = 0`**, which is the shape of a kind
|
||||
tag — 🟡 but the drain was not shown branching on it, so that is an observation,
|
||||
not a reading.
|
||||
|
||||
## 🟡 Not settled
|
||||
|
||||
* **What `local[12]` indexes.** Values 1–12, and `SYM1_SLOTS` lists slot 12 for
|
||||
built-in 25 (symbol table 1 — routes, messages, objectives), which is
|
||||
consistent with a small index but was not verified here.
|
||||
* **What the drain actually spawns.** `[stack+112]` is filled from `payload+28`,
|
||||
yet the spawner takes a code offset — so some step in that chain is not what I
|
||||
read. Left open rather than guessed.
|
||||
* ~~What the drain actually spawns.~~ ✅ Resolved above: built-in 19's nodes carry
|
||||
a real routine offset at `+28`.
|
||||
* **Whether `+24` selects between the two node kinds** — 1 from built-in 25, 0
|
||||
from built-in 19 — is unshown; the drain reads it into a stack slot that the
|
||||
spawn path does not visibly use.
|
||||
* **This still does not explain the unreached code.** All 79 of built-in 19's
|
||||
handler offsets are already reachable — **0 are unreached run-starts**.
|
||||
* **`sub_8226E458`'s effect** on the container.
|
||||
* **What starts the ~15 % of unreached code** — still open, and this was not it.
|
||||
|
||||
Reference in New Issue
Block a user