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