-
Notifications
You must be signed in to change notification settings - Fork 0
Debugging Recipes
This page owns: short, copyable procedures for the questions that come up repeatedly.
Each recipe has been used in anger on this project. They assume the caveats on When your instrumentation lies — particularly that memory access is physical and unmunged.
Question: something bugchecked with an address. Which instruction referenced it?
A boot takes thousands of ordinary page faults, so the trick is filtering.
python3 tools/mkbootscript.py --veneer <VENEER.EXE> --ckpt <pre-go.ckpt> \
--bp '0x300:machine.cpu.dar == 0xEE315C98' \
--bp 0x80679eac \
--out tmp/boot.gsThe first is a conditional breakpoint on the data-storage vector, firing only for that
address. Use the value as reported — DAR is munged, and so is the bugcheck's parameter.
The second is KeBugCheckEx as a backstop, so the run still stops usefully if the condition never
matches. Get its address from the export table plus the load base:
python3 tools/pe-exports.py NTOSKRNL.EXE | grep KeBugCheckEx # -> descriptor RVAEach hit reports srr0 srr1 dar dsisr lr r1 r3..r6. SRR0 is your answer.
Question: SRR0 is 0xEE326BC0. What is that?
If it is in a module the HAL listed, arithmetic is enough. Every boot prints the list:
HAL: module VIDEOPRT.SYS at 80705000 size 00007720
If it is not — a driver loaded after that point — fingerprint it:
# 1. translate and read the instructions (word munge!)
# machine.cpu.mmu.translate(0xEE326BC0) -> 0x004BEBC0
# machine.memory.peek.l((0x4BEBC0 + 4*i) ^ 4)
# 2. take a branch-free run — position-independent
words = [0x63a40000, 0x63c50000, 0x3be10038, 0x63e60000, 0x38600004]
pat = b''.join(struct.pack('<I', w) for w in words)
# 3. search every driver on the disk
for f in glob.glob('drivers/*'):
i = open(f, 'rb').read().find(pat)
if i >= 0: print(f, hex(i))Seven instructions identified MGA_MIL.SYS uniquely among 53. The file offset converts to an RVA,
and runtime_address - RVA gives the module's load base — which unlocks everything else.
Extract the drivers first with tools/fatcat.py.
Question: a value in memory is wrong. Who wrote it?
--watch 0xE6E84324Emits a write logpoint that reports the value, the writing PC and r1 on every hit.
Two caveats, both learned the hard way. Logpoint output goes to the log stream — for a
daemon, its own stdout, not your client — so look in the daemon's log. And the address may need
the munge applied depending on how the hook is wired; if nothing fires, try addr ^ 4.
When the watch does not work, the stack itself often answers it for free. Read the frame directly:
0xe6e84320 0xe6e84380 ← back chain
0xe6e84324 0xee31db04 ← 4(r1): the saved TOC slot, holding a code address
That single line was the whole of wall 49's proof.
Question: the loader is behaving as though it were told something odd. What was it told?
Turn on the veneer's argv tracing:
--vrdebug 0x2000Argv[0]: multi(0)scsi(1)disk(0)rdisk(0)partition(1)\os\winnt40\osloader.exe
Argv[1]: OsLoader=multi(0)scsi(1)disk(0)rdisk(0)partition(1)\os\…
Argv[2]: SystemPartition=multi(0)scsi(1)disk(0)rdisk(0)partition(1)
One line answers "did the environment reach the loader?", which is otherwise several hours of
inference. Add 0x1000 to trace every read and seek — that is how "is it reading the partition or
the whole disk?" gets settled.
→ The veneer §7
Question: is the file on the disk the one I think it is?
python3 tools/fatls.py tmp/nt-installed.img # what is there
python3 tools/fatcat.py tmp/nt-installed.img 1 '\OS\WINNT40\HAL.DLL' /tmp/x.dll
cmp build/hal.dll /tmp/x.dll && echo sameAnd the one that has saved two debugging sessions — check the image is not truncated:
python3 tools/savedisk.py tmp/ckpt-daemon tmp/out.img --check→ Capturing the installed image
Question: did the compiler fold my careful byte-at-a-time code back into a wide access?
llvm-objdump -d build/hal.elf | grep -E 'lwbrx|stwbrx|lhbrx|sthbrx'Nothing printed is the pass condition. This catches the byte-reversed load shape, which the Makefile's store-merging flag does not cover. → Little-endian PowerPC §5
Question: is the firmware doing what I think, or is something above it at fault?
Drop to the 0 > prompt and try it by hand. This settled the :N raw-open question in one run,
after a day of inference:
variable ih
" /bandit@F2000000/53c825@12/sd@0,0:1" open-dev ih !
600000 200 " read" ih @ $call-method .
600000 10 dump
ih @ close-devTwo practical notes. The serial input FIFO holds about sixteen characters, so long lines lose
their head — feed them in chunks. And dump on memory the firmware has not claimed throws
DEFAULT CATCH!; use alloc-mem for a buffer.
→ Open Firmware §6
Question: a breakpoint or poke that worked last week now does nothing.
Almost always this: changing the HAL's size moves every module loaded after it. Any hard-coded runtime address in another module is invalidated by an unrelated HAL rebuild.
Recompute from the module list the HAL prints on every boot:
HAL: module VIDEOPRT.SYS at 80705000 size 00007720
^^^^^^^^ this moved
base + RVA for a breakpoint; (physical + RVA) ^ 4 for a word poke.
→ Iterating on the HAL §7
Question: the run is over and I wish I had printed one more thing.
You may not need to re-run. The daemon holds the machine after a script finishes — registers, memory and screen are all still there:
python3 tools/gsh.py 'echo "pc=${machine.cpu.pc} r2=${machine.cpu.r2}"'Execution is deterministic, so a re-run reproduces the same state exactly — but inspecting what is already loaded is free, and more than once the answer was sitting in a daemon nobody had touched.
- The emulator shell — the primitives these are built from.
- Decoding a bugcheck — recipes 1 and 2 in context.
Corrections welcome — this wiki is edited directly, so nothing here has had a review. Repository · STORY.md · GPL-2.0-only
Start here
Theory
- Why NT on a Power Mac is hard
- Open Firmware
- ARC
- The veneer
- The NT boot chain
- The HAL contract
- The NT PowerPC ABI
- Little-endian PowerPC
- How Setup chooses a HAL
- The NT video stack
Machines
Emulator
- Getting Granny Smith
- Media you must supply
- Building the HAL
- Making an OEM CD
- Preparing disks
- Running text-mode Setup
- Capturing the installed image
- Booting the installed system
- Iterating on the HAL
- Checkpoints and deltas
Real hardware
Debugging
- The emulator shell
- Reading NT binaries
- Decoding a bugcheck
- When your instrumentation lies
- Debugging recipes
Reference
- HAL exports
- ARC environment variables
- The veneer's VrDebug bitmask
- Veneer patch catalogue
- Address and interrupt map
- Error codes seen
Project