re: the trigger-queue appender found -- sub_8226EAB8, count at inner+8
The watchpoint plus Canary's source settle it. At the write, the guest context (rsi, per x64_emitter.cc:881) holds 0x8226EAE0, inside sub_8226EAB8. That function is a generic list-node insert: it reads the count at 8(r30), guards against 0x3FFFFFFF overflow, does addi r11,r11,1 / stw r11,8(r30), then links the node. It has 16 callers, so it is a shared container helper. That explains why two static searches missed it. The trigger container at phase+272 EMBEDS an inner list object at +12 -- which is why the push does 'addi r31, r30, 12' -- and the inner object keeps its count at its own +8. So 272 + 12 + 8 = 292 = the watched word, and the instruction is stw r11, 8(r30) with r30 = phase+284. Searching for 'stw rN, 20(rM)' could never have found it. Also resolves the earlier open item on +12: it is the embedded list object, not a list head pointer, which is why it read 0x000A0009 instead of an address. Method note kept: the static hunt assumed the field's offset in the OUTER object would appear in the writing instruction. A watchpoint is indifferent to the addressing form, which is why it was the right tool after two failed offset searches.
This commit is contained in:
@@ -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.
|
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:
|
Three approaches, none of which produced the appender:
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,23 @@ for t in 1 2 3; do
|
|||||||
fi
|
fi
|
||||||
done
|
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)
|
HOSTADDR=$(python3 "$SD/host_addr.py" 2>/tmp/tw-addr.err)
|
||||||
echo "--- translation: $(cat /tmp/tw-addr.err)"
|
echo "--- translation: $(cat /tmp/tw-addr.err)"
|
||||||
echo "--- host addr: $HOSTADDR"
|
echo "--- host addr: $HOSTADDR"
|
||||||
|
|||||||
Reference in New Issue
Block a user