diff --git a/Cargo.toml b/Cargo.toml index 7e1dd15..33d4666 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,7 +29,8 @@ hide-trace = [] personality = [] personality-dummy = [] print = ["libc"] -panic = ["alloc"] +panicking = [] +panic = ["panicking", "alloc"] panic-handler = ["print", "panic"] panic-handler-dummy = [] system-alloc = [] diff --git a/README.md b/README.md index 4f99a69..ebc069f 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,8 @@ Here are the feature gates related: |---------------|---------|-| | personality | No | Provides `#[lang = eh_personality]` | | print | No | Provides `(e)?print(ln)?`. This is really only here because panic handler needs to provide things. Depends on libc. | -| panic | No | Provides `begin_panic` and `catch_unwind`. Only stack unwinding functionality is provided and no printing is done, because this feature does not depend on libc. | +| panicking | No | Provides a generic `begin_panic` and `catch_unwind`. Only stack unwinding functionality is provided, memory allocation and panic handling is left to the user. | +| panic | No | Provides Rust `begin_panic` and `catch_unwind`. Only stack unwinding functionality is provided and no printing is done, because this feature does not depend on libc. | | panic-handler | No | Provides `#[panic_handler]`. Provides similar behaviour on panic to std, with `RUST_BACKTRACE` support as well. Stack trace won't have symbols though. Depends on libc. | | system-alloc | No | Provides a global allocator which calls `malloc` and friends. Provided for convience. | diff --git a/src/abi.rs b/src/abi.rs index 277350e..9970a69 100644 --- a/src/abi.rs +++ b/src/abi.rs @@ -75,13 +75,6 @@ pub struct UnwindException { private: [usize; Arch::UNWIND_PRIVATE_DATA_SIZE], } -impl UnwindException { - #[inline] - pub fn new() -> UnwindException { - unsafe { core::mem::zeroed() } - } -} - pub type UnwindTraceFn = extern "C" fn(ctx: &mut UnwindContext<'_>, arg: *mut c_void) -> UnwindReasonCode; diff --git a/src/lib.rs b/src/lib.rs index 0d2b7a3..6aa442c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,7 +6,7 @@ feature(lang_items) )] #![cfg_attr( - any(feature = "panic", feature = "panic-handler-dummy"), + any(feature = "panicking", feature = "panic-handler-dummy"), feature(core_intrinsics) )] #![cfg_attr(feature = "panic-handler", feature(thread_local))] @@ -35,6 +35,8 @@ mod personality_dummy; #[cfg(feature = "panic")] pub mod panic; +#[cfg(feature = "panicking")] +pub mod panicking; #[cfg(feature = "panic-handler")] pub mod panic_handler; diff --git a/src/panic.rs b/src/panic.rs index 039c0b1..f25bc69 100644 --- a/src/panic.rs +++ b/src/panic.rs @@ -1,106 +1,70 @@ use alloc::boxed::Box; use core::any::Any; -use core::mem::ManuallyDrop; +use core::mem::MaybeUninit; use crate::abi::*; #[cfg(feature = "panic-handler")] -pub use crate::panic_handler::*; +use crate::panic_handler::*; +use crate::panicking::Exception; -#[repr(C)] -struct Exception { - exception: UnwindException, - payload: Box, -} +#[repr(transparent)] +struct RustPanic(Box, ForeignGuard); -const RUST_EXCEPTION_CLASS: u64 = u64::from_be_bytes(*b"MOZ\0RUST"); +struct ForeignGuard; -pub fn begin_panic(payload: Box) -> UnwindReasonCode { - unsafe extern "C" fn exception_cleanup( - _unwind_code: UnwindReasonCode, - exception: *mut UnwindException, - ) { - unsafe { - let _ = Box::from_raw(exception as *mut Exception); - } - #[cfg(feature = "panic-handler")] - { - drop_panic(); - } - #[cfg(not(feature = "panic-handler"))] - { - core::intrinsics::abort(); - } - } - - let mut unwind_ex = UnwindException::new(); - unwind_ex.exception_class = RUST_EXCEPTION_CLASS; - unwind_ex.exception_cleanup = Some(exception_cleanup); - let exception = Box::new(Exception { - exception: unwind_ex, - payload, - }); - _Unwind_RaiseException(unsafe { &mut *(Box::into_raw(exception) as *mut UnwindException) }) -} - -#[cold] -unsafe fn cleanup(payload: *mut u8) -> Box { - let exception = payload as *mut UnwindException; - if unsafe { (*exception).exception_class } != RUST_EXCEPTION_CLASS { - unsafe { _Unwind_DeleteException(exception) }; +impl Drop for ForeignGuard { + fn drop(&mut self) { #[cfg(feature = "panic-handler")] { foreign_exception(); } - #[cfg(not(feature = "panic-handler"))] - { - core::intrinsics::abort(); - } + core::intrinsics::abort(); } - #[cfg(feature = "panic-handler")] - { - panic_caught(); +} + +#[repr(C)] +struct ExceptionWithPayload { + exception: MaybeUninit, + payload: RustPanic, +} + +unsafe impl Exception for RustPanic { + const CLASS: [u8; 8] = *b"MOZ\0RUST"; + + fn wrap(this: Self) -> *mut UnwindException { + Box::into_raw(Box::new(ExceptionWithPayload { + exception: MaybeUninit::uninit(), + payload: this, + })) as *mut UnwindException } - let unwind_ex = unsafe { Box::from_raw(exception as *mut Exception) }; - unwind_ex.payload + + unsafe fn unwrap(ex: *mut UnwindException) -> Self { + let ex = unsafe { Box::from_raw(ex as *mut ExceptionWithPayload) }; + ex.payload + } +} + +pub fn begin_panic(payload: Box) -> UnwindReasonCode { + crate::panicking::begin_panic(RustPanic(payload, ForeignGuard)) } pub fn catch_unwind R>(f: F) -> Result> { - union Data { - f: ManuallyDrop, - r: ManuallyDrop, - p: ManuallyDrop>, - } - - let mut data = Data { - f: ManuallyDrop::new(f), - }; - - let data_ptr = &mut data as *mut _ as *mut u8; - unsafe { - return if core::intrinsics::r#try(do_call::, data_ptr, do_catch::) == 0 { - Ok(ManuallyDrop::into_inner(data.r)) - } else { - Err(ManuallyDrop::into_inner(data.p)) - }; - } - - #[inline] - fn do_call R, R>(data: *mut u8) { - unsafe { - let data = data as *mut Data; - let data = &mut (*data); - let f = ManuallyDrop::take(&mut data.f); - data.r = ManuallyDrop::new(f()); - } - } - - #[inline] - fn do_catch R, R>(data: *mut u8, payload: *mut u8) { - unsafe { - let data = data as *mut Data; - let data = &mut (*data); - let obj = cleanup(payload); - data.p = ManuallyDrop::new(obj); + #[cold] + fn process_panic(p: Option) -> Box { + match p { + None => { + #[cfg(feature = "panic-handler")] + { + drop_panic(); + } + core::intrinsics::abort(); + } + Some(e) => { + panic_caught(); + core::mem::forget(e.1); + e.0 + } } } + crate::panicking::catch_unwind(f).map_err(process_panic) } diff --git a/src/panicking.rs b/src/panicking.rs new file mode 100644 index 0000000..24da451 --- /dev/null +++ b/src/panicking.rs @@ -0,0 +1,71 @@ +use core::mem::ManuallyDrop; + +use crate::abi::*; + +pub unsafe trait Exception { + const CLASS: [u8; 8]; + + fn wrap(this: Self) -> *mut UnwindException; + unsafe fn unwrap(ex: *mut UnwindException) -> Self; +} + +pub fn begin_panic(exception: E) -> UnwindReasonCode { + unsafe extern "C" fn exception_cleanup( + _unwind_code: UnwindReasonCode, + exception: *mut UnwindException, + ) { + unsafe { E::unwrap(exception) }; + } + + let ex = E::wrap(exception); + unsafe { + (*ex).exception_class = u64::from_be_bytes(E::CLASS); + (*ex).exception_cleanup = Some(exception_cleanup::); + _Unwind_RaiseException(&mut *ex) + } +} + +pub fn catch_unwind R>(f: F) -> Result> { + #[repr(C)] + union Data { + f: ManuallyDrop, + r: ManuallyDrop, + p: ManuallyDrop>, + } + + let mut data = Data { + f: ManuallyDrop::new(f), + }; + + let data_ptr = &mut data as *mut _ as *mut u8; + unsafe { + return if core::intrinsics::r#try(do_call::, data_ptr, do_catch::) == 0 { + Ok(ManuallyDrop::into_inner(data.r)) + } else { + Err(ManuallyDrop::into_inner(data.p)) + }; + } + + #[inline] + fn do_call R, R>(data: *mut u8) { + unsafe { + let data = &mut *(data as *mut Data); + let f = ManuallyDrop::take(&mut data.f); + data.r = ManuallyDrop::new(f()); + } + } + + #[cold] + fn do_catch(data: *mut u8, exception: *mut u8) { + unsafe { + let data = &mut *(data as *mut ManuallyDrop>); + let exception = exception as *mut UnwindException; + if (*exception).exception_class != u64::from_be_bytes(E::CLASS) { + _Unwind_DeleteException(exception); + *data = ManuallyDrop::new(None); + return; + } + *data = ManuallyDrop::new(Some(E::unwrap(exception))); + } + } +}