re: the trigger-count watchpoint fires, but the writer is JIT guest code

trigger_watch.sh + host_addr.py translate the guest VA to a host address
(0xBE14DEA4 -> host 0x1BE14DEA4) and watch it. It fires: Thread 50 'Main
XThread', old 0, new 16777216 -- which is big-endian 1 read little-endian, so
the count going 0 -> 1, independently confirming the field. The write happens on
the guest's own main thread, not an emulator worker.

But the writer cannot be named from the host stack: the faulting PC is
0xa0c65f23 with no symbol, executing 'mov 0x110(%rsi),%rbx', i.e. Xenia's
JIT-compiled guest code, and the frames above it are not host-unwindable. So the
watchpoint answers when and which thread, not which guest function.

Recorded as a ceiling of the method rather than retried blindly. The way past it
is that the JIT holds the guest context in a register (%rsi here), so the guest
PC is recoverable from the context block -- which needs Xenia's context layout
from the xenia-rs sources on this box, a separate tractable piece of work.
This commit is contained in:
Sylpheed RE agent
2026-08-25 18:52:35 +00:00
parent 28a4b1ead1
commit da2d02db77

View File

@@ -368,3 +368,36 @@ watchpoint on the count word** during a live mission. The address is known at
runtime (`ScriptPhase + 272 + 20`), the count demonstrably changes within ~2
minutes of flight, and the watchpoint reports the writing instruction directly
instead of inferring it from static shape.
## 🟡 The watchpoint fired — the writer is JIT-compiled GUEST code, not host code
`tools/re-capture/trigger_watch.sh` + `host_addr.py` translate the guest VA into
a host address and set a gdb watchpoint on it:
```
mission 0xBC7A2A20 phase 0xBE14DD80 va 0xBE14DEA4 off 0x11E14DEA4 -> host 0x1BE14DEA4
Hardware watchpoint 1: *(unsigned int*)0x1BE14DEA4
Thread 50 "Main XThread" hit it: Old value = 0 New value = 16777216
```
**Two things confirmed.** `16777216` is `0x01000000` — big-endian `1` read
little-endian, so this is exactly the count going **0 → 1**, independently
confirming that `[ScriptPhase+272+20]` is the field. And the write happens on
the **guest's own Main XThread**, not on an emulator worker.
🔴 **But the writer cannot be named from the host stack.** The faulting PC is
`0xa0c65f23`, with no symbol, and the instruction is
`mov 0x110(%rsi),%rbx` — this is **Xenia's JIT-compiled guest code**. The
backtrace above it is garbage (`0x45e0000000`, `0x100000000`), because JIT frames
are not host-unwindable.
So the host watchpoint answers *when* and *which guest thread*, but **not which
guest function** — the thing I actually wanted. The method has a ceiling here,
and it is worth recording rather than re-attempting the same way.
**What would get past it:** the JIT keeps the guest context in a register
(`%rsi` here, given `mov 0x110(%rsi),%rbx`), so the **guest PC is recoverable
from the context block** at the moment of the write. Reading the right offset out
of `$rsi` would name the guest instruction. That needs Xenia's context layout —
which is in the xenia-rs sources on this box — and is a separate, tractable
piece of work rather than another blind run.