about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/doc/unstable-book/src/language-features/lang-items.md6
-rw-r--r--src/libpanic_unwind/dwarf/eh.rs15
-rw-r--r--src/libpanic_unwind/emcc.rs52
-rw-r--r--src/libpanic_unwind/gcc.rs277
-rw-r--r--src/libpanic_unwind/lib.rs9
-rw-r--r--src/libpanic_unwind/seh.rs100
-rw-r--r--src/libpanic_unwind/seh64_gnu.rs127
-rw-r--r--src/libpanic_unwind/windows.rs86
-rw-r--r--src/librustc/middle/lang_items.rs2
-rw-r--r--src/librustc_codegen_llvm/intrinsic.rs44
-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.cpp60
-rw-r--r--src/test/run-make-fulldeps/foreign-exceptions/foo.rs66
-rw-r--r--src/test/run-make-fulldeps/issue-36710/Makefile2
-rw-r--r--src/test/run-make-fulldeps/issue-36710/foo.rs3
-rw-r--r--src/test/run-make-fulldeps/tools.mk22
17 files changed, 480 insertions, 428 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..d4ad65e84b7 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 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/emcc.rs b/src/libpanic_unwind/emcc.rs
index 18e9006468e..3d0d5a4151e 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<dyn Any + Send> {
     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<dyn Any + Send>) -> u32 {
     if exception == ptr::null_mut() {
         return uw::_URC_FATAL_PHASE1_ERROR as u32;
     }
-    let exception = exception as *mut Box<dyn Any + Send>;
-    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 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..d2a0ef7b1dd 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 809e4619812..621813a2fee 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 <stdin.h>
+//      #include <stdint.h>
+//
+//      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<dyn Any + Send>) -> u32 {
@@ -246,6 +239,11 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> 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<dyn Any + Send>) -> 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 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/libpanic_unwind/windows.rs b/src/libpanic_unwind/windows.rs
deleted file mode 100644
index 3257a9d25a5..00000000000
--- 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 72fb1fd3561..41f02a876f5 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 97bd57a7ded..02424956b9a 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 <stdint.h>
         //
+        //      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 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..7eba52f3c24
--- /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 00000000000..b0fd65f88e7
--- /dev/null
+++ b/src/test/run-make-fulldeps/foreign-exceptions/foo.cpp
@@ -0,0 +1,60 @@
+#include <assert.h>
+#include <stddef.h>
+#include <stdio.h>
+
+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 00000000000..399c78f8d2d
--- /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::<i32>().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 dc1fbb4cefb..4f93d97636e 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 a9d3effbd18..061f07c3243 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 9a113b7fa63..20a5e8e6422 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