-
Notifications
You must be signed in to change notification settings - Fork 0
Debugging Decoding a bugcheck
This page owns: turning a blue screen — or a loader error — into a faulting instruction and a module name.
*** STOP: 0x00000050 (0xEE315C98,0x00000000,0x00000000,0x00000000)
PAGE_FAULT_IN_NONPAGED_AREA
That is four numbers and a name, and on its own it tells you almost nothing useful: not which instruction, not which driver, not why. This page is the route from there to an answer.
The short version: a bugcheck reports the address that was referenced, never the instruction that referenced it. Getting the second one is the whole job.
Three different things print errors on this machine, and mistaking one for another wastes hours.
| What you see | Who is talking | Where to look |
|---|---|---|
DEFAULT CATCH!, Program complete, a 0 > prompt |
Open Firmware | Open Firmware |
OS Loader V4.00 then a sentence, or bare hex ids |
OSLOADER.EXE |
§5 below |
*** STOP: 0x… on a blue screen |
the NT kernel | the rest of this page |
The third is good news even when it looks worst: it means the whole firmware and loader chain succeeded. → The NT boot chain §3
*** STOP: 0x00000050 (P1, P2, P3, P4)
^^^^ the bugcheck code
Codes seen on this project, and what each turned out to mean here:
| Code | Name | On this machine it meant |
|---|---|---|
0x1E |
KMODE_EXCEPTION_NOT_HANDLED |
with P1 0x80000002: a misaligned access. Almost always the compiler folding byte accesses back into a wide one |
0x50 |
PAGE_FAULT_IN_NONPAGED_AREA |
P1 is the referenced address, P2 0 for a read |
0x0A / 0x09
|
IRQL_NOT_LESS_OR_EQUAL / NOT_GREATER_OR_EQUAL
|
a bad pointer, or something called at the wrong IRQL |
0x7B |
INACCESSIBLE_BOOT_DEVICE |
the loader could not reach the disk — usually firmware, not NT |
0x4C |
FATAL_UNHANDLED_HARD_ERROR |
P1 carries the real status, e.g. 0xC000026C "unable to load device driver" |
A bugcheck during a bugcheck is normal and not the interesting one. The kernel calls the HAL to paint the screen, and if that path faults you get a second, unrelated code. Diagnose the first.
0x50's first parameter — and the processor's DAR — are reported with the little-endian
address munge still applied:
reported 0xEE315C98 ^ 4 → really 0xEE315C9C
Un-munge before doing arithmetic. A diagnosis that comes out "four bytes off and I cannot see why" is this, every time. → When your instrumentation lies
On PowerPC the processor saves it in SRR0 when it takes the exception. The trick is catching
the machine at that moment, because a boot takes thousands of ordinary page faults.
A conditional breakpoint on the data-storage vector, filtered on the address you care about:
debug.breakpoints.add 0x300 "machine.cpu.dar == 0xEE315C98" "logical"
(Use the munged value there — that is what the register holds.)
mkbootscript.py --bp emits exactly this and dumps the registers on every hit:
BP 0x300 srr0=0xee326bc0 srr1=0xb031 dar=0xee315c98 dsisr=0x40000000
lr=0xee31db04 r1=0xe6e84320 r3=0x4 …
Now you have:
-
srr0— the faulting instruction, -
dsisr— why (bit 1 set: translation not found), -
lr— who called the function you are in, -
r1— the stack frame, which is often the key to the rest.
Add a breakpoint on KeBugCheckEx as a backstop: it catches the failure even when your condition
never matches, and r3/r4 are the code and first parameter.
SRR0 is an address; you need a name.
If it is in a module the HAL listed, it is arithmetic. Every boot prints:
HAL: module ntoskrnl.exe at 8064a000 size 00140000
HAL: module hal.dll at 8078a000 size 0000e000
HAL: module VIDEOPRT.SYS at 80705000 size 00007720
Find the range containing SRR0, subtract the base, and you have an RVA to look up.
→ Reading NT binaries
If it is not, the code is in a driver loaded after that list was printed. Then:
- Translate the address and read the instructions there —
machine.cpu.mmu.translate(srr0), then peek with the^ 4word munge. - Take a run of branch-free instructions, which are position-independent.
- Search every driver image on the disk for that byte sequence.
Seven instructions uniquely identified MGA_MIL.SYS among 53 drivers, and the file offset gave
the load base. → Debugging recipes
OSLOADER.EXE does not bugcheck. It prints sentences — or, when its own resource loading has
failed, bare hexadecimal message ids:
0000232e
00002333
00002350
Those are message-table ids. Decoding them needs the table out of OSLOADER.EXE's .rsrc:
9006 Windows NT could not start because of a computer disk hardware configuration problem.
9011 Could not access disk partition tables
9040 Please check the Windows NT(TM) documentation …
Seeing raw numbers is itself a diagnosis: it means the first failure was BlInitResources,
which is what loads the message table, and everything after is downstream of that.
→ The NT boot chain §2.2
Wall 49, start to finish, as a template.
| Step | What it gave |
|---|---|
| The screen |
0x50, referenced 0xEE315C98
|
Conditional breakpoint on 0x300
|
SRR0 = 0xEE326BC0, DSISR = translation not found |
mmu.translate on both |
SRR0 maps; DAR does not — the fault is real |
Disassemble at SRR0
|
lwz r11,-32360(r2) — the first instruction of an import glue stub |
| Therefore |
r2 is wrong, not the memory |
| Fingerprint the caller against 53 drivers |
MGA_MIL.SYS, loaded at 0xEE319000
|
| Its entry descriptor | TOC should be 0xEE321280; r2 held 0xEE31DB04 — a code address |
Read the stack at 4(r1)
|
that same value, sitting in the saved-TOC slot |
| Conclusion | the HAL's prologue had overwritten its caller's saved TOC |
Two general lessons in that table. The faulting instruction's identity mattered more than its address — recognising glue is what turned a memory question into a register question. And the distance between cause and symptom was several calls and an unrelated module, which is normal for ABI bugs and is why "the driver that crashed" is rarely the driver at fault.
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