Add generic panicking support
This commit is contained in:
parent
8241603500
commit
88d34ea7ad
@ -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 = []
|
||||
|
@ -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. |
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
136
src/panic.rs
136
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<dyn Any + Send>,
|
||||
}
|
||||
#[repr(transparent)]
|
||||
struct RustPanic(Box<dyn Any + Send>, ForeignGuard);
|
||||
|
||||
const RUST_EXCEPTION_CLASS: u64 = u64::from_be_bytes(*b"MOZ\0RUST");
|
||||
struct ForeignGuard;
|
||||
|
||||
pub fn begin_panic(payload: Box<dyn Any + Send>) -> 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<dyn Any + Send + 'static> {
|
||||
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<UnwindException>,
|
||||
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<dyn Any + Send>) -> UnwindReasonCode {
|
||||
crate::panicking::begin_panic(RustPanic(payload, ForeignGuard))
|
||||
}
|
||||
|
||||
pub fn catch_unwind<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>> {
|
||||
union Data<F, R> {
|
||||
f: ManuallyDrop<F>,
|
||||
r: ManuallyDrop<R>,
|
||||
p: ManuallyDrop<Box<dyn Any + Send>>,
|
||||
}
|
||||
|
||||
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::<F, R>, data_ptr, do_catch::<F, R>) == 0 {
|
||||
Ok(ManuallyDrop::into_inner(data.r))
|
||||
} else {
|
||||
Err(ManuallyDrop::into_inner(data.p))
|
||||
};
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn do_call<F: FnOnce() -> R, R>(data: *mut u8) {
|
||||
unsafe {
|
||||
let data = data as *mut Data<F, R>;
|
||||
let data = &mut (*data);
|
||||
let f = ManuallyDrop::take(&mut data.f);
|
||||
data.r = ManuallyDrop::new(f());
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn do_catch<F: FnOnce() -> R, R>(data: *mut u8, payload: *mut u8) {
|
||||
unsafe {
|
||||
let data = data as *mut Data<F, R>;
|
||||
let data = &mut (*data);
|
||||
let obj = cleanup(payload);
|
||||
data.p = ManuallyDrop::new(obj);
|
||||
#[cold]
|
||||
fn process_panic(p: Option<RustPanic>) -> Box<dyn Any + Send> {
|
||||
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)
|
||||
}
|
||||
|
71
src/panicking.rs
Normal file
71
src/panicking.rs
Normal file
@ -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<E: Exception>(exception: E) -> UnwindReasonCode {
|
||||
unsafe extern "C" fn exception_cleanup<E: Exception>(
|
||||
_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::<E>);
|
||||
_Unwind_RaiseException(&mut *ex)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn catch_unwind<E: Exception, R, F: FnOnce() -> R>(f: F) -> Result<R, Option<E>> {
|
||||
#[repr(C)]
|
||||
union Data<F, R, E> {
|
||||
f: ManuallyDrop<F>,
|
||||
r: ManuallyDrop<R>,
|
||||
p: ManuallyDrop<Option<E>>,
|
||||
}
|
||||
|
||||
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::<F, R>, data_ptr, do_catch::<E>) == 0 {
|
||||
Ok(ManuallyDrop::into_inner(data.r))
|
||||
} else {
|
||||
Err(ManuallyDrop::into_inner(data.p))
|
||||
};
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn do_call<F: FnOnce() -> R, R>(data: *mut u8) {
|
||||
unsafe {
|
||||
let data = &mut *(data as *mut Data<F, R, ()>);
|
||||
let f = ManuallyDrop::take(&mut data.f);
|
||||
data.r = ManuallyDrop::new(f());
|
||||
}
|
||||
}
|
||||
|
||||
#[cold]
|
||||
fn do_catch<E: Exception>(data: *mut u8, exception: *mut u8) {
|
||||
unsafe {
|
||||
let data = &mut *(data as *mut ManuallyDrop<Option<E>>);
|
||||
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)));
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user