From a459dd3fb267bfba24c3fad1e3dc789bf50ee20b Mon Sep 17 00:00:00 2001 From: salmonik Date: Sun, 2 Aug 2026 16:06:40 +0200 Subject: v0.5.6: fault dumps carry kernel image base + stack backtrace - fault_dump prints img= (kernel image base) on the serial line and the on-screen band, so a real-hardware repro can be resolved against the PDB without serial - backtrace: rbp-chain walk then a raw scan of the stack above the interrupted rsp; both filter to the .text range (PE-header parse via memory::text_range) so only code addresses are collected - on-screen boot marker shows the kernel image range at every boot - crash.log now records img= + bt= lines alongside rip/rsp --- rsnix/src/memory.rs | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) (limited to 'rsnix/src/memory.rs') diff --git a/rsnix/src/memory.rs b/rsnix/src/memory.rs index e6896f2..35f3464 100644 --- a/rsnix/src/memory.rs +++ b/rsnix/src/memory.rs @@ -91,7 +91,10 @@ pub fn set_kernel_image_range(base: usize, size: usize) { } } -fn kernel_image_range() -> Option<(usize, usize)> { +/// The loaded kernel image range `(start, end)`, if known. Used to +/// resolve fault RIPs against the symbol table and to filter stack +/// scans for return addresses. +pub fn kernel_image_range() -> Option<(usize, usize)> { unsafe { if KERNEL_IMAGE_END > KERNEL_IMAGE_START { Some((KERNEL_IMAGE_START, KERNEL_IMAGE_END)) @@ -101,6 +104,40 @@ fn kernel_image_range() -> Option<(usize, usize)> { } } +/// The in-memory `.text` section range `(start, end)`, parsed from the +/// loaded PE headers. Used to filter fault backtraces: only code +/// addresses are meaningful return addresses (the rest of the image +/// range is .rdata/.data pointers). Falls back to the full image +/// range if the headers are unreadable. +pub fn text_range() -> Option<(usize, usize)> { + let (base, _end) = kernel_image_range()?; + unsafe { + let base = base as *const u8; + if base.read() != b'M' || base.add(1).read() != b'Z' { + return kernel_image_range(); + } + let e_lfanew = (base.add(0x3c) as *const u32).read() as usize; + let pe = base.add(e_lfanew); + if (pe as *const u32).read() != 0x4550 { + return kernel_image_range(); + } + let fh = pe.add(4); // COFF file header (20 bytes) + let size_opt = (fh.add(16) as *const u16).read() as usize; + let nsec = (fh.add(2) as *const u16).read() as usize; + let sec = fh.add(20).add(size_opt); + for i in 0..nsec { + let s = sec.add(i * 40); + let nm = core::slice::from_raw_parts(s, 8); + if nm.starts_with(b".text") { + let vsz = (s.add(8) as *const u32).read() as usize; + let va = (s.add(12) as *const u32).read() as usize; + return Some((base as usize + va, base as usize + va + vsz)); + } + } + kernel_image_range() + } +} + /// Minimum pages `grow_heap` takes from the page allocator per growth. const HEAP_GROW_PAGES: usize = 64; -- cgit v1.2.3