diff --git a/docs/re/structures/isl-builtins.md b/docs/re/structures/isl-builtins.md index 98ac008e..1ab75a28 100644 --- a/docs/re/structures/isl-builtins.md +++ b/docs/re/structures/isl-builtins.md @@ -340,7 +340,48 @@ the script side) and **`0x8226D420`, an engine site** — so the engine clears i too. What actually *appends* a node is still unidentified. -## 🔴 What appends a trigger node — NOT FOUND this iteration +## ✅ FOUND: the appender is `sub_8226EAB8`, and the count lives at `inner+8` + +The watchpoint plus Canary's own source settles it. At the moment of the write +the guest context (`%rsi`, per `x64_emitter.cc:881`) contains **`0x8226EAE0`**, +which is inside `sub_8226EAB8` — so that is the guest code doing it. + +`sub_8226EAB8` is a **generic list-node insert**: + +``` +8226eae8 lwz r11, 8(r30) ; current count +8226eaf0 cmplwi r10, 0x1 ; overflow guard against 0x3FFFFFFF +8226eb30 addi r11, r11, 1 +8226eb34 stw r11, 8(r30) ; count += 1 +8226eb38 stw r3, 4(r29) ; link the new node +8226eb40 stw r3, 0(r11) +``` + +**It increments a count at `+8` of the container it is handed** — and it has +**16 callers**, so it is a shared container helper, not trigger-specific. + +### ✅ Why the static search missed it, and what `+12` really is + +The trigger container at `phase+272` **embeds an inner list object at `+12`** +(which is why the push does `addi r31, r30, 12`). That inner object keeps its own +count at **its** `+8`: + +``` +phase + 272 + 12 + 8 = phase + 272 + 20 +``` + +— exactly the word the watchpoint was set on. So the write really is +`stw r11, 8(r30)` with `r30 = phase+284`, and searching for `stw rN, 20(rM)` +could never have found it. That also resolves the earlier 🟡: **`+12` is the +embedded list object**, not a list head pointer, which is why it read +`0x000A0009` rather than an address. + +**Method note worth keeping:** the static hunt failed because it assumed the +field's offset in the *outer* object would appear in the writing instruction. A +watchpoint does not care about the addressing form, which is exactly why it was +the right tool once the offset search came up empty twice. + +## ~~🔴 What appends a trigger node — NOT FOUND~~ (superseded above) Three approaches, none of which produced the appender: diff --git a/tools/re-capture/trigger_watch.sh b/tools/re-capture/trigger_watch.sh index cba80abd..9d9d0829 100755 --- a/tools/re-capture/trigger_watch.sh +++ b/tools/re-capture/trigger_watch.sh @@ -31,6 +31,23 @@ for t in 1 2 3; do fi done +# One run reached "IN FLIGHT" with the script NOT resident and the mission +# unlocatable -- and it was frozen on a black screen soon after +# (script-runtime-probe.md). A healthy run has the script resident at the first +# sample, so a short poll separates "not loaded yet" from "this run is broken" +# without waiting on a doomed one. +ok=0 +for _ in 1 2 3 4 5 6; do + if python3 -c " +import sys,os; sys.path.insert(0,'$SD') +import squadron_state as S, isl, gmem +ssb=isl.load('/tmp/Stage02.ssb'); p=gmem.mem_path() +with open(p,'rb',buffering=0) as f: + sys.exit(0 if S._find(f, os.path.getsize(p), ssb[:20]) else 1)"; then ok=1; break; fi + echo "--- script not resident yet"; sleepfor 10 +done +[ $ok = 1 ] || { echo "SCRIPT NEVER BECAME RESIDENT -- run is broken, not slow"; exit 3; } + HOSTADDR=$(python3 "$SD/host_addr.py" 2>/tmp/tw-addr.err) echo "--- translation: $(cat /tmp/tw-addr.err)" echo "--- host addr: $HOSTADDR"