From b19c98d4a36fa456d9bf4ea4e058e9e3265c2a7a Mon Sep 17 00:00:00 2001 From: Godones Date: Sat, 1 Nov 2025 17:26:12 +0800 Subject: [PATCH] add unwinding support Signed-off-by: Godones --- Cargo.lock | 22 +++++++- Cargo.toml | 10 ++++ arceos | 2 +- src/main.rs | 7 ++- src/panic.rs | 148 +++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 186 insertions(+), 3 deletions(-) create mode 100644 src/panic.rs diff --git a/Cargo.lock b/Cargo.lock index 48317c97..6b693d6c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -478,6 +478,16 @@ dependencies = [ "log", ] +[[package]] +name = "axmacros" +version = "0.1.0" +source = "git+https://github.com/Starry-OS/axmacros#eeda6822ad83c4bf3e8df3d247fd524715940108" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "axmm" version = "0.2.0" @@ -677,7 +687,6 @@ name = "axruntime" version = "0.2.0" dependencies = [ "axalloc", - "axbacktrace", "axconfig", "axdisplay", "axdriver", @@ -2104,6 +2113,7 @@ dependencies = [ "axfs-ng", "axhal", "axlog", + "axmacros", "axplat-riscv64-visionfive2", "axruntime", "axsync", @@ -2115,6 +2125,7 @@ dependencies = [ "starry-core", "starry-process", "starry-signal", + "unwinding", ] [[package]] @@ -2451,6 +2462,15 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" +[[package]] +name = "unwinding" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60612c845ef41699f39dc8c5391f252942c0a88b7d15da672eff0d14101bbd6d" +dependencies = [ + "gimli", +] + [[package]] name = "utf8parse" version = "0.2.2" diff --git a/Cargo.toml b/Cargo.toml index d6f0fe0b..3d628a01 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,6 +73,7 @@ starry-vm = "0.2" starry-core = { path = "./core" } starry-api = { path = "./api" } +axmacros = { git = "https://github.com/Starry-OS/axmacros" } [package] name = "starry" @@ -131,6 +132,15 @@ starry-signal.workspace = true starry-core.workspace = true starry-api.workspace = true +axmacros.workspace = true + +[target.'cfg(not(target_arch = "loongarch64"))'.dependencies] +unwinding = { version = "=0.2.8", default-features = false, features = [ + "unwinder", + "fde-gnu-eh-frame-hdr", + "panic", + "personality", +] } [dependencies.axplat-riscv64-visionfive2] git = "https://github.com/Starry-OS/axplat-riscv64-visionfive2.git" diff --git a/arceos b/arceos index a5f554a1..ff7a381c 160000 --- a/arceos +++ b/arceos @@ -1 +1 @@ -Subproject commit a5f554a11367dc9a591811b7f3e150e94ac76d5e +Subproject commit ff7a381c94378cfbd03d279c94696115692c084f diff --git a/src/main.rs b/src/main.rs index 0db6518e..a6d9ee99 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ #![no_std] #![no_main] +#![feature(panic_can_unwind)] #![doc = include_str!("../README.md")] #[macro_use] @@ -13,13 +14,17 @@ use alloc::{borrow::ToOwned, vec::Vec}; use axfs_ng::FS_CONTEXT; mod entry; +mod panic; pub const CMDLINE: &[&str] = &["/bin/sh", "-c", include_str!("init.sh")]; -#[unsafe(no_mangle)] +#[axmacros::main] fn main() { starry_api::init(); + #[cfg(not(target_arch = "loongarch64"))] + panic::test_unwind(); + let args = CMDLINE .iter() .copied() diff --git a/src/panic.rs b/src/panic.rs new file mode 100644 index 00000000..2acc4260 --- /dev/null +++ b/src/panic.rs @@ -0,0 +1,148 @@ +use core::panic::PanicInfo; + +#[cfg(not(target_arch = "loongarch64"))] +#[allow(unused)] +pub use unwind::{KPanicInfo, catch_panics_as_oops}; + +#[cfg(target_arch = "loongarch64")] +#[axmacros::panic_handler] +fn panic(info: &PanicInfo) -> ! { + ax_println!("{}", info); + axhal::power::system_off() +} + +#[cfg(not(target_arch = "loongarch64"))] +mod unwind { + use alloc::boxed::Box; + use core::{ + ffi::c_void, + sync::atomic::{AtomicBool, AtomicUsize, Ordering}, + }; + + use unwinding::{ + abi::{ + _Unwind_Backtrace, _Unwind_FindEnclosingFunction, _Unwind_GetIP, UnwindContext, + UnwindReasonCode, + }, + panic, + }; + + use super::PanicInfo; + + static RECURSION: AtomicBool = AtomicBool::new(false); + #[derive(Debug)] + pub struct KPanicInfo; + + impl KPanicInfo { + pub fn new() -> Self { + Self + } + } + + #[axmacros::panic_handler] + fn panic_handler(info: &PanicInfo) -> ! { + if let Some(p) = info.location() { + ax_println!("line {}, file {}: {}", p.line(), p.file(), info.message()); + } else { + ax_println!("no location information available"); + } + if !RECURSION.swap(true, core::sync::atomic::Ordering::SeqCst) { + if info.can_unwind() { + let guard = Box::new(KPanicInfo::new()); + print_stack_trace(); + let _res = unwinding::panic::begin_panic(guard); + panic!("panic unreachable: {:?}", _res.0); + } + } + axhal::power::system_off() + } + + pub fn print_stack_trace() { + ax_println!("Rust Panic Backtrace:"); + struct CallbackData { + counter: usize, + kernel_main: bool, + } + extern "C" fn callback( + unwind_ctx: &UnwindContext<'_>, + arg: *mut c_void, + ) -> UnwindReasonCode { + let data = unsafe { &mut *(arg as *mut CallbackData) }; + if data.kernel_main { + // If we are in kernel_main, we don't need to print the backtrace. + return UnwindReasonCode::NORMAL_STOP; + } + data.counter += 1; + let pc = _Unwind_GetIP(unwind_ctx); + if pc > 0 { + let fde_initial_address = _Unwind_FindEnclosingFunction(pc as *mut c_void) as usize; + // TODO: lookup_kallsyms + ax_println!( + "#{:<2} {:#018x} - + {:#x}", + data.counter, + pc, + pc - fde_initial_address + ); + } + UnwindReasonCode::NO_REASON + } + let mut data = CallbackData { + counter: 0, + kernel_main: false, + }; + _Unwind_Backtrace(callback, &mut data as *mut _ as _); + } + + /// The maximum number of oops allowed before the kernel panics. + /// + /// It is the same as Linux's default value. + const MAX_OOPS_COUNT: usize = 10_000; + + static OOPS_COUNT: AtomicUsize = AtomicUsize::new(0); + + /// Catch panics in the given closure and treat them as kernel oops. + pub fn catch_panics_as_oops(f: F) -> Result + where + F: FnOnce() -> R, + { + let result = panic::catch_unwind(f); + + match result { + Ok(result) => Ok(result), + Err(err) => { + let info = err.downcast::().unwrap(); + + let count = OOPS_COUNT.fetch_add(1, Ordering::Relaxed); + if count >= MAX_OOPS_COUNT { + // Too many oops. Abort the kernel. + axlog::error!("Too many oops. The kernel panics."); + axhal::power::system_off(); + } + Err(*info) + } + } + } +} + +#[cfg(not(target_arch = "loongarch64"))] +pub fn test_unwind() { + struct UnwindTest; + impl Drop for UnwindTest { + fn drop(&mut self) { + ax_println!("Drop UnwindTest"); + } + } + let res1 = catch_panics_as_oops(|| { + let _unwind_test = UnwindTest; + ax_println!("Test panic..."); + panic!("Test panic"); + }); + assert!(res1.is_err()); + let res2 = catch_panics_as_oops(|| { + let _unwind_test = UnwindTest; + ax_println!("Test no panic..."); + 0 + }); + assert!(res2.is_ok()); + ax_println!("Unwind test passed."); +}