use super::{
super::{abort, intrinsic::intercept},
RethrowHandle, ThrowByValue,
};
use alloc::boxed::Box;
use core::any::Any;
use core::marker::{FnPtr, PhantomData};
use core::mem::ManuallyDrop;
use core::panic::PanicPayload;
use core::sync::atomic::{AtomicU32, Ordering};
pub(crate) struct ActiveBackend;
unsafe impl ThrowByValue for ActiveBackend {
type RethrowHandle<E> = SehRethrowHandle;
#[inline(always)]
unsafe fn throw<E>(cause: E) -> ! {
CATCHABLE_TYPE
.type_descriptor
.write(SmallPtr::new(&raw const TYPE_DESCRIPTOR));
CATCHABLE_TYPE.copy_function.write(SmallPtr::new_fn(copy));
CATCHABLE_TYPE_ARRAY.catchable_types[0].write(SmallPtr::new(&raw const CATCHABLE_TYPE));
THROW_INFO.destructor.write(SmallPtr::new_fn(cleanup));
THROW_INFO
.catchable_type_array
.write(SmallPtr::new(&raw const CATCHABLE_TYPE_ARRAY));
unsafe {
do_throw(cause);
}
}
#[inline(always)]
unsafe fn intercept<Func: FnOnce() -> R, R, E>(func: Func) -> Result<R, (E, SehRethrowHandle)> {
enum CaughtUnwind<E> {
LithiumException(E),
RustPanic(Box<dyn Any + Send + 'static>),
}
let catch = |ex: *mut u8| {
if ex.is_null() {
abort(
"Lithium caught a foreign exception. This is unsupported. The process will now terminate.\n",
);
}
let ex_lithium: *mut Exception<E> = ex.cast();
if unsafe { (*ex_lithium).header.canary } != (&raw const THROW_INFO).cast() {
let payload = unsafe { __rust_panic_cleanup(ex) };
let payload = unsafe { Box::from_raw(payload) };
return CaughtUnwind::RustPanic(payload);
}
unsafe {
(*ex_lithium).header.caught = true;
}
let cause = unsafe { &mut (*ex_lithium).cause };
CaughtUnwind::LithiumException(unsafe { ManuallyDrop::take(cause) })
};
match intercept(func, catch) {
Ok(value) => Ok(value),
Err(CaughtUnwind::LithiumException(cause)) => Err((cause, SehRethrowHandle)),
Err(CaughtUnwind::RustPanic(payload)) => throw_std_panic(payload),
}
}
}
#[derive(Debug)]
pub(crate) struct SehRethrowHandle;
impl RethrowHandle for SehRethrowHandle {
#[inline(never)]
unsafe fn rethrow<F>(self, new_cause: F) -> ! {
unsafe {
do_throw(new_cause);
}
}
}
unsafe fn do_throw<E>(cause: E) -> ! {
let mut exception = Exception {
header: ExceptionHeader {
canary: (&raw const THROW_INFO).cast(), caught: false,
},
cause: ManuallyDrop::new(cause),
};
unsafe {
cxx_throw((&raw mut exception).cast(), &raw const THROW_INFO);
}
}
#[repr(C)]
struct ExceptionHeader {
canary: *const (), caught: bool,
}
#[repr(C)]
struct Exception<E> {
header: ExceptionHeader,
cause: ManuallyDrop<E>,
}
#[cfg(target_arch = "x86")]
macro_rules! thiscall {
($(#[$outer:meta])* fn $($tt:tt)*) => {
$(#[$outer])* unsafe extern "thiscall" fn $($tt)*
};
}
#[cfg(not(target_arch = "x86"))]
macro_rules! thiscall {
($(#[$outer:meta])* fn $($tt:tt)*) => {
$(#[$outer])* unsafe extern "C" fn $($tt)*
};
}
#[repr(C)]
struct ExceptionRecordParameters {
magic: usize,
exception_object: *mut ExceptionHeader,
throw_info: *const ThrowInfo,
#[cfg(target_pointer_width = "64")]
image_base: *const u8,
}
#[repr(C)]
struct ThrowInfo {
attributes: u32,
destructor: SmallPtr<thiscall!(fn(*mut ExceptionHeader))>,
forward_compat: SmallPtr<fn()>,
catchable_type_array: SmallPtr<*const CatchableTypeArray>,
}
#[repr(C)]
struct CatchableTypeArray {
n_types: i32,
catchable_types: [SmallPtr<*const CatchableType>; 1],
}
#[repr(C)]
struct CatchableType {
properties: u32,
type_descriptor: SmallPtr<*const TypeDescriptor>,
this_displacement: PointerToMemberData,
size_or_offset: i32,
copy_function: SmallPtr<
thiscall!(fn(*mut ExceptionHeader, *const ExceptionHeader) -> *mut ExceptionHeader),
>,
}
#[repr(C)]
struct TypeDescriptor {
vtable: *const *const (),
reserved: usize,
name: [u8; 11], }
unsafe impl Sync for TypeDescriptor {}
#[repr(C)]
struct PointerToMemberData {
member_displacement: i32,
virtual_base_pointer_displacement: i32,
vdisp: i32, }
const EH_EXCEPTION_NUMBER: u32 = u32::from_be_bytes(*b"\xe0msc");
const EH_NONCONTINUABLE: u32 = 1;
const EH_MAGIC_NUMBER1: usize = 0x1993_0520;
static TYPE_DESCRIPTOR: TypeDescriptor = TypeDescriptor {
vtable: &raw const TYPE_INFO_VTABLE,
reserved: 0,
name: *b"rust_panic\0",
};
static CATCHABLE_TYPE: CatchableType = CatchableType {
properties: 0,
type_descriptor: SmallPtr::null(), this_displacement: PointerToMemberData {
member_displacement: 0,
virtual_base_pointer_displacement: -1,
vdisp: 0,
},
size_or_offset: 1,
copy_function: SmallPtr::null(), };
static CATCHABLE_TYPE_ARRAY: CatchableTypeArray = CatchableTypeArray {
n_types: 1,
catchable_types: [
SmallPtr::null(), ],
};
static THROW_INFO: ThrowInfo = ThrowInfo {
attributes: 0,
destructor: SmallPtr::null(), forward_compat: SmallPtr::null(),
catchable_type_array: SmallPtr::null(), };
fn abort_on_caught_by_cxx() -> ! {
abort("A Lithium exception was caught by a non-Lithium catch mechanism. This is undefined behavior. The process will now terminate.\n");
}
thiscall! {
fn cleanup(ex: *mut ExceptionHeader) {
if !unsafe { (*ex).caught } {
abort_on_caught_by_cxx();
}
}
}
thiscall! {
fn copy(_to: *mut ExceptionHeader, _from: *const ExceptionHeader) -> *mut ExceptionHeader {
abort_on_caught_by_cxx();
}
}
unsafe extern "C" {
#[cfg(target_pointer_width = "64")]
static __ImageBase: u8;
#[link_name = "\x01??_7type_info@@6B@"]
static TYPE_INFO_VTABLE: *const ();
}
#[repr(transparent)]
struct SmallPtr<P> {
value: AtomicU32,
phantom: PhantomData<P>,
}
unsafe impl<P> Sync for SmallPtr<P> {}
impl<P> SmallPtr<P> {
#[inline]
fn from_erased(p: *const ()) -> Self {
#[cfg(target_pointer_width = "32")]
let value = p.expose_provenance() as u32;
#[cfg(target_pointer_width = "64")]
#[expect(
clippy::cast_possible_truncation,
reason = "PE images are at most 4 GiB long"
)]
let value = p
.expose_provenance()
.wrapping_sub((&raw const __ImageBase).addr()) as u32;
Self {
value: AtomicU32::new(value),
phantom: PhantomData,
}
}
const fn null() -> Self {
Self {
value: AtomicU32::new(0),
phantom: PhantomData,
}
}
fn write(&self, rhs: SmallPtr<P>) {
self.value.store(rhs.value.into_inner(), Ordering::Relaxed);
}
}
impl<P: FnPtr> SmallPtr<P> {
fn new_fn(p: P) -> Self {
Self::from_erased(p.addr())
}
}
impl<T: ?Sized> SmallPtr<*const T> {
fn new(p: *const T) -> Self {
Self::from_erased(p.cast())
}
}
unsafe extern "system-unwind" {
fn RaiseException(
code: u32,
flags: u32,
n_parameters: u32,
paremeters: *mut ExceptionRecordParameters,
) -> !;
}
unsafe extern "Rust" {
#[rustc_std_internal_symbol]
safe fn __rust_start_panic(payload: &mut dyn PanicPayload) -> u32;
}
unsafe extern "C" {
#[expect(improper_ctypes, reason = "Copied from std")]
#[rustc_std_internal_symbol]
fn __rust_panic_cleanup(payload: *mut u8) -> *mut (dyn Any + Send + 'static);
}
fn throw_std_panic(payload: Box<dyn Any + Send + 'static>) -> ! {
struct RewrapBox(Box<dyn Any + Send + 'static>);
unsafe impl PanicPayload for RewrapBox {
fn take_box(&mut self) -> *mut (dyn Any + Send) {
Box::into_raw(core::mem::replace(&mut self.0, Box::new(())))
}
fn get(&mut self) -> &(dyn Any + Send) {
&*self.0
}
}
impl core::fmt::Display for RewrapBox {
fn fmt(&self, _f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
unreachable!()
}
}
__rust_start_panic(&mut RewrapBox(payload));
core::intrinsics::abort();
}
#[inline(always)]
unsafe fn cxx_throw(exception_object: *mut ExceptionHeader, throw_info: *const ThrowInfo) -> ! {
#[expect(clippy::cast_possible_truncation, reason = "This is a constant")]
const N_PARAMETERS: u32 =
(core::mem::size_of::<ExceptionRecordParameters>() / core::mem::size_of::<usize>()) as u32;
let mut parameters = ExceptionRecordParameters {
magic: EH_MAGIC_NUMBER1,
exception_object,
throw_info,
#[cfg(target_pointer_width = "64")]
image_base: &raw const __ImageBase,
};
unsafe {
RaiseException(
EH_EXCEPTION_NUMBER,
EH_NONCONTINUABLE,
N_PARAMETERS,
&raw mut parameters,
);
}
}