diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh index 9480663a01d09..4e92169b3d05d 100755 --- a/src/tools/miri/ci/ci.sh +++ b/src/tools/miri/ci/ci.sh @@ -145,7 +145,7 @@ case $HOST_TARGET in TEST_TARGET=s390x-unknown-linux-gnu run_tests # big-endian architecture of choice # Partially supported targets (tier 2) BASIC="empty_main integer vec string btreemap hello hashmap heap_alloc align" # ensures we have the basics: stdout/stderr, system allocator, randomness (for HashMap initialization) - UNIX="panic/panic concurrency/simple atomic libc-mem libc-misc libc-random env num_cpus" # the things that are very similar across all Unixes, and hence easily supported there + UNIX="panic/panic panic/unwind concurrency/simple atomic libc-mem libc-misc libc-random env num_cpus" # the things that are very similar across all Unixes, and hence easily supported there TEST_TARGET=x86_64-unknown-freebsd run_tests_minimal $BASIC $UNIX threadname libc-time fs TEST_TARGET=i686-unknown-freebsd run_tests_minimal $BASIC $UNIX threadname libc-time fs TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $BASIC $UNIX pthread-sync diff --git a/src/tools/miri/src/intrinsics/atomic.rs b/src/tools/miri/src/intrinsics/atomic.rs index 0c212c45dbda7..501dbc1603e67 100644 --- a/src/tools/miri/src/intrinsics/atomic.rs +++ b/src/tools/miri/src/intrinsics/atomic.rs @@ -116,7 +116,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs index 7344846d6d9d2..0a7927d062146 100644 --- a/src/tools/miri/src/intrinsics/mod.rs +++ b/src/tools/miri/src/intrinsics/mod.rs @@ -26,7 +26,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { args: &[OpTy<'tcx, Provenance>], dest: &MPlaceTy<'tcx, Provenance>, ret: Option, - _unwind: mir::UnwindAction, + unwind: mir::UnwindAction, ) -> InterpResult<'tcx, Option>> { let this = self.eval_context_mut(); @@ -62,11 +62,16 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { args: instance.args, })) } - EmulateItemResult::NeedsJumping => { + EmulateItemResult::NeedsReturn => { trace!("{:?}", this.dump_place(&dest.clone().into())); this.return_to_block(ret)?; Ok(None) } + EmulateItemResult::NeedsUnwind => { + // Jump to the unwind block to begin unwinding. + this.unwind_to_block(unwind)?; + Ok(None) + } EmulateItemResult::AlreadyJumped => Ok(None), } } @@ -441,6 +446,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/intrinsics/simd.rs b/src/tools/miri/src/intrinsics/simd.rs index d0a78429ca851..4cde364fbc47c 100644 --- a/src/tools/miri/src/intrinsics/simd.rs +++ b/src/tools/miri/src/intrinsics/simd.rs @@ -746,7 +746,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } fn fminmax_op( diff --git a/src/tools/miri/src/shims/alloc.rs b/src/tools/miri/src/shims/alloc.rs index bd84de81e6936..ca672bdc611f5 100644 --- a/src/tools/miri/src/shims/alloc.rs +++ b/src/tools/miri/src/shims/alloc.rs @@ -87,7 +87,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } AllocatorKind::Default => { default(this)?; - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } } diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index a65da823e24cd..eccccb4a44974 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -82,11 +82,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } // The rest either implements the logic, or falls back to `lookup_exported_symbol`. - match this.emulate_foreign_item_inner(link_name, abi, args, dest, unwind)? { - EmulateItemResult::NeedsJumping => { + match this.emulate_foreign_item_inner(link_name, abi, args, dest)? { + EmulateItemResult::NeedsReturn => { trace!("{:?}", this.dump_place(&dest.clone().into())); this.return_to_block(ret)?; } + EmulateItemResult::NeedsUnwind => { + // Jump to the unwind block to begin unwinding. + this.unwind_to_block(unwind)?; + } EmulateItemResult::AlreadyJumped => (), EmulateItemResult::NotSupported => { if let Some(body) = this.lookup_exported_symbol(link_name)? { @@ -206,7 +210,6 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { abi: Abi, args: &[OpTy<'tcx, Provenance>], dest: &MPlaceTy<'tcx, Provenance>, - unwind: mir::UnwindAction, ) -> InterpResult<'tcx, EmulateItemResult> { let this = self.eval_context_mut(); @@ -218,7 +221,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // by the specified `.so` file; we should continue and check if it corresponds to // a provided shim. if this.call_native_fn(link_name, dest, args)? { - return Ok(EmulateItemResult::NeedsJumping); + return Ok(EmulateItemResult::NeedsReturn); } } @@ -263,9 +266,9 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { match link_name.as_str() { // Miri-specific extern functions "miri_start_unwind" => { - // `check_shim` happens inside `handle_miri_start_unwind`. - this.handle_miri_start_unwind(abi, link_name, args, unwind)?; - return Ok(EmulateItemResult::AlreadyJumped); + let [payload] = this.check_shim(abi, Abi::Rust, link_name, args)?; + this.handle_miri_start_unwind(payload)?; + return Ok(EmulateItemResult::NeedsUnwind); } "miri_run_provenance_gc" => { let [] = this.check_shim(abi, Abi::Rust, link_name, args)?; @@ -480,7 +483,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { "__rust_alloc" => return this.emulate_allocator(default), "miri_alloc" => { default(this)?; - return Ok(EmulateItemResult::NeedsJumping); + return Ok(EmulateItemResult::NeedsReturn); } _ => unreachable!(), } @@ -540,7 +543,7 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } "miri_dealloc" => { default(this)?; - return Ok(EmulateItemResult::NeedsJumping); + return Ok(EmulateItemResult::NeedsReturn); } _ => unreachable!(), } @@ -961,6 +964,6 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { }; // We only fall through to here if we did *not* hit the `_` arm above, // i.e., if we actually emulated the function with one of the shims. - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/mod.rs b/src/tools/miri/src/shims/mod.rs index d9c4a2282c1fc..a41a2883c9153 100644 --- a/src/tools/miri/src/shims/mod.rs +++ b/src/tools/miri/src/shims/mod.rs @@ -22,8 +22,10 @@ pub use unix::{DirTable, FdTable}; /// What needs to be done after emulating an item (a shim or an intrinsic) is done. pub enum EmulateItemResult { /// The caller is expected to jump to the return block. - NeedsJumping, - /// Jumping has already been taken care of. + NeedsReturn, + /// The caller is expected to jump to the unwind block. + NeedsUnwind, + /// Jumping to the next block has already been taken care of. AlreadyJumped, /// The item is not supported. NotSupported, diff --git a/src/tools/miri/src/shims/panic.rs b/src/tools/miri/src/shims/panic.rs index 4444d297469fb..e0e5396a455fd 100644 --- a/src/tools/miri/src/shims/panic.rs +++ b/src/tools/miri/src/shims/panic.rs @@ -13,7 +13,6 @@ use rustc_ast::Mutability; use rustc_middle::{mir, ty}; -use rustc_span::Symbol; use rustc_target::spec::abi::Abi; use rustc_target::spec::PanicStrategy; @@ -46,25 +45,15 @@ impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { /// Handles the special `miri_start_unwind` intrinsic, which is called /// by libpanic_unwind to delegate the actual unwinding process to Miri. - fn handle_miri_start_unwind( - &mut self, - abi: Abi, - link_name: Symbol, - args: &[OpTy<'tcx, Provenance>], - unwind: mir::UnwindAction, - ) -> InterpResult<'tcx> { + fn handle_miri_start_unwind(&mut self, payload: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> { let this = self.eval_context_mut(); trace!("miri_start_unwind: {:?}", this.frame().instance); - // Get the raw pointer stored in arg[0] (the panic payload). - let [payload] = this.check_shim(abi, Abi::Rust, link_name, args)?; let payload = this.read_scalar(payload)?; let thread = this.active_thread_mut(); thread.panic_payloads.push(payload); - // Jump to the unwind block to begin unwinding. - this.unwind_to_block(unwind)?; Ok(()) } diff --git a/src/tools/miri/src/shims/unix/android/foreign_items.rs b/src/tools/miri/src/shims/unix/android/foreign_items.rs index f85b8a725f2fc..590a5672f1564 100644 --- a/src/tools/miri/src/shims/unix/android/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/android/foreign_items.rs @@ -27,6 +27,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index 396011cc22afc..86dd23f31c7d6 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -639,6 +639,31 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.gen_random(ptr, len)?; this.write_scalar(Scalar::from_target_usize(len, this), dest)?; } + "_Unwind_RaiseException" => { + // This is not formally part of POSIX, but it is very wide-spread on POSIX systems. + // It was originally specified as part of the Itanium C++ ABI: + // https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html#base-throw. + // On Linux it is + // documented as part of the LSB: + // https://refspecs.linuxfoundation.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/baselib--unwind-raiseexception.html + // Basically every other UNIX uses the exact same api though. Arm also references + // back to the Itanium C++ ABI for the definition of `_Unwind_RaiseException` for + // arm64: + // https://github.com/ARM-software/abi-aa/blob/main/cppabi64/cppabi64.rst#toc-entry-35 + // For arm32 they did something custom, but similar enough that the same + // `_Unwind_RaiseException` impl in miri should work: + // https://github.com/ARM-software/abi-aa/blob/main/ehabi32/ehabi32.rst + if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd" | "illumos" | "solaris" | "android" | "macos") { + throw_unsup_format!( + "`_Unwind_RaiseException` is not supported on {}", + this.tcx.sess.target.os + ); + } + // This function looks and behaves excatly like miri_start_unwind. + let [payload] = this.check_shim(abi, Abi::C { unwind: true }, link_name, args)?; + this.handle_miri_start_unwind(payload)?; + return Ok(EmulateItemResult::NeedsUnwind); + } // Incomplete shims that we "stub out" just to get pre-main initialization code to work. // These shims are enabled only when the caller is in the standard library. @@ -760,6 +785,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { } }; - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs index e70cd35dda6d5..8a7f7e9d1fd82 100644 --- a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs @@ -86,6 +86,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/unix/linux/foreign_items.rs b/src/tools/miri/src/shims/unix/linux/foreign_items.rs index 7cd749a41072e..b2666101ff296 100644 --- a/src/tools/miri/src/shims/unix/linux/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/linux/foreign_items.rs @@ -203,6 +203,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { _ => return Ok(EmulateItemResult::NotSupported), }; - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/unix/macos/foreign_items.rs b/src/tools/miri/src/shims/unix/macos/foreign_items.rs index 912623b722570..2b9ce746a5653 100644 --- a/src/tools/miri/src/shims/unix/macos/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/macos/foreign_items.rs @@ -177,6 +177,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { _ => return Ok(EmulateItemResult::NotSupported), }; - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs index c216d8afd7733..6c5155618c963 100644 --- a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs @@ -45,6 +45,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/wasi/foreign_items.rs b/src/tools/miri/src/shims/wasi/foreign_items.rs index d911f43eb6203..774a5e7202549 100644 --- a/src/tools/miri/src/shims/wasi/foreign_items.rs +++ b/src/tools/miri/src/shims/wasi/foreign_items.rs @@ -35,6 +35,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index 086abf19c5cff..91def80227db8 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -762,6 +762,6 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/x86/aesni.rs b/src/tools/miri/src/shims/x86/aesni.rs index 8dc3748a12d47..3a66c4315062e 100644 --- a/src/tools/miri/src/shims/x86/aesni.rs +++ b/src/tools/miri/src/shims/x86/aesni.rs @@ -127,7 +127,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: // with an external crate. _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/x86/avx.rs b/src/tools/miri/src/shims/x86/avx.rs index 86ef180a44893..b1c61c8b3b2b5 100644 --- a/src/tools/miri/src/shims/x86/avx.rs +++ b/src/tools/miri/src/shims/x86/avx.rs @@ -344,6 +344,6 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: } _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/x86/avx2.rs b/src/tools/miri/src/shims/x86/avx2.rs index adecf7b89240c..e0bd2298ab8d3 100644 --- a/src/tools/miri/src/shims/x86/avx2.rs +++ b/src/tools/miri/src/shims/x86/avx2.rs @@ -440,6 +440,6 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: } _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/x86/mod.rs b/src/tools/miri/src/shims/x86/mod.rs index 58d6db1886a18..1f7138ca338c7 100644 --- a/src/tools/miri/src/shims/x86/mod.rs +++ b/src/tools/miri/src/shims/x86/mod.rs @@ -144,7 +144,7 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/x86/sse.rs b/src/tools/miri/src/shims/x86/sse.rs index ea967a8cf72bb..3636fb2f3fbf9 100644 --- a/src/tools/miri/src/shims/x86/sse.rs +++ b/src/tools/miri/src/shims/x86/sse.rs @@ -212,6 +212,6 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: } _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/x86/sse2.rs b/src/tools/miri/src/shims/x86/sse2.rs index 31fa66a496f3f..54d1e0c803bb3 100644 --- a/src/tools/miri/src/shims/x86/sse2.rs +++ b/src/tools/miri/src/shims/x86/sse2.rs @@ -388,6 +388,6 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: } _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/x86/sse3.rs b/src/tools/miri/src/shims/x86/sse3.rs index 8de339eb9e582..fa1dd07e90b44 100644 --- a/src/tools/miri/src/shims/x86/sse3.rs +++ b/src/tools/miri/src/shims/x86/sse3.rs @@ -51,6 +51,6 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: } _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/x86/sse41.rs b/src/tools/miri/src/shims/x86/sse41.rs index 011a7a16c68bd..cd82108678dae 100644 --- a/src/tools/miri/src/shims/x86/sse41.rs +++ b/src/tools/miri/src/shims/x86/sse41.rs @@ -176,6 +176,6 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: } _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/src/shims/x86/ssse3.rs b/src/tools/miri/src/shims/x86/ssse3.rs index d30de4300886c..ec625da68c27a 100644 --- a/src/tools/miri/src/shims/x86/ssse3.rs +++ b/src/tools/miri/src/shims/x86/ssse3.rs @@ -137,6 +137,6 @@ pub(super) trait EvalContextExt<'mir, 'tcx: 'mir>: } _ => return Ok(EmulateItemResult::NotSupported), } - Ok(EmulateItemResult::NeedsJumping) + Ok(EmulateItemResult::NeedsReturn) } } diff --git a/src/tools/miri/tests/pass/panic/unwind_dwarf.rs b/src/tools/miri/tests/pass/panic/unwind_dwarf.rs new file mode 100644 index 0000000000000..f690be471ba70 --- /dev/null +++ b/src/tools/miri/tests/pass/panic/unwind_dwarf.rs @@ -0,0 +1,96 @@ +//@ignore-target-windows: Windows uses a different unwinding mechanism +#![feature(core_intrinsics, panic_unwind, rustc_attrs)] +#![allow(internal_features)] + +//! Unwinding using `_Unwind_RaiseException` + +extern crate unwind as uw; + +use std::any::Any; +use std::ptr; + +#[repr(C)] +struct Exception { + _uwe: uw::_Unwind_Exception, + cause: Box, +} + +pub fn panic(data: Box) -> u32 { + extern "C" fn exception_cleanup( + _unwind_code: uw::_Unwind_Reason_Code, + _exception: *mut uw::_Unwind_Exception, + ) { + std::process::abort(); + } + + let exception = Box::new(Exception { + _uwe: uw::_Unwind_Exception { + exception_class: miri_exception_class(), + exception_cleanup: Some(exception_cleanup), + private: [core::ptr::null(); uw::unwinder_private_data_size], + }, + cause: data, + }); + let exception_param = Box::into_raw(exception) as *mut uw::_Unwind_Exception; + return unsafe { uw::_Unwind_RaiseException(exception_param) as u32 }; +} + +pub unsafe fn rust_panic_cleanup(ptr: *mut u8) -> Box { + let exception = ptr as *mut uw::_Unwind_Exception; + if (*exception).exception_class != miri_exception_class() { + std::process::abort(); + } + + let exception = exception.cast::(); + + let exception = Box::from_raw(exception as *mut Exception); + exception.cause +} + +fn miri_exception_class() -> uw::_Unwind_Exception_Class { + // M O Z \0 M I R I -- vendor, language + // (Miri's own exception class is just used for testing) + 0x4d4f5a_00_4d495249 +} + +pub fn catch_unwind R>(f: F) -> Result> { + struct Data { + f: Option, + r: Option, + p: Option>, + } + + let mut data = Data { f: Some(f), r: None, p: None }; + + let data_ptr = ptr::addr_of_mut!(data) as *mut u8; + unsafe { + return if std::intrinsics::catch_unwind(do_call::, data_ptr, do_catch::) == 0 { + Ok(data.r.take().unwrap()) + } else { + Err(data.p.take().unwrap()) + }; + } + + fn do_call R, R>(data: *mut u8) { + unsafe { + let data = &mut *data.cast::>(); + let f = data.f.take().unwrap(); + data.r = Some(f()); + } + } + + #[rustc_nounwind] + fn do_catch R, R>(data: *mut u8, payload: *mut u8) { + unsafe { + let obj = rust_panic_cleanup(payload); + (*data.cast::>()).p = Some(obj); + } + } +} + +fn main() { + assert_eq!( + catch_unwind(|| panic(Box::new(42))).unwrap_err().downcast::().unwrap(), + Box::new(42) + ); +}