about summary refs log tree commit diff
path: root/src/libpanic_unwind
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2016-04-08 16:18:40 -0700
committerAlex Crichton <alex@alexcrichton.com>2016-05-09 08:22:36 -0700
commit0ec321f7b541fcbfbf20286beb497e6d9d3352b2 (patch)
tree30abd6498f7e3ae65fa94057e2bd46f6c769fcf2 /src/libpanic_unwind
parent32683ce1930ef1390f20e4ab72650e6804fd1c1b (diff)
downloadrust-0ec321f7b541fcbfbf20286beb497e6d9d3352b2.tar.gz
rust-0ec321f7b541fcbfbf20286beb497e6d9d3352b2.zip
rustc: Implement custom panic runtimes
This commit is an implementation of [RFC 1513] which allows applications to
alter the behavior of panics at compile time. A new compiler flag, `-C panic`,
is added and accepts the values `unwind` or `panic`, with the default being
`unwind`. This model affects how code is generated for the local crate, skipping
generation of landing pads with `-C panic=abort`.

[RFC 1513]: https://github.com/rust-lang/rfcs/blob/master/text/1513-less-unwinding.md

Panic implementations are then provided by crates tagged with
`#![panic_runtime]` and lazily required by crates with
`#![needs_panic_runtime]`. The panic strategy (`-C panic` value) of the panic
runtime must match the final product, and if the panic strategy is not `abort`
then the entire DAG must have the same panic strategy.

With the `-C panic=abort` strategy, users can expect a stable method to disable
generation of landing pads, improving optimization in niche scenarios,
decreasing compile time, and decreasing output binary size. With the `-C
panic=unwind` strategy users can expect the existing ability to isolate failure
in Rust code from the outside world.

Organizationally, this commit dismantles the `sys_common::unwind` module in
favor of some bits moving part of it to `libpanic_unwind` and the rest into the
`panicking` module in libstd. The custom panic runtime support is pretty similar
to the custom allocator support with the only major difference being how the
panic runtime is injected (takes the `-C panic` flag into account).
Diffstat (limited to 'src/libpanic_unwind')
-rw-r--r--src/libpanic_unwind/Cargo.lock27
-rw-r--r--src/libpanic_unwind/Cargo.toml13
-rw-r--r--src/libpanic_unwind/dwarf/eh.rs158
-rw-r--r--src/libpanic_unwind/dwarf/mod.rs106
-rw-r--r--src/libpanic_unwind/gcc.rs335
-rw-r--r--src/libpanic_unwind/lib.rs109
-rw-r--r--src/libpanic_unwind/seh.rs147
-rw-r--r--src/libpanic_unwind/seh64_gnu.rs142
-rw-r--r--src/libpanic_unwind/windows.rs96
9 files changed, 1133 insertions, 0 deletions
diff --git a/src/libpanic_unwind/Cargo.lock b/src/libpanic_unwind/Cargo.lock
new file mode 100644
index 00000000000..20d826d4a47
--- /dev/null
+++ b/src/libpanic_unwind/Cargo.lock
@@ -0,0 +1,27 @@
+[root]
+name = "panic_unwind"
+version = "0.0.0"
+dependencies = [
+ "alloc 0.0.0",
+ "core 0.0.0",
+ "libc 0.0.0",
+]
+
+[[package]]
+name = "alloc"
+version = "0.0.0"
+dependencies = [
+ "core 0.0.0",
+]
+
+[[package]]
+name = "core"
+version = "0.0.0"
+
+[[package]]
+name = "libc"
+version = "0.0.0"
+dependencies = [
+ "core 0.0.0",
+]
+
diff --git a/src/libpanic_unwind/Cargo.toml b/src/libpanic_unwind/Cargo.toml
new file mode 100644
index 00000000000..27edecd6f96
--- /dev/null
+++ b/src/libpanic_unwind/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+authors = ["The Rust Project Developers"]
+name = "panic_unwind"
+version = "0.0.0"
+
+[lib]
+path = "lib.rs"
+
+[dependencies]
+alloc = { path = "../liballoc" }
+core = { path = "../libcore" }
+libc = { path = "../rustc/libc_shim" }
+unwind = { path = "../libunwind" }
diff --git a/src/libpanic_unwind/dwarf/eh.rs b/src/libpanic_unwind/dwarf/eh.rs
new file mode 100644
index 00000000000..1c3fca98a1f
--- /dev/null
+++ b/src/libpanic_unwind/dwarf/eh.rs
@@ -0,0 +1,158 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Parsing of GCC-style Language-Specific Data Area (LSDA)
+//! For details see:
+//!   http://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA/ehframechpt.html
+//!   http://mentorembedded.github.io/cxx-abi/exceptions.pdf
+//!   http://www.airs.com/blog/archives/460
+//!   http://www.airs.com/blog/archives/464
+//!
+//! A reference implementation may be found in the GCC source tree
+//! (<root>/libgcc/unwind-c.c as of this writing)
+
+#![allow(non_upper_case_globals)]
+#![allow(unused)]
+
+use dwarf::DwarfReader;
+use core::mem;
+
+pub const DW_EH_PE_omit     : u8 = 0xFF;
+pub const DW_EH_PE_absptr   : u8 = 0x00;
+
+pub const DW_EH_PE_uleb128  : u8 = 0x01;
+pub const DW_EH_PE_udata2   : u8 = 0x02;
+pub const DW_EH_PE_udata4   : u8 = 0x03;
+pub const DW_EH_PE_udata8   : u8 = 0x04;
+pub const DW_EH_PE_sleb128  : u8 = 0x09;
+pub const DW_EH_PE_sdata2   : u8 = 0x0A;
+pub const DW_EH_PE_sdata4   : u8 = 0x0B;
+pub const DW_EH_PE_sdata8   : u8 = 0x0C;
+
+pub const DW_EH_PE_pcrel    : u8 = 0x10;
+pub const DW_EH_PE_textrel  : u8 = 0x20;
+pub const DW_EH_PE_datarel  : u8 = 0x30;
+pub const DW_EH_PE_funcrel  : u8 = 0x40;
+pub const DW_EH_PE_aligned  : u8 = 0x50;
+
+pub const DW_EH_PE_indirect : u8 = 0x80;
+
+#[derive(Copy, Clone)]
+pub struct EHContext {
+    pub ip: usize,         // Current instruction pointer
+    pub func_start: usize, // Address of the current function
+    pub text_start: usize, // Address of the code section
+    pub data_start: usize, // Address of the data section
+}
+
+pub unsafe fn find_landing_pad(lsda: *const u8, context: &EHContext)
+                               -> Option<usize> {
+    if lsda.is_null() {
+        return None;
+    }
+
+    let func_start = context.func_start;
+    let mut reader = DwarfReader::new(lsda);
+
+    let start_encoding = reader.read::<u8>();
+    // base address for landing pad offsets
+    let lpad_base = if start_encoding != DW_EH_PE_omit {
+        read_encoded_pointer(&mut reader, context, start_encoding)
+    } else {
+        func_start
+    };
+
+    let ttype_encoding = reader.read::<u8>();
+    if ttype_encoding != DW_EH_PE_omit {
+        // Rust doesn't analyze exception types, so we don't care about the type table
+        reader.read_uleb128();
+    }
+
+    let call_site_encoding = reader.read::<u8>();
+    let call_site_table_length = reader.read_uleb128();
+    let action_table = reader.ptr.offset(call_site_table_length as isize);
+    // Return addresses point 1 byte past the call instruction, which could
+    // be in the next IP range.
+    let ip = context.ip-1;
+
+    while reader.ptr < action_table {
+        let cs_start = read_encoded_pointer(&mut reader, context, call_site_encoding);
+        let cs_len = read_encoded_pointer(&mut reader, context, call_site_encoding);
+        let cs_lpad = read_encoded_pointer(&mut reader, context, call_site_encoding);
+        let cs_action = reader.read_uleb128();
+        // Callsite table is sorted by cs_start, so if we've passed the ip, we
+        // may stop searching.
+        if ip < func_start + cs_start {
+            break
+        }
+        if ip < func_start + cs_start + cs_len {
+            if cs_lpad != 0 {
+                return Some(lpad_base + cs_lpad);
+            } else {
+                return None;
+            }
+        }
+    }
+    // IP range not found: gcc's C++ personality calls terminate() here,
+    // however the rest of the languages treat this the same as cs_lpad == 0.
+    // We follow this suit.
+    None
+}
+
+#[inline]
+fn round_up(unrounded: usize, align: usize) -> usize {
+    assert!(align.is_power_of_two());
+    (unrounded + align - 1) & !(align - 1)
+}
+
+unsafe fn read_encoded_pointer(reader: &mut DwarfReader,
+                               context: &EHContext,
+                               encoding: u8) -> usize {
+    assert!(encoding != DW_EH_PE_omit);
+
+    // DW_EH_PE_aligned implies it's an absolute pointer value
+    if encoding == DW_EH_PE_aligned {
+        reader.ptr = round_up(reader.ptr as usize,
+                              mem::size_of::<usize>()) as *const u8;
+        return reader.read::<usize>();
+    }
+
+    let mut result = match encoding & 0x0F {
+        DW_EH_PE_absptr => reader.read::<usize>(),
+        DW_EH_PE_uleb128 => reader.read_uleb128() as usize,
+        DW_EH_PE_udata2 => reader.read::<u16>() as usize,
+        DW_EH_PE_udata4 => reader.read::<u32>() as usize,
+        DW_EH_PE_udata8 => reader.read::<u64>() as usize,
+        DW_EH_PE_sleb128 => reader.read_sleb128() as usize,
+        DW_EH_PE_sdata2 => reader.read::<i16>() as usize,
+        DW_EH_PE_sdata4 => reader.read::<i32>() as usize,
+        DW_EH_PE_sdata8 => reader.read::<i64>() as usize,
+        _ => panic!()
+    };
+
+    result += match encoding & 0x70 {
+        DW_EH_PE_absptr => 0,
+        // relative to address of the encoded value, despite the name
+        DW_EH_PE_pcrel => reader.ptr as usize,
+        DW_EH_PE_textrel => { assert!(context.text_start != 0);
+                              context.text_start },
+        DW_EH_PE_datarel => { assert!(context.data_start != 0);
+                              context.data_start },
+        DW_EH_PE_funcrel => { assert!(context.func_start != 0);
+                              context.func_start },
+        _ => panic!()
+    };
+
+    if encoding & DW_EH_PE_indirect != 0 {
+        result = *(result as *const usize);
+    }
+
+    result
+}
diff --git a/src/libpanic_unwind/dwarf/mod.rs b/src/libpanic_unwind/dwarf/mod.rs
new file mode 100644
index 00000000000..cde21f90811
--- /dev/null
+++ b/src/libpanic_unwind/dwarf/mod.rs
@@ -0,0 +1,106 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Utilities for parsing DWARF-encoded data streams.
+//! See http://www.dwarfstd.org,
+//! DWARF-4 standard, Section 7 - "Data Representation"
+
+// This module is used only by x86_64-pc-windows-gnu for now, but we
+// are compiling it everywhere to avoid regressions.
+#![allow(unused)]
+
+pub mod eh;
+
+use core::mem;
+
+pub struct DwarfReader {
+    pub ptr : *const u8
+}
+
+#[repr(C,packed)]
+struct Unaligned<T>(T);
+
+impl DwarfReader {
+
+    pub fn new(ptr : *const u8) -> DwarfReader {
+        DwarfReader {
+            ptr : ptr
+        }
+    }
+
+    // DWARF streams are packed, so e.g. a u32 would not necessarily be aligned
+    // on a 4-byte boundary. This may cause problems on platforms with strict
+    // alignment requirements. By wrapping data in a "packed" struct, we are
+    // telling the backend to generate "misalignment-safe" code.
+    pub unsafe fn read<T:Copy>(&mut self) -> T {
+        let Unaligned(result) = *(self.ptr as *const Unaligned<T>);
+        self.ptr = self.ptr.offset(mem::size_of::<T>() as isize);
+        result
+    }
+
+    // ULEB128 and SLEB128 encodings are defined in Section 7.6 - "Variable
+    // Length Data".
+    pub unsafe fn read_uleb128(&mut self) -> u64 {
+        let mut shift : usize = 0;
+        let mut result : u64 = 0;
+        let mut byte : u8;
+        loop {
+            byte = self.read::<u8>();
+            result |= ((byte & 0x7F) as u64) << shift;
+            shift += 7;
+            if byte & 0x80 == 0 {
+                break;
+            }
+        }
+        result
+    }
+
+    pub unsafe fn read_sleb128(&mut self) -> i64 {
+        let mut shift : usize = 0;
+        let mut result : u64 = 0;
+        let mut byte : u8;
+        loop {
+            byte = self.read::<u8>();
+            result |= ((byte & 0x7F) as u64) << shift;
+            shift += 7;
+            if byte & 0x80 == 0 {
+                break;
+            }
+        }
+        // sign-extend
+        if shift < 8 * mem::size_of::<u64>() && (byte & 0x40) != 0 {
+            result |= (!0 as u64) << shift;
+        }
+        result as i64
+    }
+}
+
+#[test]
+fn dwarf_reader() {
+    let encoded: &[u8] = &[1,
+                           2, 3,
+                           4, 5, 6, 7,
+                           0xE5, 0x8E, 0x26,
+                           0x9B, 0xF1, 0x59,
+                           0xFF, 0xFF];
+
+    let mut reader = DwarfReader::new(encoded.as_ptr());
+
+    unsafe {
+        assert!(reader.read::<u8>() == u8::to_be(1u8));
+        assert!(reader.read::<u16>() == u16::to_be(0x0203));
+        assert!(reader.read::<u32>() == u32::to_be(0x04050607));
+
+        assert!(reader.read_uleb128() == 624485);
+        assert!(reader.read_sleb128() == -624485);
+
+        assert!(reader.read::<i8>() == i8::to_be(-1));
+    }
+}
diff --git a/src/libpanic_unwind/gcc.rs b/src/libpanic_unwind/gcc.rs
new file mode 100644
index 00000000000..50b2e1534d7
--- /dev/null
+++ b/src/libpanic_unwind/gcc.rs
@@ -0,0 +1,335 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Implementation of panics backed by libgcc/libunwind (in some form)
+//!
+//! For background on exception handling and stack unwinding please see
+//! "Exception Handling in LLVM" (llvm.org/docs/ExceptionHandling.html) and
+//! documents linked from it.
+//! These are also good reads:
+//!     http://mentorembedded.github.io/cxx-abi/abi-eh.html
+//!     http://monoinfinito.wordpress.com/series/exception-handling-in-c/
+//!     http://www.airs.com/blog/index.php?s=exception+frames
+//!
+//! ## A brief summary
+//!
+//! Exception handling happens in two phases: a search phase and a cleanup
+//! phase.
+//!
+//! In both phases the unwinder walks stack frames from top to bottom using
+//! information from the stack frame unwind sections of the current process's
+//! modules ("module" here refers to an OS module, i.e. an executable or a
+//! dynamic library).
+//!
+//! For each stack frame, it invokes the associated "personality routine", whose
+//! address is also stored in the unwind info section.
+//!
+//! In the search phase, the job of a personality routine is to examine
+//! exception object being thrown, and to decide whether it should be caught at
+//! that stack frame.  Once the handler frame has been identified, cleanup phase
+//! begins.
+//!
+//! In the cleanup phase, the unwinder invokes each personality routine again.
+//! This time it decides which (if any) cleanup code needs to be run for
+//! the current stack frame.  If so, the control is transferred to a special
+//! branch in the function body, the "landing pad", which invokes destructors,
+//! frees memory, etc.  At the end of the landing pad, control is transferred
+//! back to the unwinder and unwinding resumes.
+//!
+//! Once stack has been unwound down to the handler frame level, unwinding stops
+//! and the last personality routine transfers control to the catch block.
+//!
+//! ## `eh_personality` and `eh_unwind_resume`
+//!
+//! These language items are used by the compiler when generating unwind info.
+//! The first one is the personality routine described above.  The second one
+//! allows compilation target to customize the process of resuming unwind at the
+//! end of the landing pads. `eh_unwind_resume` is used only if
+//! `custom_unwind_resume` flag in the target options is set.
+
+#![allow(private_no_mangle_fns)]
+
+use core::any::Any;
+use alloc::boxed::Box;
+
+use unwind as uw;
+
+#[repr(C)]
+struct Exception {
+    _uwe: uw::_Unwind_Exception,
+    cause: Option<Box<Any + Send>>,
+}
+
+pub unsafe fn panic(data: Box<Any + Send>) -> u32 {
+    let exception = Box::new(Exception {
+        _uwe: uw::_Unwind_Exception {
+            exception_class: rust_exception_class(),
+            exception_cleanup: exception_cleanup,
+            private: [0; uw::unwinder_private_data_size],
+        },
+        cause: Some(data),
+    });
+    let exception_param = Box::into_raw(exception) as *mut uw::_Unwind_Exception;
+    return uw::_Unwind_RaiseException(exception_param) as u32;
+
+    extern fn exception_cleanup(_unwind_code: uw::_Unwind_Reason_Code,
+                                exception: *mut uw::_Unwind_Exception) {
+        unsafe {
+            let _: Box<Exception> = Box::from_raw(exception as *mut Exception);
+        }
+    }
+}
+
+pub fn payload() -> *mut u8 {
+    0 as *mut u8
+}
+
+pub unsafe fn cleanup(ptr: *mut u8) -> Box<Any + Send> {
+    let my_ep = ptr as *mut Exception;
+    let cause = (*my_ep).cause.take();
+    uw::_Unwind_DeleteException(ptr as *mut _);
+    cause.unwrap()
+}
+
+// Rust's exception class identifier.  This is used by personality routines to
+// determine whether the exception was thrown by their own runtime.
+fn rust_exception_class() -> uw::_Unwind_Exception_Class {
+    // M O Z \0  R U S T -- vendor, language
+    0x4d4f5a_00_52555354
+}
+
+// We could implement our personality routine in Rust, however exception
+// info decoding is tedious.  More importantly, personality routines have to
+// handle various platform quirks, which are not fun to maintain.  For this
+// reason, we attempt to reuse personality routine of the C language:
+// __gcc_personality_v0.
+//
+// Since C does not support exception catching, __gcc_personality_v0 simply
+// always returns _URC_CONTINUE_UNWIND in search phase, and always returns
+// _URC_INSTALL_CONTEXT (i.e. "invoke cleanup code") in cleanup phase.
+//
+// This is pretty close to Rust's exception handling approach, except that Rust
+// does have a single "catch-all" handler at the bottom of each thread's stack.
+// So we have two versions of the personality routine:
+// - rust_eh_personality, used by all cleanup landing pads, which never catches,
+//   so the behavior of __gcc_personality_v0 is perfectly adequate there, and
+// - rust_eh_personality_catch, used only by rust_try(), which always catches.
+//
+// See also: rustc_trans::trans::intrinsic::trans_gnu_try
+
+#[cfg(all(not(target_arch = "arm"),
+          not(all(windows, target_arch = "x86_64"))))]
+pub mod eabi {
+    use unwind as uw;
+    use libc::c_int;
+
+    extern {
+        fn __gcc_personality_v0(version: c_int,
+                                actions: uw::_Unwind_Action,
+                                exception_class: uw::_Unwind_Exception_Class,
+                                ue_header: *mut uw::_Unwind_Exception,
+                                context: *mut uw::_Unwind_Context)
+            -> uw::_Unwind_Reason_Code;
+    }
+
+    #[lang = "eh_personality"]
+    #[no_mangle]
+    extern fn rust_eh_personality(
+        version: c_int,
+        actions: uw::_Unwind_Action,
+        exception_class: uw::_Unwind_Exception_Class,
+        ue_header: *mut uw::_Unwind_Exception,
+        context: *mut uw::_Unwind_Context
+    ) -> uw::_Unwind_Reason_Code
+    {
+        unsafe {
+            __gcc_personality_v0(version, actions, exception_class, ue_header,
+                                 context)
+        }
+    }
+
+    #[lang = "eh_personality_catch"]
+    #[no_mangle]
+    pub extern fn rust_eh_personality_catch(
+        version: c_int,
+        actions: uw::_Unwind_Action,
+        exception_class: uw::_Unwind_Exception_Class,
+        ue_header: *mut uw::_Unwind_Exception,
+        context: *mut uw::_Unwind_Context
+    ) -> uw::_Unwind_Reason_Code
+    {
+
+        if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase
+            uw::_URC_HANDLER_FOUND // catch!
+        }
+        else { // cleanup phase
+            unsafe {
+                __gcc_personality_v0(version, actions, exception_class, ue_header,
+                                     context)
+            }
+        }
+    }
+}
+
+// iOS on armv7 is using SjLj exceptions and therefore requires to use
+// a specialized personality routine: __gcc_personality_sj0
+
+#[cfg(all(target_os = "ios", target_arch = "arm"))]
+pub mod eabi {
+    use unwind as uw;
+    use libc::c_int;
+
+    extern {
+        fn __gcc_personality_sj0(version: c_int,
+                                actions: uw::_Unwind_Action,
+                                exception_class: uw::_Unwind_Exception_Class,
+                                ue_header: *mut uw::_Unwind_Exception,
+                                context: *mut uw::_Unwind_Context)
+            -> uw::_Unwind_Reason_Code;
+    }
+
+    #[lang = "eh_personality"]
+    #[no_mangle]
+    pub extern fn rust_eh_personality(
+        version: c_int,
+        actions: uw::_Unwind_Action,
+        exception_class: uw::_Unwind_Exception_Class,
+        ue_header: *mut uw::_Unwind_Exception,
+        context: *mut uw::_Unwind_Context
+    ) -> uw::_Unwind_Reason_Code
+    {
+        unsafe {
+            __gcc_personality_sj0(version, actions, exception_class, ue_header,
+                                  context)
+        }
+    }
+
+    #[lang = "eh_personality_catch"]
+    #[no_mangle]
+    pub extern fn rust_eh_personality_catch(
+        version: c_int,
+        actions: uw::_Unwind_Action,
+        exception_class: uw::_Unwind_Exception_Class,
+        ue_header: *mut uw::_Unwind_Exception,
+        context: *mut uw::_Unwind_Context
+    ) -> uw::_Unwind_Reason_Code
+    {
+        if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase
+            uw::_URC_HANDLER_FOUND // catch!
+        }
+        else { // cleanup phase
+            unsafe {
+                __gcc_personality_sj0(version, actions, exception_class, ue_header,
+                                      context)
+            }
+        }
+    }
+}
+
+
+// ARM EHABI uses a slightly different personality routine signature,
+// but otherwise works the same.
+#[cfg(all(target_arch = "arm", not(target_os = "ios")))]
+pub mod eabi {
+    use unwind as uw;
+    use libc::c_int;
+
+    extern {
+        fn __gcc_personality_v0(state: uw::_Unwind_State,
+                                ue_header: *mut uw::_Unwind_Exception,
+                                context: *mut uw::_Unwind_Context)
+            -> uw::_Unwind_Reason_Code;
+    }
+
+    #[lang = "eh_personality"]
+    #[no_mangle]
+    extern fn rust_eh_personality(
+        state: uw::_Unwind_State,
+        ue_header: *mut uw::_Unwind_Exception,
+        context: *mut uw::_Unwind_Context
+    ) -> uw::_Unwind_Reason_Code
+    {
+        unsafe {
+            __gcc_personality_v0(state, ue_header, context)
+        }
+    }
+
+    #[lang = "eh_personality_catch"]
+    #[no_mangle]
+    pub extern fn rust_eh_personality_catch(
+        state: uw::_Unwind_State,
+        ue_header: *mut uw::_Unwind_Exception,
+        context: *mut uw::_Unwind_Context
+    ) -> uw::_Unwind_Reason_Code
+    {
+        // 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 as c_int & uw::_US_ACTION_MASK as c_int)
+                           == uw::_US_VIRTUAL_UNWIND_FRAME as c_int
+               && (state as c_int & uw::_US_FORCE_UNWIND as c_int) == 0 { // search phase
+            uw::_URC_HANDLER_FOUND // catch!
+        }
+        else { // cleanup phase
+            unsafe {
+                __gcc_personality_v0(state, ue_header, context)
+            }
+        }
+    }
+}
+
+// See docs in the `unwind` module.
+#[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))]
+#[lang = "eh_unwind_resume"]
+#[unwind]
+unsafe extern fn rust_eh_unwind_resume(panic_ctx: *mut u8) -> ! {
+    uw::_Unwind_Resume(panic_ctx as *mut uw::_Unwind_Exception);
+}
+
+// Frame unwind info registration
+//
+// Each module's image contains a frame unwind info section (usually
+// ".eh_frame").  When a module is loaded/unloaded into the process, the
+// unwinder must be informed about the location of this section in memory. The
+// methods of achieving that vary by the platform.  On some (e.g. Linux), the
+// unwinder can discover unwind info sections on its own (by dynamically
+// enumerating currently loaded modules via the dl_iterate_phdr() API and
+// finding their ".eh_frame" sections); Others, like Windows, require modules
+// to actively register their unwind info sections via unwinder API.
+//
+// This module defines two symbols which are referenced and called from
+// rsbegin.rs to reigster our information with the GCC runtime. The
+// implementation of stack unwinding is (for now) deferred to libgcc_eh, however
+// Rust crates use these Rust-specific entry points to avoid potential clashes
+// with any GCC runtime.
+#[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))]
+pub mod eh_frame_registry {
+    #[link(name = "gcc_eh")]
+    #[cfg(not(cargobuild))]
+    extern {}
+
+    extern {
+        fn __register_frame_info(eh_frame_begin: *const u8, object: *mut u8);
+        fn __deregister_frame_info(eh_frame_begin: *const u8, object: *mut u8);
+    }
+
+    #[no_mangle]
+    pub unsafe extern fn rust_eh_register_frames(eh_frame_begin: *const u8,
+                                                 object: *mut u8) {
+        __register_frame_info(eh_frame_begin, object);
+    }
+
+    #[no_mangle]
+    pub  unsafe extern fn rust_eh_unregister_frames(eh_frame_begin: *const u8,
+                                                   object: *mut u8) {
+        __deregister_frame_info(eh_frame_begin, object);
+    }
+}
diff --git a/src/libpanic_unwind/lib.rs b/src/libpanic_unwind/lib.rs
new file mode 100644
index 00000000000..17cbd2e0d4c
--- /dev/null
+++ b/src/libpanic_unwind/lib.rs
@@ -0,0 +1,109 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Implementation of panics via stack unwinding
+//!
+//! This crate is an implementation of panics in Rust using "most native" stack
+//! unwinding mechanism of the platform this is being compiled for. This
+//! 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.
+//!
+//! More documentation about each implementation can be found in the respective
+//! module.
+
+#![no_std]
+#![crate_name = "panic_unwind"]
+#![crate_type = "rlib"]
+#![unstable(feature = "panic_unwind", issue = "32837")]
+#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
+       html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
+       html_root_url = "https://doc.rust-lang.org/nightly/",
+       issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/")]
+#![cfg_attr(not(stage0), deny(warnings))]
+
+#![feature(alloc)]
+#![feature(core_intrinsics)]
+#![feature(lang_items)]
+#![feature(libc)]
+#![feature(panic_unwind)]
+#![feature(raw)]
+#![feature(staged_api)]
+#![feature(unwind_attributes)]
+#![cfg_attr(target_env = "msvc", feature(raw))]
+
+#![cfg_attr(not(stage0), panic_runtime)]
+#![cfg_attr(not(stage0), feature(panic_runtime))]
+
+extern crate alloc;
+extern crate libc;
+extern crate unwind;
+
+use core::intrinsics;
+use core::mem;
+use core::raw;
+
+// Rust runtime's startup objects depend on these symbols, so make them public.
+#[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))]
+pub use imp::eh_frame_registry::*;
+
+// *-pc-windows-msvc
+#[cfg(target_env = "msvc")]
+#[path = "seh.rs"]
+mod imp;
+
+// x86_64-pc-windows-gnu
+#[cfg(all(windows, target_arch = "x86_64", target_env = "gnu"))]
+#[path = "seh64_gnu.rs"]
+mod imp;
+
+// i686-pc-windows-gnu and all others
+#[cfg(any(unix, all(windows, target_arch = "x86", target_env = "gnu")))]
+#[path = "gcc.rs"]
+mod imp;
+
+mod dwarf;
+mod windows;
+
+// Entry point for catching an exception, implemented using the `try` intrinsic
+// in the compiler.
+//
+// The interaction between the `payload` function and the compiler is pretty
+// hairy and tightly coupled, for more information see the compiler's
+// implementation of this.
+#[no_mangle]
+pub unsafe extern fn __rust_maybe_catch_panic(f: fn(*mut u8),
+                                              data: *mut u8,
+                                              data_ptr: *mut usize,
+                                              vtable_ptr: *mut usize)
+                                              -> u32 {
+    let mut payload = imp::payload();
+    if intrinsics::try(f, data, &mut payload as *mut _ as *mut _) == 0 {
+        0
+    } else {
+        let obj = mem::transmute::<_, raw::TraitObject>(imp::cleanup(payload));
+        *data_ptr = obj.data as usize;
+        *vtable_ptr = obj.vtable as usize;
+        1
+    }
+}
+
+// Entry point for raising an exception, just delegates to the platform-specific
+// implementation.
+#[no_mangle]
+pub unsafe extern fn __rust_start_panic(data: usize, vtable: usize) -> u32 {
+    imp::panic(mem::transmute(raw::TraitObject {
+        data: data as *mut (),
+        vtable: vtable as *mut (),
+    }))
+}
diff --git a/src/libpanic_unwind/seh.rs b/src/libpanic_unwind/seh.rs
new file mode 100644
index 00000000000..c451eeca237
--- /dev/null
+++ b/src/libpanic_unwind/seh.rs
@@ -0,0 +1,147 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Windows SEH
+//!
+//! On Windows (currently only on MSVC), the default exception handling
+//! mechanism is Structured Exception Handling (SEH). This is quite different
+//! than Dwarf-based exception handling (e.g. what other unix platforms use) in
+//! terms of compiler internals, so LLVM is required to have a good deal of
+//! extra support for SEH.
+//!
+//! In a nutshell, what happens here is:
+//!
+//! 1. The `panic` function calls the standard Windows function `RaiseException`
+//!    with a Rust-specific code, triggering the unwinding process.
+//! 2. All landing pads generated by the compiler use the personality function
+//!    `__C_specific_handler` on 64-bit and `__except_handler3` on 32-bit,
+//!    functions in the CRT, and the unwinding code in Windows will use this
+//!    personality function to execute all cleanup code on the stack.
+//! 3. All compiler-generated calls to `invoke` have a landing pad set as a
+//!    `cleanuppad` LLVM instruction, which indicates the start of the cleanup
+//!    routine. The personality (in step 2, defined in the CRT) is responsible
+//!    for running the cleanup routines.
+//! 4. Eventually the "catch" code in the `try` intrinsic (generated by the
+//!    compiler) is executed, which will ensure that the exception being caught
+//!    is indeed a Rust exception, indicating that control should come back to
+//!    Rust. This is done via a `catchswitch` plus a `catchpad` instruction in
+//!    LLVM IR terms, finally returning normal control to the program with a
+//!    `catchret` instruction. The `try` intrinsic uses a filter function to
+//!    detect what kind of exception is being thrown, and this detection is
+//!    implemented as the msvc_try_filter language item below.
+//!
+//! Some specific differences from the gcc-based exception handling are:
+//!
+//! * Rust has no custom personality function, it is instead *always*
+//!   __C_specific_handler or __except_handler3, so the filtering is done in a
+//!   C++-like manner instead of in the personality function itself. Note that
+//!   the precise codegen for this was lifted from an LLVM test case for SEH
+//!   (this is the `__rust_try_filter` function below).
+//! * We've got some data to transmit across the unwinding boundary,
+//!   specifically a `Box<Any + Send>`. Like with Dwarf exceptions
+//!   these two pointers are stored as a payload in the exception itself. On
+//!   MSVC, however, there's no need for an extra allocation because the call
+//!   stack is preserved while filter functions are being executed. This means
+//!   that the pointers are passed directly to `RaiseException` which are then
+//!   recovered in the filter function to be written to the stack frame of the
+//!   `try` intrinsic.
+//!
+//! [win64]: http://msdn.microsoft.com/en-us/library/1eyas8tf.aspx
+//! [llvm]: http://llvm.org/docs/ExceptionHandling.html#background-on-windows-exceptions
+
+use alloc::boxed::Box;
+use core::any::Any;
+use core::intrinsics;
+use core::mem;
+use core::raw;
+
+use windows as c;
+
+// A code which indicates panics that originate from Rust. Note that some of the
+// upper bits are used by the system so we just set them to 0 and ignore them.
+//                           0x 0 R S T
+const RUST_PANIC: c::DWORD = 0x00525354;
+
+pub unsafe fn panic(data: Box<Any + Send>) -> u32 {
+    // As mentioned above, the call stack here is preserved while the filter
+    // functions are running, so it's ok to pass stack-local arrays into
+    // `RaiseException`.
+    //
+    // The two pointers of the `data` trait object are written to the stack,
+    // passed to `RaiseException`, and they're later extracted by the filter
+    // function below in the "custom exception information" section of the
+    // `EXCEPTION_RECORD` type.
+    let ptrs = mem::transmute::<_, raw::TraitObject>(data);
+    let ptrs = [ptrs.data, ptrs.vtable];
+    c::RaiseException(RUST_PANIC, 0, 2, ptrs.as_ptr() as *mut _);
+    u32::max_value()
+}
+
+pub fn payload() -> [usize; 2] {
+    [0; 2]
+}
+
+pub unsafe fn cleanup(payload: [usize; 2]) -> Box<Any + Send> {
+    mem::transmute(raw::TraitObject {
+        data: payload[0] as *mut _,
+        vtable: payload[1] as *mut _,
+    })
+}
+
+// This is quite a special function, and it's not literally passed in as the
+// filter function for the `catchpad` of the `try` intrinsic. The compiler
+// actually generates its own filter function wrapper which will delegate to
+// this for the actual execution logic for whether the exception should be
+// caught. The reasons for this are:
+//
+// * Each architecture has a slightly different ABI for the filter function
+//   here. For example on x86 there are no arguments but on x86_64 there are
+//   two.
+// * This function needs access to the stack frame of the `try` intrinsic
+//   which is using this filter as a catch pad. This is because the payload
+//   of this exception, `Box<Any>`, needs to be transmitted to that
+//   location.
+//
+// Both of these differences end up using a ton of weird llvm-specific
+// intrinsics, so it's actually pretty difficult to express the entire
+// filter function in Rust itself. As a compromise, the compiler takes care
+// of all the weird LLVM-specific and platform-specific stuff, getting to
+// the point where this function makes the actual decision about what to
+// catch given two parameters.
+//
+// The first parameter is `*mut EXCEPTION_POINTERS` which is some contextual
+// information about the exception being filtered, and the second pointer is
+// `*mut *mut [usize; 2]` (the payload here). This value points directly
+// into the stack frame of the `try` intrinsic itself, and we use it to copy
+// information from the exception onto the stack.
+#[lang = "msvc_try_filter"]
+#[cfg(not(test))]
+unsafe extern fn __rust_try_filter(eh_ptrs: *mut u8,
+                                   payload: *mut u8) -> i32 {
+    let eh_ptrs = eh_ptrs as *mut c::EXCEPTION_POINTERS;
+    let payload = payload as *mut *mut [usize; 2];
+    let record = &*(*eh_ptrs).ExceptionRecord;
+    if record.ExceptionCode != RUST_PANIC {
+        return 0
+    }
+    (**payload)[0] = record.ExceptionInformation[0] as usize;
+    (**payload)[1] = record.ExceptionInformation[1] as usize;
+    return 1
+}
+
+// This is required by the compiler to exist (e.g. it's a lang item), but
+// it's never actually called by the compiler because __C_specific_handler
+// or _except_handler3 is the personality function that is always used.
+// Hence this is just an aborting stub.
+#[lang = "eh_personality"]
+#[cfg(not(test))]
+fn rust_eh_personality() {
+    unsafe { intrinsics::abort() }
+}
diff --git a/src/libpanic_unwind/seh64_gnu.rs b/src/libpanic_unwind/seh64_gnu.rs
new file mode 100644
index 00000000000..adb38d857ea
--- /dev/null
+++ b/src/libpanic_unwind/seh64_gnu.rs
@@ -0,0 +1,142 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Unwinding implementation of top of native Win64 SEH,
+//! however the unwind handler data (aka LSDA) uses GCC-compatible encoding.
+
+#![allow(bad_style)]
+#![allow(private_no_mangle_fns)]
+
+use alloc::boxed::Box;
+
+use core::any::Any;
+use core::intrinsics;
+use dwarf::eh;
+use 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<Any + Send>
+}
+
+pub unsafe fn panic(data: Box<Any + Send>) -> u32 {
+    let panic_ctx = Box::new(PanicData { data: 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 {
+    0 as *mut u8
+}
+
+pub unsafe fn cleanup(ptr: *mut u8) -> Box<Any + Send> {
+    let panic_ctx = Box::from_raw(ptr as *mut PanicData);
+    return 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_catch"]
+#[cfg(not(test))]
+unsafe extern fn rust_eh_personality_catch(
+    exceptionRecord: *mut c::EXCEPTION_RECORD,
+    establisherFrame: c::LPVOID,
+    contextRecord: *mut c::CONTEXT,
+    dispatcherContext: *mut c::DISPATCHER_CONTEXT
+) -> c::EXCEPTION_DISPOSITION
+{
+    rust_eh_personality(exceptionRecord, establisherFrame,
+                        contextRecord, dispatcherContext)
+}
+
+#[lang = "eh_personality"]
+#[cfg(not(test))]
+unsafe extern 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]
+unsafe extern 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 = eh::EHContext {
+        ip: dc.ControlPc as usize,
+        func_start: dc.ImageBase as usize + (*dc.FunctionEntry).BeginAddress as usize,
+        text_start: dc.ImageBase as usize,
+        data_start: 0
+    };
+    eh::find_landing_pad(dc.HandlerData, &eh_ctx)
+}
diff --git a/src/libpanic_unwind/windows.rs b/src/libpanic_unwind/windows.rs
new file mode 100644
index 00000000000..a0ccbc00028
--- /dev/null
+++ b/src/libpanic_unwind/windows.rs
@@ -0,0 +1,96 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![allow(bad_style)]
+#![allow(dead_code)]
+#![cfg(windows)]
+
+use libc::{c_void, c_ulong, c_long, c_ulonglong};
+
+pub use self::EXCEPTION_DISPOSITION::*;
+pub type DWORD = c_ulong;
+pub type LONG = c_long;
+pub type ULONG_PTR = c_ulonglong;
+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)]
+#[allow(dead_code)] // we only use some variants
+pub enum EXCEPTION_DISPOSITION {
+    ExceptionContinueExecution,
+    ExceptionContinueSearch,
+    ExceptionNestedException,
+    ExceptionCollidedUnwind
+}
+
+extern "system" {
+    #[unwind]
+    pub fn RaiseException(dwExceptionCode: DWORD,
+                          dwExceptionFlags: DWORD,
+                          nNumberOfArguments: DWORD,
+                          lpArguments: *const ULONG_PTR);
+    #[unwind]
+    pub fn RtlUnwindEx(TargetFrame: LPVOID,
+                       TargetIp: LPVOID,
+                       ExceptionRecord: *const EXCEPTION_RECORD,
+                       ReturnValue: LPVOID,
+                       OriginalContext: *const CONTEXT,
+                       HistoryTable: *const UNWIND_HISTORY_TABLE);
+}