diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index 0645c1f176..9ccf567bdb 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -38,6 +38,8 @@ impl DynSym { pub enum EmulateForeignItemResult { /// The caller is expected to jump to the return block. NeedsJumping, + /// The caller is expected to jump to the unwind block. + NeedsUnwind, /// Jumping has already been taken care of. AlreadyJumped, /// The item is not supported. @@ -126,6 +128,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { trace!("{:?}", this.dump_place(dest)); this.go_to_block(ret); } + EmulateForeignItemResult::NeedsUnwind => { + // Jump to the unwind block to begin unwinding. + this.unwind_to_block(unwind)?; + } EmulateForeignItemResult::AlreadyJumped => (), EmulateForeignItemResult::NotSupported => { if let Some(body) = this.lookup_exported_symbol(link_name)? { diff --git a/src/shims/unix/foreign_items.rs b/src/shims/unix/foreign_items.rs index b5cd18396a..b6107535ce 100644 --- a/src/shims/unix/foreign_items.rs +++ b/src/shims/unix/foreign_items.rs @@ -729,6 +729,18 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { this.write_scalar(Scalar::from_i32(-1), dest)?; } + "_Unwind_RaiseException" => { + trace!("_Unwind_RaiseException: {:?}", this.frame().instance); + + // Get the raw pointer stored in arg[0] (the panic payload). + let [payload] = this.check_shim(abi, Abi::C { unwind: true }, link_name, args)?; + let payload = this.read_scalar(payload)?; + let thread = this.active_thread_mut(); + thread.panic_payloads.push(payload); + + return Ok(EmulateForeignItemResult::NeedsUnwind); + } + // Platform-specific shims _ => { let target_os = &*this.tcx.sess.target.os; diff --git a/tests/pass/panic/unwind_dwarf.rs b/tests/pass/panic/unwind_dwarf.rs new file mode 100644 index 0000000000..aa5ec05fbc --- /dev/null +++ b/tests/pass/panic/unwind_dwarf.rs @@ -0,0 +1,95 @@ +//@only-target-linux +#![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 { + let exception = Box::new(Exception { + _uwe: uw::_Unwind_Exception { + exception_class: rust_exception_class(), + 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 }; + + extern "C" fn exception_cleanup( + _unwind_code: uw::_Unwind_Reason_Code, + _exception: *mut uw::_Unwind_Exception, + ) { + std::process::abort(); + } +} + +pub unsafe fn rust_panic_cleanup(ptr: *mut u8) -> Box { + let exception = ptr as *mut uw::_Unwind_Exception; + if (*exception).exception_class != rust_exception_class() { + std::process::abort(); + } + + let exception = exception.cast::(); + + let exception = Box::from_raw(exception as *mut Exception); + exception.cause +} + +fn rust_exception_class() -> uw::_Unwind_Exception_Class { + // M O Z \0 R U S T -- vendor, language + 0x4d4f5a_00_52555354 +} + +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::r#try(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) + ); +}