about summary refs log tree commit diff
path: root/src/libpanic_unwind
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2019-11-03 18:36:59 +0000
committerbors <bors@rust-lang.org>2019-11-03 18:36:59 +0000
commitb520af6fd515b186caed436d75162a42aa183d95 (patch)
tree6d7b585e1cfdde034a31707c546323a07a9837e6 /src/libpanic_unwind
parentb43a6822597061dc18cbdde1769d9815e718d7bb (diff)
parentf223e0d6274a8626b9d9428582384955e7f24b15 (diff)
Auto merge of #65646 - Amanieu:foreign-exceptions, r=nikomatsakis
Allow foreign exceptions to unwind through Rust code and Rust panics to unwind through FFI

This PR fixes interactions between Rust panics and foreign (mainly C++) exceptions.

C++ exceptions (and other FFI exceptions) can now safely unwind through Rust code:
- The FFI function causing the unwind must be marked with `#[unwind(allowed)]`. If this is not the case then LLVM may optimize landing pads away with the assumption that they are unreachable.
- Drop code will be executed as the exception unwinds through the stack, as with a Rust panic.
- `catch_unwind` will *not* catch the exception, instead the exception will silently continue unwinding past it.

Rust panics can now safely unwind through C++ code:
- C++ destructors will be called as the stack unwinds.
- The Rust panic can only be caught with `catch (...)`, after which it can be either rethrown or discarded.
- C++ cannot name the type of the Rust exception object used for unwinding, which means that it can't be caught explicitly or have its contents inspected.

Tests have been added to ensure all of the above works correctly.

Some notes about non-C++ exceptions:
- `pthread_cancel` and `pthread_exit` use unwinding on glibc. This has the same behavior as a C++ exception: destructors are run but it cannot be caught by `catch_unwind`.
- `longjmp` on Windows is implemented using unwinding. Destructors are run on MSVC, but not on MinGW. In both cases the unwind cannot be caught by `catch_unwind`.
- As with C++ exceptions, you need to mark the relevant FFI functions with `#[unwind(allowed)]`, otherwise LLVM will optimize out the destructors since they seem unreachable.

I haven't updated any of the documentation, so officially unwinding through FFI is still UB. However this is a step towards making it well-defined.

Fixes #65441

cc @gnzlbg
r? @alexcrichton
Diffstat (limited to 'src/libpanic_unwind')
-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
7 files changed, 264 insertions, 402 deletions
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);
-}