|
| 1 | +use crate::arch::{EMERG_STACK_END, IMAGE_BASE, KERNEL_STACK_AREA, KERNEL_STACK_PG_ORDER}; |
| 2 | +use core::arch::asm; |
| 3 | +use core::slice; |
| 4 | +use libkernel::memory::{PAGE_SIZE, address::VA, region::VirtMemoryRegion}; |
| 5 | +use log::error; |
| 6 | +#[cfg(target_pointer_width = "32")] |
| 7 | +use object::elf::FileHeader32 as FileHeader; |
| 8 | +#[cfg(target_pointer_width = "64")] |
| 9 | +use object::elf::FileHeader64 as FileHeader; |
| 10 | +use object::{ |
| 11 | + NativeEndian, elf, |
| 12 | + read::elf::{FileHeader as _, Sym as _}, |
| 13 | +}; |
| 14 | + |
| 15 | +const MAX_FRAMES: usize = 64; |
| 16 | + |
| 17 | +pub struct StackTrace { |
| 18 | + pub fp: usize, |
| 19 | + pub pc_ptr: *const usize, |
| 20 | +} |
| 21 | + |
| 22 | +impl StackTrace { |
| 23 | + #[inline(always)] |
| 24 | + pub unsafe fn start() -> Option<Self> { |
| 25 | + unsafe { |
| 26 | + let fp: usize; |
| 27 | + asm!("mov {}, fp", out(reg) fp); |
| 28 | + let pc_ptr = fp.checked_add(size_of::<usize>())?; |
| 29 | + Some(StackTrace { |
| 30 | + fp, |
| 31 | + pc_ptr: pc_ptr as *const usize, |
| 32 | + }) |
| 33 | + } |
| 34 | + } |
| 35 | + |
| 36 | + pub unsafe fn next(self) -> Option<Self> { |
| 37 | + unsafe { |
| 38 | + let fp = *(self.fp as *const usize); |
| 39 | + let pc_ptr = fp.checked_add(size_of::<usize>())?; |
| 40 | + Some(StackTrace { |
| 41 | + fp, |
| 42 | + pc_ptr: pc_ptr as *const usize, |
| 43 | + }) |
| 44 | + } |
| 45 | + } |
| 46 | +} |
| 47 | + |
| 48 | +fn emergency_stack_area() -> VirtMemoryRegion { |
| 49 | + let stack_size = PAGE_SIZE << KERNEL_STACK_PG_ORDER; |
| 50 | + VirtMemoryRegion::new(EMERG_STACK_END.sub_bytes(stack_size), stack_size) |
| 51 | +} |
| 52 | + |
| 53 | +fn is_valid_frame_pointer(frame: &StackTrace) -> bool { |
| 54 | + let fp_virt = VA::from_value(frame.fp); |
| 55 | + let pc_virt = VA::from_value(frame.pc_ptr.addr()); |
| 56 | + let emergency_stack = emergency_stack_area(); |
| 57 | + |
| 58 | + let in_kernel_stack = |
| 59 | + |va| KERNEL_STACK_AREA.contains_address(va) || emergency_stack.contains_address(va); |
| 60 | + |
| 61 | + in_kernel_stack(fp_virt) |
| 62 | + && in_kernel_stack(pc_virt) |
| 63 | + && (frame.fp as *const usize).is_aligned() |
| 64 | + && frame.pc_ptr.is_aligned() |
| 65 | +} |
| 66 | + |
| 67 | +pub fn print_backtrace() { |
| 68 | + unsafe { |
| 69 | + let kernel_ptr = IMAGE_BASE.cast::<u8>().as_ptr(); |
| 70 | + let elf_header: &FileHeader<NativeEndian> = object::pod::from_bytes(slice::from_raw_parts( |
| 71 | + kernel_ptr, |
| 72 | + size_of::<FileHeader<NativeEndian>>(), |
| 73 | + )) |
| 74 | + .unwrap() |
| 75 | + .0; |
| 76 | + |
| 77 | + // This assumes that the linker places .shstrtab as last section. If it |
| 78 | + // isn't, that just causes a recursive panic, not UB. |
| 79 | + let kernel_size = elf_header.e_shoff(NativeEndian) as usize |
| 80 | + + usize::from(elf_header.e_shnum(NativeEndian)) |
| 81 | + * usize::from(elf_header.e_shentsize(NativeEndian)); |
| 82 | + let kernel_slice = slice::from_raw_parts(kernel_ptr, kernel_size); |
| 83 | + |
| 84 | + let symbols = elf_header |
| 85 | + .sections(NativeEndian, kernel_slice) |
| 86 | + .unwrap() |
| 87 | + .symbols(NativeEndian, kernel_slice, elf::SHT_SYMTAB) |
| 88 | + .unwrap(); |
| 89 | + |
| 90 | + let mut frame = StackTrace::start(); |
| 91 | + |
| 92 | + for _ in 0..MAX_FRAMES { |
| 93 | + let Some(frame_) = frame else { |
| 94 | + break; |
| 95 | + }; |
| 96 | + |
| 97 | + if !is_valid_frame_pointer(&frame_) { |
| 98 | + error!(" {:>016x}: INVALID FRAME", frame_.fp); |
| 99 | + break; |
| 100 | + } |
| 101 | + |
| 102 | + let pc = *frame_.pc_ptr; |
| 103 | + if pc == 0 { |
| 104 | + error!(" {:>016x}: EMPTY RETURN", frame_.fp); |
| 105 | + break; |
| 106 | + } |
| 107 | + |
| 108 | + error!(" FP {:>016x}: PC {:>016x}", frame_.fp, pc); |
| 109 | + |
| 110 | + for sym in symbols.iter() { |
| 111 | + if sym.st_type() != elf::STT_FUNC { |
| 112 | + continue; |
| 113 | + } |
| 114 | + let sym_addr = sym.st_value.get(NativeEndian) as usize; |
| 115 | + if !(pc >= sym_addr && pc < sym_addr + sym.st_size.get(NativeEndian) as usize) { |
| 116 | + continue; |
| 117 | + } |
| 118 | + |
| 119 | + let sym_offset = pc - sym_addr; |
| 120 | + if let Some(sym_name) = sym |
| 121 | + .name(NativeEndian, symbols.strings()) |
| 122 | + .ok() |
| 123 | + .and_then(|name| core::str::from_utf8(name).ok()) |
| 124 | + { |
| 125 | + error!(" {sym_name} @ {sym_addr:>016X}+{sym_offset:>04X}"); |
| 126 | + } else { |
| 127 | + error!(" {sym_addr:>016X}+{sym_offset:>04X}"); |
| 128 | + } |
| 129 | + } |
| 130 | + frame = frame_.next(); |
| 131 | + } |
| 132 | + } |
| 133 | +} |
0 commit comments