Add generic panicking support
This commit is contained in:
parent
8241603500
commit
88d34ea7ad
@ -29,7 +29,8 @@ hide-trace = []
|
|||||||
personality = []
|
personality = []
|
||||||
personality-dummy = []
|
personality-dummy = []
|
||||||
print = ["libc"]
|
print = ["libc"]
|
||||||
panic = ["alloc"]
|
panicking = []
|
||||||
|
panic = ["panicking", "alloc"]
|
||||||
panic-handler = ["print", "panic"]
|
panic-handler = ["print", "panic"]
|
||||||
panic-handler-dummy = []
|
panic-handler-dummy = []
|
||||||
system-alloc = []
|
system-alloc = []
|
||||||
|
@ -44,7 +44,8 @@ Here are the feature gates related:
|
|||||||
|---------------|---------|-|
|
|---------------|---------|-|
|
||||||
| personality | No | Provides `#[lang = eh_personality]` |
|
| 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. |
|
| 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. |
|
| 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. |
|
| 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],
|
private: [usize; Arch::UNWIND_PRIVATE_DATA_SIZE],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UnwindException {
|
|
||||||
#[inline]
|
|
||||||
pub fn new() -> UnwindException {
|
|
||||||
unsafe { core::mem::zeroed() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type UnwindTraceFn =
|
pub type UnwindTraceFn =
|
||||||
extern "C" fn(ctx: &mut UnwindContext<'_>, arg: *mut c_void) -> UnwindReasonCode;
|
extern "C" fn(ctx: &mut UnwindContext<'_>, arg: *mut c_void) -> UnwindReasonCode;
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
feature(lang_items)
|
feature(lang_items)
|
||||||
)]
|
)]
|
||||||
#![cfg_attr(
|
#![cfg_attr(
|
||||||
any(feature = "panic", feature = "panic-handler-dummy"),
|
any(feature = "panicking", feature = "panic-handler-dummy"),
|
||||||
feature(core_intrinsics)
|
feature(core_intrinsics)
|
||||||
)]
|
)]
|
||||||
#![cfg_attr(feature = "panic-handler", feature(thread_local))]
|
#![cfg_attr(feature = "panic-handler", feature(thread_local))]
|
||||||
@ -35,6 +35,8 @@ mod personality_dummy;
|
|||||||
|
|
||||||
#[cfg(feature = "panic")]
|
#[cfg(feature = "panic")]
|
||||||
pub mod panic;
|
pub mod panic;
|
||||||
|
#[cfg(feature = "panicking")]
|
||||||
|
pub mod panicking;
|
||||||
|
|
||||||
#[cfg(feature = "panic-handler")]
|
#[cfg(feature = "panic-handler")]
|
||||||
pub mod panic_handler;
|
pub mod panic_handler;
|
||||||
|
126
src/panic.rs
126
src/panic.rs
@ -1,106 +1,70 @@
|
|||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
use core::any::Any;
|
use core::any::Any;
|
||||||
use core::mem::ManuallyDrop;
|
use core::mem::MaybeUninit;
|
||||||
|
|
||||||
use crate::abi::*;
|
use crate::abi::*;
|
||||||
#[cfg(feature = "panic-handler")]
|
#[cfg(feature = "panic-handler")]
|
||||||
pub use crate::panic_handler::*;
|
use crate::panic_handler::*;
|
||||||
|
use crate::panicking::Exception;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(transparent)]
|
||||||
struct Exception {
|
struct RustPanic(Box<dyn Any + Send>, ForeignGuard);
|
||||||
exception: UnwindException,
|
|
||||||
payload: Box<dyn Any + Send>,
|
|
||||||
}
|
|
||||||
|
|
||||||
const RUST_EXCEPTION_CLASS: u64 = u64::from_be_bytes(*b"MOZ\0RUST");
|
struct ForeignGuard;
|
||||||
|
|
||||||
pub fn begin_panic(payload: Box<dyn Any + Send>) -> UnwindReasonCode {
|
impl Drop for ForeignGuard {
|
||||||
unsafe extern "C" fn exception_cleanup(
|
fn drop(&mut self) {
|
||||||
_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) };
|
|
||||||
#[cfg(feature = "panic-handler")]
|
#[cfg(feature = "panic-handler")]
|
||||||
{
|
{
|
||||||
foreign_exception();
|
foreign_exception();
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "panic-handler"))]
|
|
||||||
{
|
|
||||||
core::intrinsics::abort();
|
core::intrinsics::abort();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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
|
||||||
}
|
}
|
||||||
#[cfg(feature = "panic-handler")]
|
|
||||||
{
|
unsafe fn unwrap(ex: *mut UnwindException) -> Self {
|
||||||
panic_caught();
|
let ex = unsafe { Box::from_raw(ex as *mut ExceptionWithPayload) };
|
||||||
|
ex.payload
|
||||||
}
|
}
|
||||||
let unwind_ex = unsafe { Box::from_raw(exception as *mut Exception) };
|
}
|
||||||
unwind_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>> {
|
pub fn catch_unwind<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>> {
|
||||||
union Data<F, R> {
|
#[cold]
|
||||||
f: ManuallyDrop<F>,
|
fn process_panic(p: Option<RustPanic>) -> Box<dyn Any + Send> {
|
||||||
r: ManuallyDrop<R>,
|
match p {
|
||||||
p: ManuallyDrop<Box<dyn Any + Send>>,
|
None => {
|
||||||
|
#[cfg(feature = "panic-handler")]
|
||||||
|
{
|
||||||
|
drop_panic();
|
||||||
}
|
}
|
||||||
|
core::intrinsics::abort();
|
||||||
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))
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
Some(e) => {
|
||||||
#[inline]
|
panic_caught();
|
||||||
fn do_call<F: FnOnce() -> R, R>(data: *mut u8) {
|
core::mem::forget(e.1);
|
||||||
unsafe {
|
e.0
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
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