about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorAmanieu d'Antras <amanieu@gmail.com>2019-10-21 02:46:04 +0100
committerAmanieu d'Antras <amanieu@gmail.com>2019-11-03 14:03:21 +0000
commit5f1a0afd22c7f1e26782467503d32d026c55fc0c (patch)
tree7e7fc14e9dadb4770e40294ec56cdc54642e7db4 /src
parent9a8bb3a26e8a058be64a0d778b3b168bc9b74caa (diff)
Allow foreign exceptions to unwind through Rust code
Diffstat (limited to 'src')
-rw-r--r--src/doc/unstable-book/src/language-features/lang-items.md3
-rw-r--r--src/libpanic_unwind/dwarf/eh.rs15
-rw-r--r--src/libpanic_unwind/gcc.rs277
-rw-r--r--src/libpanic_unwind/lib.rs8
-rw-r--r--src/libpanic_unwind/seh64_gnu.rs127
-rw-r--r--src/libunwind/libunwind.rs27
-rw-r--r--src/test/run-make-fulldeps/foreign-exceptions/Makefile10
-rw-r--r--src/test/run-make-fulldeps/foreign-exceptions/foo.cpp59
-rw-r--r--src/test/run-make-fulldeps/foreign-exceptions/foo.rs63
9 files changed, 333 insertions, 256 deletions
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 3ee024c6b58..60fdc6c7b69 100644
--- a/src/doc/unstable-book/src/language-features/lang-items.md
+++ b/src/doc/unstable-book/src/language-features/lang-items.md
@@ -249,9 +249,8 @@ 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)
   - `panic`: `libcore/panicking.rs`
diff --git a/src/libpanic_unwind/dwarf/eh.rs b/src/libpanic_unwind/dwarf/eh.rs
index 07fa2971847..1e9e7e4b835 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<EHAction, ()>
 {
     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/gcc.rs b/src/libpanic_unwind/gcc.rs
index a35847c85fc..4f572fe21b3 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<EHAction, ()>
 {
     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 2089a02083c..50d067b56e4 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"))]
diff --git a/src/libpanic_unwind/seh64_gnu.rs b/src/libpanic_unwind/seh64_gnu.rs
deleted file mode 100644
index 16b699a4437..00000000000
--- 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<dyn Any + Send>,
-}
-
-pub unsafe fn panic(data: Box<dyn Any + Send>) -> 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,
-                      &params as *const c::ULONG_PTR);
-    u32::max_value()
-}
-
-pub fn payload() -> *mut u8 {
-    ptr::null_mut()
-}
-
-pub unsafe fn cleanup(ptr: *mut u8) -> Box<dyn Any + Send> {
-    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,
-                      &params as *const c::ULONG_PTR);
-    intrinsics::abort();
-}
-
-unsafe fn find_landing_pad(dc: &c::DISPATCHER_CONTEXT) -> Option<usize> {
-    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/libunwind/libunwind.rs b/src/libunwind/libunwind.rs
index 7c9eaa51fd9..0b39503c0d0 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 00000000000..fd15db9f151
--- /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 $(EXTRACXXFLAGS)
+
+$(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 00000000000..9a6fab49f49
--- /dev/null
+++ b/src/test/run-make-fulldeps/foreign-exceptions/foo.cpp
@@ -0,0 +1,59 @@
+#include <assert.h>
+#include <stddef.h>
+#include <stdio.h>
+
+void println(const char* s) {
+    puts(s);
+    fflush(stdout);
+}
+
+struct exception {};
+
+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 (exception 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 00000000000..83adf000d9e
--- /dev/null
+++ b/src/test/run-make-fulldeps/foreign-exceptions/foo.rs
@@ -0,0 +1,63 @@
+// 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.
+
+#![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::<i32>().unwrap();
+    assert_eq!(panic_int, 1234);
+    assert!(cxx_ok);
+}
+
+fn main() {
+    unsafe { throw_cxx_exception() };
+    throw_rust_panic();
+}