Explorar o código

Add generic panicking support

Gary Guo %!s(int64=3) %!d(string=hai) anos
pai
achega
88d34ea7ad
Modificáronse 6 ficheiros con 124 adicións e 92 borrados
  1. 2 1
      Cargo.toml
  2. 2 1
      README.md
  3. 0 7
      src/abi.rs
  4. 3 1
      src/lib.rs
  5. 46 82
      src/panic.rs
  6. 71 0
      src/panicking.rs

+ 2 - 1
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 = []

+ 2 - 1
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. |
 

+ 0 - 7
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;
 

+ 3 - 1
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;

+ 46 - 82
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");
-
-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) })
-}
+struct ForeignGuard;
 
-#[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();
-    }
-    let unwind_ex = unsafe { Box::from_raw(exception as *mut Exception) };
-    unwind_ex.payload
 }
 
-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>>,
-    }
+#[repr(C)]
+struct ExceptionWithPayload {
+    exception: MaybeUninit<UnwindException>,
+    payload: RustPanic,
+}
 
-    let mut data = Data {
-        f: ManuallyDrop::new(f),
-    };
+unsafe impl Exception for RustPanic {
+    const CLASS: [u8; 8] = *b"MOZ\0RUST";
 
-    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))
-        };
+    fn wrap(this: Self) -> *mut UnwindException {
+        Box::into_raw(Box::new(ExceptionWithPayload {
+            exception: MaybeUninit::uninit(),
+            payload: this,
+        })) as *mut UnwindException
     }
 
-    #[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());
-        }
+    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))
+}
 
-    #[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);
+pub fn catch_unwind<R, F: FnOnce() -> R>(f: F) -> Result<R, Box<dyn Any + Send>> {
+    #[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 - 0
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<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)));
+        }
+    }
+}