re: the waited object reads as an xe::kernel::XEvent

Executed the wait-object read on a live run under gdb. For every thread whose
frame 3 is XObject::Wait, rbx gives a pointer whose first quadword is
0x5555562db8f0, which is the PIE base plus 0xd878f0, and the symbol table has
vtable for xe::kernel::XEvent at 0xd878e0. The stored pointer is the vtable
symbol plus sixteen, since offset-to-top and RTTI come first, so it matches
exactly. The same vtable appears on every sampled waiting thread while the this
pointers differ, meaning many threads waiting on different XEvent instances. The
whole chain needs no DWARF and no rebuild, as the static groundwork predicted.

Two caveats, both recorded rather than smoothed over.

This is a healthy-play snapshot, not the freeze. The capture landed at 195 s of
flight with screen_id reporting flight and a non-black mean, because boot under
gdb costs about 300 s and the entire experiment has to fit inside one call --
a timeout kills the process group and takes the emulator with it, which lost an
earlier attempt outright. So this describes what threads wait on during normal
play, which is the control the frozen capture never had, but it is not the
frozen case.

And the this addresses look like host stack rather than heap. Either xenia
places these objects somewhere unusual, or rbx at frame 3 is not Wait's this
after the unwind and the vtable match is coincidence. An exact plus-sixteen
match on a known symbol is hard to get by accident, but the address range is not
what was expected. The settling check is to dump a few words at rbx and see
whether they look like an XEvent -- vtable, KernelState pointer, handle and type
fields -- or like saved registers.
This commit is contained in:
Sylpheed RE agent
2026-08-25 08:04:33 +00:00
parent 4ef15a1dbe
commit a153c392f5
3 changed files with 127 additions and 0 deletions

View File

@@ -0,0 +1,63 @@
#!/usr/bin/env bash
# Read WHICH object the frozen guest threads are waiting on.
#
# Groundwork (mission-freeze-resume-spin.md): XObject::Wait keeps `this` in %rbx,
# and .eh_frame lets gdb restore callee-saved registers during the unwind, so no
# DWARF and no rebuild are needed -- select the Wait frame, read rbx, then read
# the vtable pointer at [rbx] and resolve it in the symtab.
set -u
export HOME=/sylph-home/re SDL_AUDIODRIVER=dummy DISPLAY=:98
export PYTHONPATH=/sylph-home/.local/lib/python3.12/site-packages
export XENIA_BIN=/sylph-home/re/bin/gdb-wrap/xenia_canary
SD="$(cd "$(dirname "$0")" && pwd)"
FLY="${1:-240}"
CMD=/tmp/gdb-cmd; OUT=/tmp/gdb-out.log
"$SD/launch_mission.sh" fly || { echo "BOOT FAILED"; exit 1; }
CFG=/tmp/nav-fz.json
for t in 1 2 3; do
python3 "$SD/pad.py" set "rt=1" >/dev/null 2>&1 || true; sleep 3
python3 "$SD/pad.py" clear >/dev/null 2>&1 || true
if python3 "$SD/entities2.py" self 0x130 "$CFG" >/dev/null 2>&1; then
SYLPH_HUNT=1 SYLPH_KEEPOUT=1400 nohup python3 "$SD/pilot.py" "$CFG" "$FLY" \
</dev/null >/tmp/fz-pilot.log 2>&1 & P=$!; echo "--- pilot flying"; break
fi
done
echo "--- flying ${FLY}s (boot under gdb costs ~300s, so this must leave room"
echo "--- for the gdb step inside one call: timeout kills the whole group)"
python3 -c "import time,sys; time.sleep(int(sys.argv[1]))" "$FLY"
[ -n "${P:-}" ] && kill "$P" 2>/dev/null
pid=$(pgrep -x gdb | head -1)
[ -n "$pid" ] || { echo "no gdb"; exit 2; }
before=$(wc -c < "$OUT")
kill -INT "$pid"; sleep 3
{
echo 'echo === HOT THREAD WAIT OBJECTS ===\n'
echo 'thread apply all bt 6'
echo 'echo === END BT ===\n'
} >> "$CMD"
sleep 10
tail -c +$((before + 1)) "$OUT" > /tmp/fz-bt.txt
# find threads whose frame 3 is XObject::Wait, then read rbx there
python3 - <<'PY' >> "$CMD"
import re
txt=open('/tmp/fz-bt.txt').read()
cur=None
for line in txt.splitlines():
m=re.match(r'Thread (\d+) ', line)
if m: cur=m.group(1)
if cur and 'XObject::Wait' in line:
print(r'echo === T%s ===\n' % cur)
print('thread %s' % cur)
print('frame 3')
print('info registers rbx')
print('x/1gx $rbx')
cur=None
print(r'echo === END OBJ ===\n')
print('continue')
PY
sleep 12
screenshot /tmp/fz-screen.png >/dev/null 2>&1
echo "--- screen at capture time:"
python3 "$SD/screen_id.py" /tmp/fz-screen.png 2>/dev/null | head -2
tail -c +$((before + 1)) "$OUT" | sed -n '/HOT THREAD WAIT OBJECTS/,$p' | grep -vE "^\s*$" | tail -70
echo "FREEZE WAITOBJ DONE"