Add generic panicking support

This commit is contained in:
Gary Guo 2021-10-05 00:30:16 +01:00
parent 8241603500
commit 88d34ea7ad
6 changed files with 128 additions and 96 deletions

View File

@ -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 = []

View File

@ -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. |

View File

@ -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;

View File

@ -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;

View File

@ -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
View 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)));
}
}
}