Add support for userspace stack traces using usercopy

This commit is contained in:
Jeremy Soller
2025-11-10 08:54:06 -07:00
parent c66423cec6
commit 01ec650b1e
8 changed files with 142 additions and 59 deletions
+4 -9
View File
@@ -5,7 +5,6 @@ use crate::{
context::signal::excp_handler,
exception_stack,
memory::{ArchIntCtx, GenericPfFlags},
panic::stack_trace,
sync::CleanLockToken,
syscall::{self, flag::*},
};
@@ -14,8 +13,7 @@ use super::InterruptStack;
exception_stack!(synchronous_exception_at_el1_with_sp0, |stack| {
println!("Synchronous exception at EL1 with SP0");
stack.dump();
stack_trace();
stack.trace();
loop {}
});
@@ -162,8 +160,7 @@ exception_stack!(synchronous_exception_at_el1_with_spx, |stack| {
let far_el1 = far_el1();
println!("USER FAR_EL1 = 0x{:08x}", far_el1);
}
stack.dump();
stack_trace();
stack.trace();
loop {}
}
});
@@ -205,8 +202,7 @@ exception_stack!(synchronous_exception_at_el0, |stack| {
);
println!("FAR_EL1: {:#0x}", far_el1());
//crate::debugger::debugger(None);
stack.dump();
stack_trace();
stack.trace();
excp_handler(Exception {
kind: 0, // TODO
});
@@ -217,8 +213,7 @@ exception_stack!(synchronous_exception_at_el0, |stack| {
exception_stack!(unhandled_exception, |stack| {
println!("Unhandled exception");
stack.dump();
stack_trace();
stack.trace();
loop {}
});
+14 -1
View File
@@ -1,4 +1,4 @@
use crate::syscall::IntRegisters;
use crate::{panic, syscall::IntRegisters};
#[derive(Default)]
#[repr(C, packed)]
@@ -120,6 +120,12 @@ pub struct InterruptStack {
impl InterruptStack {
pub fn init(&mut self) {}
pub fn frame_pointer(&self) -> usize {
self.preserved.x29
}
pub fn stack_pointer(&self) -> usize {
self.iret.sp_el0
}
pub fn set_stack_pointer(&mut self, sp: usize) {
self.iret.sp_el0 = sp;
}
@@ -137,6 +143,13 @@ impl InterruptStack {
self.scratch.dump();
self.preserved.dump();
}
pub fn trace(&self) {
self.dump();
unsafe {
panic::user_stack_trace(&self);
panic::stack_trace();
}
}
/// Saves all registers to a struct used by the proc:
/// scheme to read/write registers.
+3 -4
View File
@@ -132,15 +132,14 @@ unsafe fn handle_system_exception(scause: usize, regs: &InterruptStack) {
"S-mode exception! scause={:#016x}, stval={:#016x}",
scause, stval
);
regs.dump();
if tp == 0 {
// Early failure - before misc::init and potentially before RMM init
// Do not attempt to trace stack because it would probably trap again
loop {}
regs.dump();
} else {
regs.trace();
}
stack_trace();
loop {}
}
}
+14 -3
View File
@@ -1,4 +1,4 @@
use crate::{memory::ArchIntCtx, syscall::IntRegisters};
use crate::{memory::ArchIntCtx, panic, syscall::IntRegisters};
use core::mem::size_of;
#[derive(Default)]
@@ -99,12 +99,15 @@ impl InterruptStack {
assert!(32 * 8 == size_of::<InterruptStack>());
}
}
pub fn set_stack_pointer(&mut self, sp: usize) {
self.registers.x2 = sp;
pub fn frame_pointer(&self) -> usize {
self.registers.x8
}
pub fn stack_pointer(&self) -> usize {
self.registers.x2
}
pub fn set_stack_pointer(&mut self, sp: usize) {
self.registers.x2 = sp;
}
pub fn set_instr_pointer(&mut self, ip: usize) {
self.iret.sepc = ip;
}
@@ -124,6 +127,14 @@ impl InterruptStack {
self.registers.dump();
}
pub fn trace(&self) {
self.dump();
unsafe {
panic::user_stack_trace(&self);
panic::stack_trace();
}
}
/// Saves all registers to a struct used by the proc:
/// scheme to read/write registers.
pub fn save(&self, all: &mut IntRegisters) {
+21 -1
View File
@@ -1,6 +1,6 @@
use core::mem;
use crate::{memory::ArchIntCtx, syscall::IntRegisters};
use crate::{memory::ArchIntCtx, panic, syscall::IntRegisters};
use super::super::flags::*;
@@ -88,6 +88,13 @@ impl InterruptStack {
self.scratch.dump();
self.preserved.dump();
}
pub fn trace(&self) {
self.dump();
unsafe {
panic::user_stack_trace(&self);
panic::stack_trace();
}
}
/// Saves all registers to a struct used by the proc:
/// scheme to read/write registers.
pub fn save(&self, all: &mut IntRegisters) {
@@ -124,6 +131,12 @@ impl InterruptStack {
all.ss = self.iret.ss;
}
}
pub fn frame_pointer(&self) -> usize {
self.preserved.ebp
}
pub fn stack_pointer(&self) -> usize {
self.iret.esp
}
pub fn set_stack_pointer(&mut self, esp: usize) {
self.iret.esp = esp;
}
@@ -188,6 +201,13 @@ impl InterruptErrorStack {
println!("CODE: {:08x}", { self.code });
self.inner.dump();
}
pub fn trace(&self) {
self.dump();
unsafe {
panic::user_stack_trace(&self.inner);
panic::stack_trace();
}
}
}
#[macro_export]
+14 -1
View File
@@ -1,4 +1,4 @@
use crate::{memory::ArchIntCtx, syscall::IntRegisters};
use crate::{memory::ArchIntCtx, panic, syscall::IntRegisters};
use super::super::flags::*;
@@ -102,6 +102,12 @@ impl InterruptStack {
self.iret.cs = (crate::gdt::GDT_USER_CODE << 3) | 3;
self.iret.ss = (crate::gdt::GDT_USER_DATA << 3) | 3;
}
pub fn frame_pointer(&self) -> usize {
self.preserved.rbp
}
pub fn stack_pointer(&self) -> usize {
self.iret.rsp
}
pub fn set_stack_pointer(&mut self, rsp: usize) {
self.iret.rsp = rsp;
}
@@ -120,6 +126,13 @@ impl InterruptStack {
self.scratch.dump();
self.preserved.dump();
}
pub fn trace(&self) {
self.dump();
unsafe {
panic::user_stack_trace(&self);
panic::stack_trace();
}
}
/// Saves all registers to a struct used by the proc:
/// scheme to read/write registers.
pub fn save(&self, all: &mut IntRegisters) {
+19 -37
View File
@@ -3,13 +3,12 @@ use x86::irq::PageFaultError;
use crate::{
arch::x86_shared::interrupt, context::signal::excp_handler, memory::GenericPfFlags,
paging::VirtualAddress, panic::stack_trace, ptrace, sync::CleanLockToken, syscall::flag::*,
paging::VirtualAddress, ptrace, sync::CleanLockToken, syscall::flag::*,
};
interrupt_stack!(divide_by_zero, |stack| {
println!("Divide by zero");
stack.dump();
unsafe { stack_trace() };
stack.trace();
excp_handler(Exception {
kind: 0,
..Default::default()
@@ -92,8 +91,7 @@ interrupt_stack!(breakpoint, |stack| {
interrupt_stack!(overflow, |stack| {
println!("Overflow trap");
stack.dump();
unsafe { stack_trace() };
stack.trace();
excp_handler(Exception {
kind: 4,
..Default::default()
@@ -102,8 +100,7 @@ interrupt_stack!(overflow, |stack| {
interrupt_stack!(bound_range, |stack| {
println!("Bound range exceeded fault");
stack.dump();
unsafe { stack_trace() };
stack.trace();
excp_handler(Exception {
kind: 5,
..Default::default()
@@ -112,8 +109,7 @@ interrupt_stack!(bound_range, |stack| {
interrupt_stack!(invalid_opcode, |stack| {
println!("Invalid opcode fault");
stack.dump();
unsafe { stack_trace() };
stack.trace();
excp_handler(Exception {
kind: 6,
..Default::default()
@@ -122,8 +118,7 @@ interrupt_stack!(invalid_opcode, |stack| {
interrupt_stack!(device_not_available, |stack| {
println!("Device not available fault");
stack.dump();
unsafe { stack_trace() };
stack.trace();
excp_handler(Exception {
kind: 7,
..Default::default()
@@ -132,9 +127,8 @@ interrupt_stack!(device_not_available, |stack| {
interrupt_error!(double_fault, |stack, _code| {
println!("Double fault");
stack.dump();
stack.trace();
unsafe {
stack_trace();
loop {
interrupt::disable();
interrupt::halt();
@@ -144,8 +138,7 @@ interrupt_error!(double_fault, |stack, _code| {
interrupt_error!(invalid_tss, |stack, code| {
println!("Invalid TSS fault");
stack.dump();
unsafe { stack_trace() };
stack.trace();
excp_handler(Exception {
kind: 10,
code,
@@ -155,8 +148,7 @@ interrupt_error!(invalid_tss, |stack, code| {
interrupt_error!(segment_not_present, |stack, code| {
println!("Segment not present fault");
stack.dump();
unsafe { stack_trace() };
stack.trace();
excp_handler(Exception {
kind: 11,
code,
@@ -166,8 +158,7 @@ interrupt_error!(segment_not_present, |stack, code| {
interrupt_error!(stack_segment, |stack, code| {
println!("Stack segment fault");
stack.dump();
unsafe { stack_trace() };
stack.trace();
excp_handler(Exception {
kind: 12,
code,
@@ -177,8 +168,7 @@ interrupt_error!(stack_segment, |stack, code| {
interrupt_error!(protection, |stack, code| {
println!("Protection fault code={:#0x}", code);
stack.dump();
unsafe { stack_trace() };
stack.trace();
excp_handler(Exception {
kind: 13,
code,
@@ -215,8 +205,7 @@ interrupt_error!(page, |stack, code| {
#[cfg(target_arch = "x86")]
if crate::memory::page_fault_handler(&mut stack.inner, generic_flags, cr2).is_err() {
println!("Page fault: {:>08X} {:#?}", cr2.data(), arch_flags);
stack.dump();
unsafe { stack_trace() };
stack.trace();
excp_handler(Exception {
kind: 14,
code,
@@ -227,8 +216,7 @@ interrupt_error!(page, |stack, code| {
#[cfg(target_arch = "x86_64")]
if crate::memory::page_fault_handler(stack, generic_flags, cr2).is_err() {
println!("Page fault: {:>016X} {:#?}", cr2.data(), arch_flags);
stack.dump();
unsafe { stack_trace() };
stack.trace();
excp_handler(Exception {
kind: 14,
code,
@@ -239,8 +227,7 @@ interrupt_error!(page, |stack, code| {
interrupt_stack!(fpu_fault, |stack| {
println!("FPU floating point fault");
stack.dump();
unsafe { stack_trace() };
stack.trace();
excp_handler(Exception {
kind: 16,
..Default::default()
@@ -249,8 +236,7 @@ interrupt_stack!(fpu_fault, |stack| {
interrupt_error!(alignment_check, |stack, code| {
println!("Alignment check fault");
stack.dump();
unsafe { stack_trace() };
stack.trace();
excp_handler(Exception {
kind: 17,
code,
@@ -260,9 +246,8 @@ interrupt_error!(alignment_check, |stack, code| {
interrupt_stack!(machine_check, @paranoid, |stack| {
println!("Machine check fault");
stack.dump();
stack.trace();
unsafe {
stack_trace();
loop {
interrupt::disable();
interrupt::halt();
@@ -272,11 +257,10 @@ interrupt_stack!(machine_check, @paranoid, |stack| {
interrupt_stack!(simd, |stack| {
println!("SIMD floating point fault");
stack.dump();
let mut mxcsr = 0_usize;
unsafe { core::arch::asm!("stmxcsr [{}]", in(reg) core::ptr::addr_of_mut!(mxcsr)) };
println!("MXCSR {:#0x}", mxcsr);
unsafe { stack_trace() };
stack.trace();
excp_handler(Exception {
kind: 19,
..Default::default()
@@ -285,9 +269,8 @@ interrupt_stack!(simd, |stack| {
interrupt_stack!(virtualization, |stack| {
println!("Virtualization fault");
stack.dump();
stack.trace();
unsafe {
stack_trace();
loop {
interrupt::disable();
interrupt::halt();
@@ -297,9 +280,8 @@ interrupt_stack!(virtualization, |stack| {
interrupt_error!(security, |stack, _code| {
println!("Security exception");
stack.dump();
stack.trace();
unsafe {
stack_trace();
loop {
interrupt::disable();
interrupt::halt();
+53 -3
View File
@@ -1,6 +1,6 @@
//! Intrinsics for panic handling
use core::{panic::PanicInfo, slice};
use core::{mem, panic::PanicInfo, slice};
#[cfg(target_pointer_width = "32")]
use object::elf::FileHeader32 as FileHeader;
@@ -16,10 +16,11 @@ use rustc_demangle::demangle;
use crate::{
arch::{consts::USER_END_OFFSET, interrupt::trace::StackTrace},
context, cpu_id, interrupt,
context, cpu_id,
interrupt::{self, InterruptStack},
memory::KernelMapper,
sync::CleanLockToken,
syscall,
syscall::{self, usercopy::UserSliceRo},
};
/// Required to handle panics
@@ -147,3 +148,52 @@ pub unsafe fn stack_trace() {
}
}
}
/// Get a user stack trace
#[inline(never)]
pub unsafe fn user_stack_trace(stack: &InterruptStack) {
let mut fp = stack.frame_pointer();
let sp = stack.stack_pointer();
if fp < sp {
println!(" <Unable to generate stack while frame pointers omitted>");
return;
}
if fp >= crate::USER_END_OFFSET {
return;
}
for _ in 0..64 {
if fp == 0 || fp >= crate::USER_END_OFFSET {
break;
}
let rip_addr = fp + size_of::<usize>();
let rip = match UserSliceRo::new(rip_addr, mem::size_of::<usize>())
.and_then(|x| x.read_usize())
{
Ok(val) => val,
Err(err) => {
println!(" <Failed to read RIP 0x{:>016x}>: {}", fp, err);
break;
}
};
println!(" FP {:>016x}: PC {:>016x}", fp, rip);
if rip == 0 {
break;
}
let next_fp =
match UserSliceRo::new(fp, mem::size_of::<usize>()).and_then(|x| x.read_usize()) {
Ok(val) => val,
Err(_err) => break,
};
if next_fp <= fp {
println!(
" <Invalid next frame pointer 0x{:>016x}; stack walk ended>",
next_fp
);
break;
}
fp = next_fp;
}
}