diff --git a/src/doc/unstable-book/src/language-features/lang-items.md b/src/doc/unstable-book/src/language-features/lang-items.md index 3ee024c6b5883..d4ad65e84b7b4 100644 --- a/src/doc/unstable-book/src/language-features/lang-items.md +++ b/src/doc/unstable-book/src/language-features/lang-items.md @@ -249,11 +249,11 @@ the source code. - Runtime - `start`: `libstd/rt.rs` - `eh_personality`: `libpanic_unwind/emcc.rs` (EMCC) - - `eh_personality`: `libpanic_unwind/seh64_gnu.rs` (SEH64 GNU) + - `eh_personality`: `libpanic_unwind/gcc.rs` (GNU) - `eh_personality`: `libpanic_unwind/seh.rs` (SEH) - - `eh_unwind_resume`: `libpanic_unwind/seh64_gnu.rs` (SEH64 GNU) - `eh_unwind_resume`: `libpanic_unwind/gcc.rs` (GCC) - - `msvc_try_filter`: `libpanic_unwind/seh.rs` (SEH) + - `eh_catch_typeinfo`: `libpanic_unwind/seh.rs` (SEH) + - `eh_catch_typeinfo`: `libpanic_unwind/emcc.rs` (EMCC) - `panic`: `libcore/panicking.rs` - `panic_bounds_check`: `libcore/panicking.rs` - `panic_impl`: `libcore/panicking.rs` diff --git a/src/libpanic_unwind/dwarf/eh.rs b/src/libpanic_unwind/dwarf/eh.rs index 07fa2971847f6..1e9e7e4b8359c 100644 --- a/src/libpanic_unwind/dwarf/eh.rs +++ b/src/libpanic_unwind/dwarf/eh.rs @@ -51,7 +51,7 @@ pub enum EHAction { pub const USING_SJLJ_EXCEPTIONS: bool = cfg!(all(target_os = "ios", target_arch = "arm")); -pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) +pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>, foreign_exception: bool) -> Result { if lsda.is_null() { @@ -96,7 +96,7 @@ pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) return Ok(EHAction::None) } else { let lpad = lpad_base + cs_lpad; - return Ok(interpret_cs_action(cs_action, lpad)) + return Ok(interpret_cs_action(cs_action, lpad, foreign_exception)) } } } @@ -121,16 +121,23 @@ pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext<'_>) // Can never have null landing pad for sjlj -- that would have // been indicated by a -1 call site index. let lpad = (cs_lpad + 1) as usize; - return Ok(interpret_cs_action(cs_action, lpad)) + return Ok(interpret_cs_action(cs_action, lpad, foreign_exception)) } } } } -fn interpret_cs_action(cs_action: u64, lpad: usize) -> EHAction { +fn interpret_cs_action(cs_action: u64, lpad: usize, foreign_exception: bool) -> EHAction { if cs_action == 0 { + // If cs_action is 0 then this is a cleanup (Drop::drop). We run these + // for both Rust panics and foriegn exceptions. EHAction::Cleanup(lpad) + } else if foreign_exception { + // catch_unwind should not catch foreign exceptions, only Rust panics. + // Instead just continue unwinding. + EHAction::None } else { + // Stop unwinding Rust panics at catch_unwind. EHAction::Catch(lpad) } } diff --git a/src/libpanic_unwind/emcc.rs b/src/libpanic_unwind/emcc.rs index 18e9006468ef3..3d0d5a4151eed 100644 --- a/src/libpanic_unwind/emcc.rs +++ b/src/libpanic_unwind/emcc.rs @@ -15,14 +15,48 @@ use alloc::boxed::Box; use libc::{self, c_int}; use unwind as uw; +// This matches the layout of std::type_info in C++ +#[repr(C)] +struct TypeInfo { + vtable: *const usize, + name: *const u8, +} +unsafe impl Sync for TypeInfo {} + +extern "C" { + // The leading `\x01` byte here is actually a magical signal to LLVM to + // *not* apply any other mangling like prefixing with a `_` character. + // + // This symbol is the vtable used by C++'s `std::type_info`. Objects of type + // `std::type_info`, type descriptors, have a pointer to this table. Type + // descriptors are referenced by the C++ EH structures defined above and + // that we construct below. + // + // Note that the real size is larger than 3 usize, but we only need our + // vtable to point to the third element. + #[link_name = "\x01_ZTVN10__cxxabiv117__class_type_infoE"] + static CLASS_TYPE_INFO_VTABLE: [usize; 3]; +} + +// std::type_info for a rust_panic class +#[lang = "eh_catch_typeinfo"] +static EXCEPTION_TYPE_INFO: TypeInfo = TypeInfo { + // Normally we would use .as_ptr().add(2) but this doesn't work in a const context. + vtable: unsafe { &CLASS_TYPE_INFO_VTABLE[2] }, + // This intentionally doesn't use the normal name mangling scheme because + // we don't want C++ to be able to produce or catch Rust panics. + name: b"rust_panic\0".as_ptr(), +}; + pub fn payload() -> *mut u8 { ptr::null_mut() } pub unsafe fn cleanup(ptr: *mut u8) -> Box { assert!(!ptr.is_null()); - let ex = ptr::read(ptr as *mut _); - __cxa_free_exception(ptr as *mut _); + let adjusted_ptr = __cxa_begin_catch(ptr as *mut libc::c_void); + let ex = ptr::read(adjusted_ptr as *mut _); + __cxa_end_catch(); ex } @@ -32,11 +66,8 @@ pub unsafe fn panic(data: Box) -> u32 { if exception == ptr::null_mut() { return uw::_URC_FATAL_PHASE1_ERROR as u32; } - let exception = exception as *mut Box; - ptr::write(exception, data); - __cxa_throw(exception as *mut _, ptr::null_mut(), ptr::null_mut()); - - unreachable!() + ptr::write(exception as *mut _, data); + __cxa_throw(exception as *mut _, &EXCEPTION_TYPE_INFO, ptr::null_mut()); } #[lang = "eh_personality"] @@ -52,10 +83,11 @@ unsafe extern "C" fn rust_eh_personality(version: c_int, extern "C" { fn __cxa_allocate_exception(thrown_size: libc::size_t) -> *mut libc::c_void; - fn __cxa_free_exception(thrown_exception: *mut libc::c_void); + fn __cxa_begin_catch(thrown_exception: *mut libc::c_void) -> *mut libc::c_void; + fn __cxa_end_catch(); fn __cxa_throw(thrown_exception: *mut libc::c_void, - tinfo: *mut libc::c_void, - dest: *mut libc::c_void); + tinfo: *const TypeInfo, + dest: *mut libc::c_void) -> !; fn __gxx_personality_v0(version: c_int, actions: uw::_Unwind_Action, exception_class: uw::_Unwind_Exception_Class, diff --git a/src/libpanic_unwind/gcc.rs b/src/libpanic_unwind/gcc.rs index a35847c85fc20..4f572fe21b30b 100644 --- a/src/libpanic_unwind/gcc.rs +++ b/src/libpanic_unwind/gcc.rs @@ -133,133 +133,176 @@ const UNWIND_DATA_REG: (i32, i32) = (0, 1); // R0, R1 // https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_personality.cc // https://github.com/gcc-mirror/gcc/blob/trunk/libgcc/unwind-c.c -// The personality routine for most of our targets, except ARM, which has a slightly different ABI -// (however, iOS goes here as it uses SjLj unwinding). Also, the 64-bit Windows implementation -// lives in seh64_gnu.rs -#[cfg(all(any(target_os = "ios", target_os = "netbsd", not(target_arch = "arm"))))] -#[lang = "eh_personality"] -#[no_mangle] -#[allow(unused)] -unsafe extern "C" fn rust_eh_personality(version: c_int, - actions: uw::_Unwind_Action, - exception_class: uw::_Unwind_Exception_Class, - exception_object: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context) - -> uw::_Unwind_Reason_Code { - if version != 1 { - return uw::_URC_FATAL_PHASE1_ERROR; - } - let eh_action = match find_eh_action(context) { - Ok(action) => action, - Err(_) => return uw::_URC_FATAL_PHASE1_ERROR, - }; - if actions as i32 & uw::_UA_SEARCH_PHASE as i32 != 0 { - match eh_action { - EHAction::None | - EHAction::Cleanup(_) => uw::_URC_CONTINUE_UNWIND, - EHAction::Catch(_) => uw::_URC_HANDLER_FOUND, - EHAction::Terminate => uw::_URC_FATAL_PHASE1_ERROR, - } - } else { - match eh_action { - EHAction::None => uw::_URC_CONTINUE_UNWIND, - EHAction::Cleanup(lpad) | - EHAction::Catch(lpad) => { - uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0, exception_object as uintptr_t); - uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, 0); - uw::_Unwind_SetIP(context, lpad); - uw::_URC_INSTALL_CONTEXT - } - EHAction::Terminate => uw::_URC_FATAL_PHASE2_ERROR, - } - } -} +cfg_if::cfg_if! { + if #[cfg(all(target_arch = "arm", not(target_os = "ios"), not(target_os = "netbsd")))] { + // ARM EHABI personality routine. + // http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038b/IHI0038B_ehabi.pdf + // + // iOS uses the default routine instead since it uses SjLj unwinding. + #[lang = "eh_personality"] + #[no_mangle] + unsafe extern "C" fn rust_eh_personality(state: uw::_Unwind_State, + exception_object: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context) + -> uw::_Unwind_Reason_Code { + let state = state as c_int; + let action = state & uw::_US_ACTION_MASK as c_int; + let search_phase = if action == uw::_US_VIRTUAL_UNWIND_FRAME as c_int { + // Backtraces on ARM will call the personality routine with + // state == _US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND. In those cases + // we want to continue unwinding the stack, otherwise all our backtraces + // would end at __rust_try + if state & uw::_US_FORCE_UNWIND as c_int != 0 { + return continue_unwind(exception_object, context); + } + true + } else if action == uw::_US_UNWIND_FRAME_STARTING as c_int { + false + } else if action == uw::_US_UNWIND_FRAME_RESUME as c_int { + return continue_unwind(exception_object, context); + } else { + return uw::_URC_FAILURE; + }; -// ARM EHABI personality routine. -// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038b/IHI0038B_ehabi.pdf -#[cfg(all(target_arch = "arm", not(target_os = "ios"), not(target_os = "netbsd")))] -#[lang = "eh_personality"] -#[no_mangle] -unsafe extern "C" fn rust_eh_personality(state: uw::_Unwind_State, - exception_object: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context) - -> uw::_Unwind_Reason_Code { - let state = state as c_int; - let action = state & uw::_US_ACTION_MASK as c_int; - let search_phase = if action == uw::_US_VIRTUAL_UNWIND_FRAME as c_int { - // Backtraces on ARM will call the personality routine with - // state == _US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND. In those cases - // we want to continue unwinding the stack, otherwise all our backtraces - // would end at __rust_try - if state & uw::_US_FORCE_UNWIND as c_int != 0 { - return continue_unwind(exception_object, context); - } - true - } else if action == uw::_US_UNWIND_FRAME_STARTING as c_int { - false - } else if action == uw::_US_UNWIND_FRAME_RESUME as c_int { - return continue_unwind(exception_object, context); - } else { - return uw::_URC_FAILURE; - }; + // The DWARF unwinder assumes that _Unwind_Context holds things like the function + // and LSDA pointers, however ARM EHABI places them into the exception object. + // To preserve signatures of functions like _Unwind_GetLanguageSpecificData(), which + // take only the context pointer, GCC personality routines stash a pointer to + // exception_object in the context, using location reserved for ARM's + // "scratch register" (r12). + uw::_Unwind_SetGR(context, + uw::UNWIND_POINTER_REG, + exception_object as uw::_Unwind_Ptr); + // ...A more principled approach would be to provide the full definition of ARM's + // _Unwind_Context in our libunwind bindings and fetch the required data from there + // directly, bypassing DWARF compatibility functions. - // The DWARF unwinder assumes that _Unwind_Context holds things like the function - // and LSDA pointers, however ARM EHABI places them into the exception object. - // To preserve signatures of functions like _Unwind_GetLanguageSpecificData(), which - // take only the context pointer, GCC personality routines stash a pointer to exception_object - // in the context, using location reserved for ARM's "scratch register" (r12). - uw::_Unwind_SetGR(context, - uw::UNWIND_POINTER_REG, - exception_object as uw::_Unwind_Ptr); - // ...A more principled approach would be to provide the full definition of ARM's - // _Unwind_Context in our libunwind bindings and fetch the required data from there directly, - // bypassing DWARF compatibility functions. + let exception_class = (*exception_object).exception_class; + let foreign_exception = exception_class != rust_exception_class(); + let eh_action = match find_eh_action(context, foreign_exception) { + Ok(action) => action, + Err(_) => return uw::_URC_FAILURE, + }; + if search_phase { + match eh_action { + EHAction::None | + EHAction::Cleanup(_) => return continue_unwind(exception_object, context), + EHAction::Catch(_) => return uw::_URC_HANDLER_FOUND, + EHAction::Terminate => return uw::_URC_FAILURE, + } + } else { + match eh_action { + EHAction::None => return continue_unwind(exception_object, context), + EHAction::Cleanup(lpad) | + EHAction::Catch(lpad) => { + uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0, + exception_object as uintptr_t); + uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, 0); + uw::_Unwind_SetIP(context, lpad); + return uw::_URC_INSTALL_CONTEXT; + } + EHAction::Terminate => return uw::_URC_FAILURE, + } + } - let eh_action = match find_eh_action(context) { - Ok(action) => action, - Err(_) => return uw::_URC_FAILURE, - }; - if search_phase { - match eh_action { - EHAction::None | - EHAction::Cleanup(_) => return continue_unwind(exception_object, context), - EHAction::Catch(_) => return uw::_URC_HANDLER_FOUND, - EHAction::Terminate => return uw::_URC_FAILURE, + // On ARM EHABI the personality routine is responsible for actually + // unwinding a single stack frame before returning (ARM EHABI Sec. 6.1). + unsafe fn continue_unwind(exception_object: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context) + -> uw::_Unwind_Reason_Code { + if __gnu_unwind_frame(exception_object, context) == uw::_URC_NO_REASON { + uw::_URC_CONTINUE_UNWIND + } else { + uw::_URC_FAILURE + } + } + // defined in libgcc + extern "C" { + fn __gnu_unwind_frame(exception_object: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context) + -> uw::_Unwind_Reason_Code; + } } } else { - match eh_action { - EHAction::None => return continue_unwind(exception_object, context), - EHAction::Cleanup(lpad) | - EHAction::Catch(lpad) => { - uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0, exception_object as uintptr_t); - uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, 0); - uw::_Unwind_SetIP(context, lpad); - return uw::_URC_INSTALL_CONTEXT; + // Default personality routine, which is used directly on most targets + // and indirectly on Windows x86_64 via SEH. + unsafe extern "C" fn rust_eh_personality_impl(version: c_int, + actions: uw::_Unwind_Action, + exception_class: uw::_Unwind_Exception_Class, + exception_object: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context) + -> uw::_Unwind_Reason_Code { + if version != 1 { + return uw::_URC_FATAL_PHASE1_ERROR; + } + let foreign_exception = exception_class != rust_exception_class(); + let eh_action = match find_eh_action(context, foreign_exception) { + Ok(action) => action, + Err(_) => return uw::_URC_FATAL_PHASE1_ERROR, + }; + if actions as i32 & uw::_UA_SEARCH_PHASE as i32 != 0 { + match eh_action { + EHAction::None | + EHAction::Cleanup(_) => uw::_URC_CONTINUE_UNWIND, + EHAction::Catch(_) => uw::_URC_HANDLER_FOUND, + EHAction::Terminate => uw::_URC_FATAL_PHASE1_ERROR, + } + } else { + match eh_action { + EHAction::None => uw::_URC_CONTINUE_UNWIND, + EHAction::Cleanup(lpad) | + EHAction::Catch(lpad) => { + uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0, + exception_object as uintptr_t); + uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, 0); + uw::_Unwind_SetIP(context, lpad); + uw::_URC_INSTALL_CONTEXT + } + EHAction::Terminate => uw::_URC_FATAL_PHASE2_ERROR, + } } - EHAction::Terminate => return uw::_URC_FAILURE, } - } - // On ARM EHABI the personality routine is responsible for actually - // unwinding a single stack frame before returning (ARM EHABI Sec. 6.1). - unsafe fn continue_unwind(exception_object: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context) - -> uw::_Unwind_Reason_Code { - if __gnu_unwind_frame(exception_object, context) == uw::_URC_NO_REASON { - uw::_URC_CONTINUE_UNWIND - } else { - uw::_URC_FAILURE + cfg_if::cfg_if! { + if #[cfg(all(windows, target_arch = "x86_64", target_env = "gnu"))] { + // On x86_64 MinGW targets, the unwinding mechanism is SEH however the unwind + // handler data (aka LSDA) uses GCC-compatible encoding. + #[lang = "eh_personality"] + #[no_mangle] + #[allow(nonstandard_style)] + unsafe extern "C" fn rust_eh_personality(exceptionRecord: *mut uw::EXCEPTION_RECORD, + establisherFrame: uw::LPVOID, + contextRecord: *mut uw::CONTEXT, + dispatcherContext: *mut uw::DISPATCHER_CONTEXT) + -> uw::EXCEPTION_DISPOSITION { + uw::_GCC_specific_handler(exceptionRecord, + establisherFrame, + contextRecord, + dispatcherContext, + rust_eh_personality_impl) + } + } else { + // The personality routine for most of our targets. + #[lang = "eh_personality"] + #[no_mangle] + unsafe extern "C" fn rust_eh_personality(version: c_int, + actions: uw::_Unwind_Action, + exception_class: uw::_Unwind_Exception_Class, + exception_object: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context) + -> uw::_Unwind_Reason_Code { + rust_eh_personality_impl(version, + actions, + exception_class, + exception_object, + context) + } + } } } - // defined in libgcc - extern "C" { - fn __gnu_unwind_frame(exception_object: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context) - -> uw::_Unwind_Reason_Code; - } } -unsafe fn find_eh_action(context: *mut uw::_Unwind_Context) +unsafe fn find_eh_action(context: *mut uw::_Unwind_Context, foreign_exception: bool) -> Result { let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8; @@ -273,11 +316,11 @@ unsafe fn find_eh_action(context: *mut uw::_Unwind_Context) get_text_start: &|| uw::_Unwind_GetTextRelBase(context), get_data_start: &|| uw::_Unwind_GetDataRelBase(context), }; - eh::find_eh_action(lsda, &eh_context) + eh::find_eh_action(lsda, &eh_context, foreign_exception) } // See docs in the `unwind` module. -#[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))] +#[cfg(all(target_os="windows", any(target_arch = "x86", target_arch = "x86_64"), target_env="gnu"))] #[lang = "eh_unwind_resume"] #[unwind(allowed)] unsafe extern "C" fn rust_eh_unwind_resume(panic_ctx: *mut u8) -> ! { diff --git a/src/libpanic_unwind/lib.rs b/src/libpanic_unwind/lib.rs index 2089a02083c59..d2a0ef7b1dde1 100644 --- a/src/libpanic_unwind/lib.rs +++ b/src/libpanic_unwind/lib.rs @@ -5,9 +5,8 @@ //! essentially gets categorized into three buckets currently: //! //! 1. MSVC targets use SEH in the `seh.rs` file. -//! 2. The 64-bit MinGW target half-uses SEH and half-use gcc-like information -//! in the `seh64_gnu.rs` module. -//! 3. All other targets use libunwind/libgcc in the `gcc/mod.rs` module. +//! 2. Emscripten uses C++ exceptions in the `emcc.rs` file. +//! 3. All other targets use libunwind/libgcc in the `gcc.rs` file. //! //! More documentation about each implementation can be found in the respective //! module. @@ -52,9 +51,6 @@ cfg_if::cfg_if! { } else if #[cfg(target_env = "msvc")] { #[path = "seh.rs"] mod imp; - } else if #[cfg(all(windows, target_arch = "x86_64", target_env = "gnu"))] { - #[path = "seh64_gnu.rs"] - mod imp; } else { // Rust runtime's startup objects depend on these symbols, so make them public. #[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))] @@ -65,7 +61,6 @@ cfg_if::cfg_if! { } mod dwarf; -mod windows; // Entry point for catching an exception, implemented using the `try` intrinsic // in the compiler. diff --git a/src/libpanic_unwind/seh.rs b/src/libpanic_unwind/seh.rs index 809e4619812de..621813a2fee95 100644 --- a/src/libpanic_unwind/seh.rs +++ b/src/libpanic_unwind/seh.rs @@ -51,9 +51,7 @@ use alloc::boxed::Box; use core::any::Any; use core::mem; use core::raw; - -use crate::windows as c; -use libc::{c_int, c_uint}; +use libc::{c_int, c_uint, c_void}; // First up, a whole bunch of type definitions. There's a few platform-specific // oddities here, and a lot that's just blatantly copied from LLVM. The purpose @@ -76,18 +74,19 @@ use libc::{c_int, c_uint}; // sort of operation. For example, if you compile this C++ code on MSVC and emit // the LLVM IR: // -// #include +// #include +// +// struct rust_panic { +// uint64_t x[2]; +// } // // void foo() { -// uint64_t a[2] = {0, 1}; +// rust_panic a = {0, 1}; // throw a; // } // // That's essentially what we're trying to emulate. Most of the constant values -// below were just copied from LLVM, I'm at least not 100% sure what's going on -// everywhere. For example the `.PA_K\0` and `.PEA_K\0` strings below (stuck in -// the names of a few of these) I'm not actually sure what they do, but it seems -// to mirror what LLVM does! +// below were just copied from LLVM, // // In any case, these structures are all constructed in a similar manner, and // it's just somewhat verbose for us. @@ -98,10 +97,9 @@ use libc::{c_int, c_uint}; #[macro_use] mod imp { pub type ptr_t = *mut u8; - pub const OFFSET: i32 = 4; + #[cfg(bootstrap)] pub const NAME1: [u8; 7] = [b'.', b'P', b'A', b'_', b'K', 0, 0]; - pub const NAME2: [u8; 7] = [b'.', b'P', b'A', b'X', 0, 0, 0]; macro_rules! ptr { (0) => (core::ptr::null_mut()); @@ -113,10 +111,9 @@ mod imp { #[macro_use] mod imp { pub type ptr_t = u32; - pub const OFFSET: i32 = 8; + #[cfg(bootstrap)] pub const NAME1: [u8; 7] = [b'.', b'P', b'E', b'A', b'_', b'K', 0]; - pub const NAME2: [u8; 7] = [b'.', b'P', b'E', b'A', b'X', 0, 0]; extern "C" { pub static __ImageBase: u8; @@ -141,7 +138,7 @@ pub struct _ThrowInfo { #[repr(C)] pub struct _CatchableTypeArray { pub nCatchableTypes: c_int, - pub arrayOfCatchableTypes: [imp::ptr_t; 2], + pub arrayOfCatchableTypes: [imp::ptr_t; 1], } #[repr(C)] @@ -164,9 +161,19 @@ pub struct _PMD { pub struct _TypeDescriptor { pub pVFTable: *const u8, pub spare: *mut u8, + #[cfg(bootstrap)] pub name: [u8; 7], + #[cfg(not(bootstrap))] + pub name: [u8; 11], } +// Note that we intentionally ignore name mangling rules here: we don't want C++ +// to be able to catch Rust panics by simply declaring a `struct rust_panic`. +#[cfg(bootstrap)] +use imp::NAME1 as TYPE_NAME; +#[cfg(not(bootstrap))] +const TYPE_NAME: [u8; 11] = *b"rust_panic\0"; + static mut THROW_INFO: _ThrowInfo = _ThrowInfo { attributes: 0, pnfnUnwind: ptr!(0), @@ -175,31 +182,22 @@ static mut THROW_INFO: _ThrowInfo = _ThrowInfo { }; static mut CATCHABLE_TYPE_ARRAY: _CatchableTypeArray = _CatchableTypeArray { - nCatchableTypes: 2, - arrayOfCatchableTypes: [ptr!(0), ptr!(0)], + nCatchableTypes: 1, + arrayOfCatchableTypes: [ptr!(0)], }; -static mut CATCHABLE_TYPE1: _CatchableType = _CatchableType { - properties: 1, +static mut CATCHABLE_TYPE: _CatchableType = _CatchableType { + properties: 0, pType: ptr!(0), thisDisplacement: _PMD { mdisp: 0, pdisp: -1, vdisp: 0, }, - sizeOrOffset: imp::OFFSET, - copy_function: ptr!(0), -}; - -static mut CATCHABLE_TYPE2: _CatchableType = _CatchableType { - properties: 1, - pType: ptr!(0), - thisDisplacement: _PMD { - mdisp: 0, - pdisp: -1, - vdisp: 0, - }, - sizeOrOffset: imp::OFFSET, + #[cfg(bootstrap)] + sizeOrOffset: mem::size_of::<*mut u64>() as c_int, + #[cfg(not(bootstrap))] + sizeOrOffset: mem::size_of::<[u64; 2]>() as c_int, copy_function: ptr!(0), }; @@ -215,22 +213,17 @@ extern "C" { static TYPE_INFO_VTABLE: *const u8; } -// We use #[lang = "msvc_try_filter"] here as this is the type descriptor which +// We use #[lang = "eh_catch_typeinfo"] here as this is the type descriptor which // we'll use in LLVM's `catchpad` instruction which ends up also being passed as // an argument to the C++ personality function. // // Again, I'm not entirely sure what this is describing, it just seems to work. -#[cfg_attr(not(test), lang = "msvc_try_filter")] -static mut TYPE_DESCRIPTOR1: _TypeDescriptor = _TypeDescriptor { +#[cfg_attr(bootstrap, lang = "msvc_try_filter")] +#[cfg_attr(not(any(test, bootstrap)), lang = "eh_catch_typeinfo")] +static mut TYPE_DESCRIPTOR: _TypeDescriptor = _TypeDescriptor { pVFTable: unsafe { &TYPE_INFO_VTABLE } as *const _ as *const _, spare: core::ptr::null_mut(), - name: imp::NAME1, -}; - -static mut TYPE_DESCRIPTOR2: _TypeDescriptor = _TypeDescriptor { - pVFTable: unsafe { &TYPE_INFO_VTABLE } as *const _ as *const _, - spare: core::ptr::null_mut(), - name: imp::NAME2, + name: TYPE_NAME, }; pub unsafe fn panic(data: Box) -> u32 { @@ -246,6 +239,11 @@ pub unsafe fn panic(data: Box) -> u32 { let ptrs = mem::transmute::<_, raw::TraitObject>(data); let mut ptrs = [ptrs.data as u64, ptrs.vtable as u64]; let mut ptrs_ptr = ptrs.as_mut_ptr(); + let throw_ptr = if cfg!(bootstrap) { + &mut ptrs_ptr as *mut _ as *mut _ + } else { + ptrs_ptr as *mut _ + }; // This... may seems surprising, and justifiably so. On 32-bit MSVC the // pointers between these structure are just that, pointers. On 64-bit MSVC, @@ -270,17 +268,17 @@ pub unsafe fn panic(data: Box) -> u32 { atomic_store(&mut THROW_INFO.pCatchableTypeArray as *mut _ as *mut u32, ptr!(&CATCHABLE_TYPE_ARRAY as *const _) as u32); atomic_store(&mut CATCHABLE_TYPE_ARRAY.arrayOfCatchableTypes[0] as *mut _ as *mut u32, - ptr!(&CATCHABLE_TYPE1 as *const _) as u32); - atomic_store(&mut CATCHABLE_TYPE_ARRAY.arrayOfCatchableTypes[1] as *mut _ as *mut u32, - ptr!(&CATCHABLE_TYPE2 as *const _) as u32); - atomic_store(&mut CATCHABLE_TYPE1.pType as *mut _ as *mut u32, - ptr!(&TYPE_DESCRIPTOR1 as *const _) as u32); - atomic_store(&mut CATCHABLE_TYPE2.pType as *mut _ as *mut u32, - ptr!(&TYPE_DESCRIPTOR2 as *const _) as u32); + ptr!(&CATCHABLE_TYPE as *const _) as u32); + atomic_store(&mut CATCHABLE_TYPE.pType as *mut _ as *mut u32, + ptr!(&TYPE_DESCRIPTOR as *const _) as u32); + + extern "system" { + #[unwind(allowed)] + pub fn _CxxThrowException(pExceptionObject: *mut c_void, pThrowInfo: *mut u8) -> !; + } - c::_CxxThrowException(&mut ptrs_ptr as *mut _ as *mut _, - &mut THROW_INFO as *mut _ as *mut _); - u32::max_value() + _CxxThrowException(throw_ptr, + &mut THROW_INFO as *mut _ as *mut _); } pub fn payload() -> [u64; 2] { diff --git a/src/libpanic_unwind/seh64_gnu.rs b/src/libpanic_unwind/seh64_gnu.rs deleted file mode 100644 index 16b699a443799..0000000000000 --- a/src/libpanic_unwind/seh64_gnu.rs +++ /dev/null @@ -1,127 +0,0 @@ -//! Unwinding implementation of top of native Win64 SEH, -//! however the unwind handler data (aka LSDA) uses GCC-compatible encoding. - -#![allow(nonstandard_style)] -#![allow(private_no_mangle_fns)] - -use alloc::boxed::Box; - -use core::any::Any; -use core::intrinsics; -use core::ptr; -use crate::dwarf::eh::{EHContext, EHAction, find_eh_action}; -use crate::windows as c; - -// Define our exception codes: -// according to http://msdn.microsoft.com/en-us/library/het71c37(v=VS.80).aspx, -// [31:30] = 3 (error), 2 (warning), 1 (info), 0 (success) -// [29] = 1 (user-defined) -// [28] = 0 (reserved) -// we define bits: -// [24:27] = type -// [0:23] = magic -const ETYPE: c::DWORD = 0b1110_u32 << 28; -const MAGIC: c::DWORD = 0x525354; // "RST" - -const RUST_PANIC: c::DWORD = ETYPE | (1 << 24) | MAGIC; - -#[repr(C)] -struct PanicData { - data: Box, -} - -pub unsafe fn panic(data: Box) -> u32 { - let panic_ctx = Box::new(PanicData { data }); - let params = [Box::into_raw(panic_ctx) as c::ULONG_PTR]; - c::RaiseException(RUST_PANIC, - c::EXCEPTION_NONCONTINUABLE, - params.len() as c::DWORD, - ¶ms as *const c::ULONG_PTR); - u32::max_value() -} - -pub fn payload() -> *mut u8 { - ptr::null_mut() -} - -pub unsafe fn cleanup(ptr: *mut u8) -> Box { - let panic_ctx = Box::from_raw(ptr as *mut PanicData); - panic_ctx.data -} - -// SEH doesn't support resuming unwinds after calling a landing pad like -// libunwind does. For this reason, MSVC compiler outlines landing pads into -// separate functions that can be called directly from the personality function -// but are nevertheless able to find and modify stack frame of the "parent" -// function. -// -// Since this cannot be done with libdwarf-style landing pads, -// rust_eh_personality instead catches RUST_PANICs, runs the landing pad, then -// reraises the exception. -// -// Note that it makes certain assumptions about the exception: -// -// 1. That RUST_PANIC is non-continuable, so no lower stack frame may choose to -// resume execution. -// 2. That the first parameter of the exception is a pointer to an extra data -// area (PanicData). -// Since these assumptions do not generally hold true for foreign exceptions -// (system faults, C++ exceptions, etc), we make no attempt to invoke our -// landing pads (and, thus, destructors!) for anything other than RUST_PANICs. -// This is considered acceptable, because the behavior of throwing exceptions -// through a C ABI boundary is undefined. - -#[lang = "eh_personality"] -#[cfg(not(test))] -unsafe extern "C" fn rust_eh_personality(exceptionRecord: *mut c::EXCEPTION_RECORD, - establisherFrame: c::LPVOID, - contextRecord: *mut c::CONTEXT, - dispatcherContext: *mut c::DISPATCHER_CONTEXT) - -> c::EXCEPTION_DISPOSITION { - let er = &*exceptionRecord; - let dc = &*dispatcherContext; - - if er.ExceptionFlags & c::EXCEPTION_UNWIND == 0 { - // we are in the dispatch phase - if er.ExceptionCode == RUST_PANIC { - if let Some(lpad) = find_landing_pad(dc) { - c::RtlUnwindEx(establisherFrame, - lpad as c::LPVOID, - exceptionRecord, - er.ExceptionInformation[0] as c::LPVOID, // pointer to PanicData - contextRecord, - dc.HistoryTable); - } - } - } - c::ExceptionContinueSearch -} - -#[lang = "eh_unwind_resume"] -#[unwind(allowed)] -unsafe extern "C" fn rust_eh_unwind_resume(panic_ctx: c::LPVOID) -> ! { - let params = [panic_ctx as c::ULONG_PTR]; - c::RaiseException(RUST_PANIC, - c::EXCEPTION_NONCONTINUABLE, - params.len() as c::DWORD, - ¶ms as *const c::ULONG_PTR); - intrinsics::abort(); -} - -unsafe fn find_landing_pad(dc: &c::DISPATCHER_CONTEXT) -> Option { - let eh_ctx = EHContext { - // The return address points 1 byte past the call instruction, - // which could be in the next IP range in LSDA range table. - ip: dc.ControlPc as usize - 1, - func_start: dc.ImageBase as usize + (*dc.FunctionEntry).BeginAddress as usize, - get_text_start: &|| dc.ImageBase as usize, - get_data_start: &|| unimplemented!(), - }; - match find_eh_action(dc.HandlerData, &eh_ctx) { - Err(_) | - Ok(EHAction::None) => None, - Ok(EHAction::Cleanup(lpad)) | - Ok(EHAction::Catch(lpad)) => Some(lpad), - Ok(EHAction::Terminate) => intrinsics::abort(), - } -} diff --git a/src/libpanic_unwind/windows.rs b/src/libpanic_unwind/windows.rs deleted file mode 100644 index 3257a9d25a51a..0000000000000 --- a/src/libpanic_unwind/windows.rs +++ /dev/null @@ -1,86 +0,0 @@ -#![allow(nonstandard_style)] -#![allow(dead_code)] -#![cfg(windows)] - -use libc::{c_long, c_ulong, c_void}; - -pub type DWORD = c_ulong; -pub type LONG = c_long; -pub type ULONG_PTR = usize; -pub type LPVOID = *mut c_void; - -pub const EXCEPTION_MAXIMUM_PARAMETERS: usize = 15; -pub const EXCEPTION_NONCONTINUABLE: DWORD = 0x1; // Noncontinuable exception -pub const EXCEPTION_UNWINDING: DWORD = 0x2; // Unwind is in progress -pub const EXCEPTION_EXIT_UNWIND: DWORD = 0x4; // Exit unwind is in progress -pub const EXCEPTION_TARGET_UNWIND: DWORD = 0x20; // Target unwind in progress -pub const EXCEPTION_COLLIDED_UNWIND: DWORD = 0x40; // Collided exception handler call -pub const EXCEPTION_UNWIND: DWORD = EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND | - EXCEPTION_TARGET_UNWIND | - EXCEPTION_COLLIDED_UNWIND; - -#[repr(C)] -pub struct EXCEPTION_RECORD { - pub ExceptionCode: DWORD, - pub ExceptionFlags: DWORD, - pub ExceptionRecord: *mut EXCEPTION_RECORD, - pub ExceptionAddress: LPVOID, - pub NumberParameters: DWORD, - pub ExceptionInformation: [LPVOID; EXCEPTION_MAXIMUM_PARAMETERS], -} - -#[repr(C)] -pub struct EXCEPTION_POINTERS { - pub ExceptionRecord: *mut EXCEPTION_RECORD, - pub ContextRecord: *mut CONTEXT, -} - -pub enum UNWIND_HISTORY_TABLE {} - -#[repr(C)] -pub struct RUNTIME_FUNCTION { - pub BeginAddress: DWORD, - pub EndAddress: DWORD, - pub UnwindData: DWORD, -} - -pub enum CONTEXT {} - -#[repr(C)] -pub struct DISPATCHER_CONTEXT { - pub ControlPc: LPVOID, - pub ImageBase: LPVOID, - pub FunctionEntry: *const RUNTIME_FUNCTION, - pub EstablisherFrame: LPVOID, - pub TargetIp: LPVOID, - pub ContextRecord: *const CONTEXT, - pub LanguageHandler: LPVOID, - pub HandlerData: *const u8, - pub HistoryTable: *const UNWIND_HISTORY_TABLE, -} - -#[repr(C)] -pub enum EXCEPTION_DISPOSITION { - ExceptionContinueExecution, - ExceptionContinueSearch, - ExceptionNestedException, - ExceptionCollidedUnwind, -} -pub use self::EXCEPTION_DISPOSITION::*; - -extern "system" { - #[unwind(allowed)] - pub fn RaiseException(dwExceptionCode: DWORD, - dwExceptionFlags: DWORD, - nNumberOfArguments: DWORD, - lpArguments: *const ULONG_PTR); - #[unwind(allowed)] - pub fn RtlUnwindEx(TargetFrame: LPVOID, - TargetIp: LPVOID, - ExceptionRecord: *const EXCEPTION_RECORD, - ReturnValue: LPVOID, - OriginalContext: *const CONTEXT, - HistoryTable: *const UNWIND_HISTORY_TABLE); - #[unwind(allowed)] - pub fn _CxxThrowException(pExceptionObject: *mut c_void, pThrowInfo: *mut u8); -} diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index 72fb1fd3561f0..41f02a876f597 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -385,7 +385,7 @@ language_item_table! { EhPersonalityLangItem, "eh_personality", eh_personality, Target::Fn; EhUnwindResumeLangItem, "eh_unwind_resume", eh_unwind_resume, Target::Fn; - MSVCTryFilterLangItem, "msvc_try_filter", msvc_try_filter, Target::Static; + EhCatchTypeinfoLangItem, "eh_catch_typeinfo", eh_catch_typeinfo, Target::Static; OwnedBoxLangItem, "owned_box", owned_box, Target::Struct; diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs index 97bd57a7ded06..02424956b9aa5 100644 --- a/src/librustc_codegen_llvm/intrinsic.rs +++ b/src/librustc_codegen_llvm/intrinsic.rs @@ -849,7 +849,7 @@ fn codegen_msvc_try( // We're generating an IR snippet that looks like: // // declare i32 @rust_try(%func, %data, %ptr) { - // %slot = alloca i64* + // %slot = alloca [2 x i64] // invoke %func(%data) to label %normal unwind label %catchswitch // // normal: @@ -873,21 +873,25 @@ fn codegen_msvc_try( // // #include // + // struct rust_panic { + // uint64_t x[2]; + // } + // // int bar(void (*foo)(void), uint64_t *ret) { // try { // foo(); // return 0; - // } catch(uint64_t a[2]) { - // ret[0] = a[0]; - // ret[1] = a[1]; + // } catch(rust_panic a) { + // ret[0] = a.x[0]; + // ret[1] = a.x[1]; // return 1; // } // } // // More information can be found in libstd's seh.rs implementation. - let i64p = bx.type_ptr_to(bx.type_i64()); - let ptr_align = bx.tcx().data_layout.pointer_align.abi; - let slot = bx.alloca(i64p, ptr_align); + let i64_2 = bx.type_array(bx.type_i64(), 2); + let i64_align = bx.tcx().data_layout.i64_align.abi; + let slot = bx.alloca(i64_2, i64_align); bx.invoke(func, &[data], normal.llbb(), catchswitch.llbb(), None); normal.ret(bx.const_i32(0)); @@ -895,22 +899,15 @@ fn codegen_msvc_try( let cs = catchswitch.catch_switch(None, None, 1); catchswitch.add_handler(cs, catchpad.llbb()); - let tydesc = match bx.tcx().lang_items().msvc_try_filter() { + let tydesc = match bx.tcx().lang_items().eh_catch_typeinfo() { Some(did) => bx.get_static(did), - None => bug!("msvc_try_filter not defined"), + None => bug!("eh_catch_typeinfo not defined, but needed for SEH unwinding"), }; let funclet = catchpad.catch_pad(cs, &[tydesc, bx.const_i32(0), slot]); - let addr = catchpad.load(slot, ptr_align); - let i64_align = bx.tcx().data_layout.i64_align.abi; - let arg1 = catchpad.load(addr, i64_align); - let val1 = bx.const_i32(1); - let gep1 = catchpad.inbounds_gep(addr, &[val1]); - let arg2 = catchpad.load(gep1, i64_align); - let local_ptr = catchpad.bitcast(local_ptr, i64p); - let gep2 = catchpad.inbounds_gep(local_ptr, &[val1]); - catchpad.store(arg1, local_ptr, i64_align); - catchpad.store(arg2, gep2, i64_align); + let payload = catchpad.load(slot, i64_align); + let local_ptr = catchpad.bitcast(local_ptr, bx.type_ptr_to(i64_2)); + catchpad.store(payload, local_ptr, i64_align); catchpad.catch_ret(&funclet, caught.llbb()); caught.ret(bx.const_i32(1)); @@ -978,7 +975,14 @@ fn codegen_gnu_try( // rust_try ignores the selector. let lpad_ty = bx.type_struct(&[bx.type_i8p(), bx.type_i32()], false); let vals = catch.landing_pad(lpad_ty, bx.eh_personality(), 1); - catch.add_clause(vals, bx.const_null(bx.type_i8p())); + let tydesc = match bx.tcx().lang_items().eh_catch_typeinfo() { + Some(tydesc) => { + let tydesc = bx.get_static(tydesc); + bx.bitcast(tydesc, bx.type_i8p()) + } + None => bx.const_null(bx.type_i8p()), + }; + catch.add_clause(vals, tydesc); let ptr = catch.extract_value(vals, 0); let ptr_align = bx.tcx().data_layout.pointer_align.abi; let bitcast = catch.bitcast(local_ptr, bx.type_ptr_to(bx.type_i8p())); diff --git a/src/libunwind/libunwind.rs b/src/libunwind/libunwind.rs index 7c9eaa51fd94e..0b39503c0d034 100644 --- a/src/libunwind/libunwind.rs +++ b/src/libunwind/libunwind.rs @@ -244,3 +244,30 @@ if #[cfg(not(all(target_os = "ios", target_arch = "arm")))] { } } } // cfg_if! + +cfg_if::cfg_if! { +if #[cfg(all(windows, target_arch = "x86_64", target_env = "gnu"))] { + // We declare these as opaque types. This is fine since you just need to + // pass them to _GCC_specific_handler and forget about them. + pub enum EXCEPTION_RECORD {} + pub type LPVOID = *mut c_void; + pub enum CONTEXT {} + pub enum DISPATCHER_CONTEXT {} + pub type EXCEPTION_DISPOSITION = c_int; + type PersonalityFn = unsafe extern "C" fn(version: c_int, + actions: _Unwind_Action, + exception_class: _Unwind_Exception_Class, + exception_object: *mut _Unwind_Exception, + context: *mut _Unwind_Context) + -> _Unwind_Reason_Code; + + extern "C" { + pub fn _GCC_specific_handler(exceptionRecord: *mut EXCEPTION_RECORD, + establisherFrame: LPVOID, + contextRecord: *mut CONTEXT, + dispatcherContext: *mut DISPATCHER_CONTEXT, + personality: PersonalityFn) + -> EXCEPTION_DISPOSITION; + } +} +} // cfg_if! diff --git a/src/test/run-make-fulldeps/foreign-exceptions/Makefile b/src/test/run-make-fulldeps/foreign-exceptions/Makefile new file mode 100644 index 0000000000000..7eba52f3c24e8 --- /dev/null +++ b/src/test/run-make-fulldeps/foreign-exceptions/Makefile @@ -0,0 +1,10 @@ +-include ../tools.mk + +all: foo + $(call RUN,foo) + +foo: foo.rs $(call NATIVE_STATICLIB,foo) + $(RUSTC) $< -lfoo $(EXTRARSCXXFLAGS) + +$(TMPDIR)/libfoo.o: foo.cpp + $(call COMPILE_OBJ_CXX,$@,$<) diff --git a/src/test/run-make-fulldeps/foreign-exceptions/foo.cpp b/src/test/run-make-fulldeps/foreign-exceptions/foo.cpp new file mode 100644 index 0000000000000..b0fd65f88e7de --- /dev/null +++ b/src/test/run-make-fulldeps/foreign-exceptions/foo.cpp @@ -0,0 +1,60 @@ +#include +#include +#include + +void println(const char* s) { + puts(s); + fflush(stdout); +} + +struct exception {}; +struct rust_panic {}; + +struct drop_check { + bool* ok; + ~drop_check() { + println("~drop_check"); + + if (ok) + *ok = true; + } +}; + +extern "C" { + void rust_catch_callback(void (*cb)(), bool* rust_ok); + + static void callback() { + println("throwing C++ exception"); + throw exception(); + } + + void throw_cxx_exception() { + bool rust_ok = false; + try { + rust_catch_callback(callback, &rust_ok); + assert(false && "unreachable"); + } catch (exception e) { + println("caught C++ exception"); + assert(rust_ok); + return; + } + assert(false && "did not catch thrown C++ exception"); + } + + void cxx_catch_callback(void (*cb)(), bool* cxx_ok) { + drop_check x; + x.ok = NULL; + try { + cb(); + } catch (rust_panic e) { + assert(false && "shouldn't be able to catch a rust panic"); + } catch (...) { + println("caught foreign exception in catch (...)"); + // Foreign exceptions are caught by catch (...). We only set the ok + // flag if we successfully caught the panic. The destructor of + // drop_check will then set the flag to true if it is executed. + x.ok = cxx_ok; + throw; + } + } +} diff --git a/src/test/run-make-fulldeps/foreign-exceptions/foo.rs b/src/test/run-make-fulldeps/foreign-exceptions/foo.rs new file mode 100644 index 0000000000000..399c78f8d2d02 --- /dev/null +++ b/src/test/run-make-fulldeps/foreign-exceptions/foo.rs @@ -0,0 +1,66 @@ +// Tests that C++ exceptions can unwind through Rust code, run destructors and +// are ignored by catch_unwind. Also tests that Rust panics can unwind through +// C++ code. + +// For linking libstdc++ on MinGW +#![cfg_attr(all(windows, target_env = "gnu"), feature(static_nobundle))] + +#![feature(unwind_attributes)] + +use std::panic::{catch_unwind, AssertUnwindSafe}; + +struct DropCheck<'a>(&'a mut bool); +impl<'a> Drop for DropCheck<'a> { + fn drop(&mut self) { + println!("DropCheck::drop"); + *self.0 = true; + } +} + +extern "C" { + fn throw_cxx_exception(); + + #[unwind(allowed)] + fn cxx_catch_callback(cb: extern "C" fn(), ok: *mut bool); +} + +#[no_mangle] +#[unwind(allowed)] +extern "C" fn rust_catch_callback(cb: extern "C" fn(), rust_ok: &mut bool) { + let _caught_unwind = catch_unwind(AssertUnwindSafe(|| { + let _drop = DropCheck(rust_ok); + cb(); + unreachable!("should have unwound instead of returned"); + })); + unreachable!("catch_unwind should not have caught foreign exception"); +} + +fn throw_rust_panic() { + #[unwind(allowed)] + extern "C" fn callback() { + println!("throwing rust panic"); + panic!(1234i32); + } + + let mut dropped = false; + let mut cxx_ok = false; + let caught_unwind = catch_unwind(AssertUnwindSafe(|| { + let _drop = DropCheck(&mut dropped); + unsafe { + cxx_catch_callback(callback, &mut cxx_ok); + } + unreachable!("should have unwound instead of returned"); + })); + println!("caught rust panic"); + assert!(dropped); + assert!(caught_unwind.is_err()); + let panic_obj = caught_unwind.unwrap_err(); + let panic_int = *panic_obj.downcast_ref::().unwrap(); + assert_eq!(panic_int, 1234); + assert!(cxx_ok); +} + +fn main() { + unsafe { throw_cxx_exception() }; + throw_rust_panic(); +} diff --git a/src/test/run-make-fulldeps/issue-36710/Makefile b/src/test/run-make-fulldeps/issue-36710/Makefile index dc1fbb4cefb84..4f93d97636e60 100644 --- a/src/test/run-make-fulldeps/issue-36710/Makefile +++ b/src/test/run-make-fulldeps/issue-36710/Makefile @@ -6,7 +6,7 @@ all: foo $(call RUN,foo) foo: foo.rs $(call NATIVE_STATICLIB,foo) - $(RUSTC) $< -lfoo $(EXTRACXXFLAGS) + $(RUSTC) $< -lfoo $(EXTRARSCXXFLAGS) $(TMPDIR)/libfoo.o: foo.cpp $(call COMPILE_OBJ_CXX,$@,$<) diff --git a/src/test/run-make-fulldeps/issue-36710/foo.rs b/src/test/run-make-fulldeps/issue-36710/foo.rs index a9d3effbd1806..061f07c324340 100644 --- a/src/test/run-make-fulldeps/issue-36710/foo.rs +++ b/src/test/run-make-fulldeps/issue-36710/foo.rs @@ -1,5 +1,8 @@ // Tests that linking to C++ code with global destructors works. +// For linking libstdc++ on MinGW +#![cfg_attr(all(windows, target_env = "gnu"), feature(static_nobundle))] + extern { fn get() -> u32; } fn main() { diff --git a/src/test/run-make-fulldeps/tools.mk b/src/test/run-make-fulldeps/tools.mk index 9a113b7fa6370..20a5e8e64225d 100644 --- a/src/test/run-make-fulldeps/tools.mk +++ b/src/test/run-make-fulldeps/tools.mk @@ -60,7 +60,7 @@ endif ifdef IS_MSVC COMPILE_OBJ = $(CC) -c -Fo:`cygpath -w $(1)` $(2) -COMPILE_OBJ_CXX = $(CXX) -c -Fo:`cygpath -w $(1)` $(2) +COMPILE_OBJ_CXX = $(CXX) -EHs -c -Fo:`cygpath -w $(1)` $(2) NATIVE_STATICLIB_FILE = $(1).lib NATIVE_STATICLIB = $(TMPDIR)/$(call NATIVE_STATICLIB_FILE,$(1)) OUT_EXE=-Fe:`cygpath -w $(TMPDIR)/$(call BIN,$(1))` \ @@ -80,10 +80,29 @@ ifdef IS_MSVC EXTRACFLAGS := ws2_32.lib userenv.lib advapi32.lib else EXTRACFLAGS := -lws2_32 -luserenv + EXTRACXXFLAGS := -lstdc++ + # So this is a bit hacky: we can't use the DLL version of libstdc++ because + # it pulls in the DLL version of libgcc, which means that we end up with 2 + # instances of the DW2 unwinding implementation. This is a problem on + # i686-pc-windows-gnu because each module (DLL/EXE) needs to register its + # unwind information with the unwinding implementation, and libstdc++'s + # __cxa_throw won't see the unwinding info we registered with our statically + # linked libgcc. + # + # Now, simply statically linking libstdc++ would fix this problem, except + # that it is compiled with the expectation that pthreads is dynamically + # linked as a DLL and will fail to link with a statically linked libpthread. + # + # So we end up with the following hack: we link use static-nobundle to only + # link the parts of libstdc++ that we actually use, which doesn't include + # the dependency on the pthreads DLL. + EXTRARSCXXFLAGS := -l static-nobundle=stdc++ endif else ifeq ($(UNAME),Darwin) EXTRACFLAGS := -lresolv + EXTRACXXFLAGS := -lc++ + EXTRARSCXXFLAGS := -lc++ else ifeq ($(UNAME),FreeBSD) EXTRACFLAGS := -lm -lpthread -lgcc_s @@ -97,6 +116,7 @@ ifeq ($(UNAME),OpenBSD) else EXTRACFLAGS := -lm -lrt -ldl -lpthread EXTRACXXFLAGS := -lstdc++ + EXTRARSCXXFLAGS := -lstdc++ endif endif endif