diff options
Diffstat (limited to 'src/libstd')
| -rw-r--r-- | src/libstd/rt/dwarf/eh.rs | 159 | ||||
| -rw-r--r-- | src/libstd/rt/dwarf/mod.rs | 107 | ||||
| -rw-r--r-- | src/libstd/rt/libunwind.rs | 2 | ||||
| -rw-r--r-- | src/libstd/rt/macros.rs | 4 | ||||
| -rw-r--r-- | src/libstd/rt/mod.rs | 2 | ||||
| -rw-r--r-- | src/libstd/rt/unwind/gcc.rs | 145 | ||||
| -rw-r--r-- | src/libstd/rt/unwind/mod.rs | 17 | ||||
| -rw-r--r-- | src/libstd/rt/unwind/seh64_gnu.rs | 227 |
8 files changed, 532 insertions, 131 deletions
diff --git a/src/libstd/rt/dwarf/eh.rs b/src/libstd/rt/dwarf/eh.rs new file mode 100644 index 00000000000..990501b28db --- /dev/null +++ b/src/libstd/rt/dwarf/eh.rs @@ -0,0 +1,159 @@ +// 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 prelude::v1::*; +use rt::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. + return 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/libstd/rt/dwarf/mod.rs b/src/libstd/rt/dwarf/mod.rs new file mode 100644 index 00000000000..822826bcc83 --- /dev/null +++ b/src/libstd/rt/dwarf/mod.rs @@ -0,0 +1,107 @@ +// 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 prelude::v1::*; +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/libstd/rt/libunwind.rs b/src/libstd/rt/libunwind.rs index d99b31c9f2b..fde612014e9 100644 --- a/src/libstd/rt/libunwind.rs +++ b/src/libstd/rt/libunwind.rs @@ -36,6 +36,7 @@ pub enum _Unwind_Action { #[cfg(target_arch = "arm")] #[repr(C)] +#[derive(Copy, Clone)] pub enum _Unwind_State { _US_VIRTUAL_UNWIND_FRAME = 0, _US_UNWIND_FRAME_STARTING = 1, @@ -46,6 +47,7 @@ pub enum _Unwind_State { } #[repr(C)] +#[derive(Copy, Clone)] pub enum _Unwind_Reason_Code { _URC_NO_REASON = 0, _URC_FOREIGN_EXCEPTION_CAUGHT = 1, diff --git a/src/libstd/rt/macros.rs b/src/libstd/rt/macros.rs index 1e3ab6d34da..414ccc911af 100644 --- a/src/libstd/rt/macros.rs +++ b/src/libstd/rt/macros.rs @@ -18,7 +18,7 @@ macro_rules! rterrln { ::rt::util::dumb_print(format_args!(concat!($fmt, "\n"))) } ); ($fmt:expr, $($arg:expr),*) => ( { - ::rt::util::dumb_print(format_args!(concat!($fmt, "\n"), $($arg)*)) + ::rt::util::dumb_print(format_args!(concat!($fmt, "\n"), $($arg),*)) } ) } @@ -31,7 +31,7 @@ macro_rules! rtdebug { } ); ($str:expr, $($arg:expr),*) => ( { if cfg!(rtdebug) { - rterrln!($str, $($arg)*) + rterrln!($str, $($arg),*) } }) } diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index 7e86bb775a1..56bf73db399 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -47,6 +47,8 @@ pub mod args; mod at_exit_imp; mod libunwind; +mod dwarf; + /// The default error code of the rust runtime if the main thread panics instead /// of exiting cleanly. pub const DEFAULT_ERROR_CODE: isize = 101; diff --git a/src/libstd/rt/unwind/gcc.rs b/src/libstd/rt/unwind/gcc.rs index 23e10ee6c39..55deb048b7e 100644 --- a/src/libstd/rt/unwind/gcc.rs +++ b/src/libstd/rt/unwind/gcc.rs @@ -74,14 +74,7 @@ fn rust_exception_class() -> uw::_Unwind_Exception_Class { // so the behavior of __gcc_personality_v0 is perfectly adequate there, and // - rust_eh_personality_catch, used only by rust_try(), which always catches. // -// Note, however, that for implementation simplicity, rust_eh_personality_catch -// lacks code to install a landing pad, so in order to obtain exception object -// pointer (which it needs to return upstream), rust_try() employs another trick: -// it calls into the nested rust_try_inner(), whose landing pad does not resume -// unwinds. Instead, it extracts the exception pointer and performs a "normal" -// return. -// -// See also: rt/rust_try.ll +// See also: rustc_trans::trans::intrinsic::trans_gnu_try #[cfg(all(not(target_arch = "arm"), not(all(windows, target_arch = "x86_64")), @@ -118,11 +111,11 @@ pub mod eabi { #[lang = "eh_personality_catch"] #[no_mangle] pub extern fn rust_eh_personality_catch( - _version: c_int, + version: c_int, actions: uw::_Unwind_Action, - _exception_class: uw::_Unwind_Exception_Class, - _ue_header: *mut uw::_Unwind_Exception, - _context: *mut uw::_Unwind_Context + exception_class: uw::_Unwind_Exception_Class, + ue_header: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context ) -> uw::_Unwind_Reason_Code { @@ -130,7 +123,10 @@ pub mod eabi { uw::_URC_HANDLER_FOUND // catch! } else { // cleanup phase - uw::_URC_INSTALL_CONTEXT + unsafe { + __gcc_personality_v0(version, actions, exception_class, ue_header, + context) + } } } } @@ -171,11 +167,11 @@ pub mod eabi { #[lang = "eh_personality_catch"] #[no_mangle] pub extern fn rust_eh_personality_catch( - _version: c_int, + version: c_int, actions: uw::_Unwind_Action, - _exception_class: uw::_Unwind_Exception_Class, - _ue_header: *mut uw::_Unwind_Exception, - _context: *mut uw::_Unwind_Context + 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 @@ -183,8 +179,8 @@ pub mod eabi { } else { // cleanup phase unsafe { - __gcc_personality_sj0(_version, actions, _exception_class, _ue_header, - _context) + __gcc_personality_sj0(version, actions, exception_class, ue_header, + context) } } } @@ -222,8 +218,8 @@ pub mod eabi { #[no_mangle] pub extern fn rust_eh_personality_catch( state: uw::_Unwind_State, - _ue_header: *mut uw::_Unwind_Exception, - _context: *mut uw::_Unwind_Context + ue_header: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context ) -> uw::_Unwind_Reason_Code { if (state as c_int & uw::_US_ACTION_MASK as c_int) @@ -231,112 +227,9 @@ pub mod eabi { uw::_URC_HANDLER_FOUND // catch! } else { // cleanup phase - uw::_URC_INSTALL_CONTEXT - } - } -} - -// Win64 SEH (see http://msdn.microsoft.com/en-us/library/1eyas8tf.aspx) -// -// This looks a bit convoluted because rather than implementing a native SEH -// handler, GCC reuses the same personality routine as for the other -// architectures by wrapping it with an "API translator" layer -// (_GCC_specific_handler). - -#[cfg(all(windows, target_arch = "x86_64", not(test)))] -#[doc(hidden)] -#[allow(non_camel_case_types, non_snake_case)] -pub mod eabi { - pub use self::EXCEPTION_DISPOSITION::*; - use rt::libunwind as uw; - use libc::{c_void, c_int}; - - // Fake definitions; these are actually complicated structs, - // but we don't use the contents here. - pub type EXCEPTION_RECORD = c_void; - pub type CONTEXT = c_void; - pub type DISPATCHER_CONTEXT = c_void; - - #[repr(C)] - #[derive(Copy, Clone)] - pub enum EXCEPTION_DISPOSITION { - ExceptionContinueExecution, - ExceptionContinueSearch, - ExceptionNestedException, - ExceptionCollidedUnwind - } - - type _Unwind_Personality_Fn = - extern fn( - 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; - - extern { - fn __gcc_personality_seh0( - exceptionRecord: *mut EXCEPTION_RECORD, - establisherFrame: *mut c_void, - contextRecord: *mut CONTEXT, - dispatcherContext: *mut DISPATCHER_CONTEXT - ) -> EXCEPTION_DISPOSITION; - - fn _GCC_specific_handler( - exceptionRecord: *mut EXCEPTION_RECORD, - establisherFrame: *mut c_void, - contextRecord: *mut CONTEXT, - dispatcherContext: *mut DISPATCHER_CONTEXT, - personality: _Unwind_Personality_Fn - ) -> EXCEPTION_DISPOSITION; - } - - #[lang = "eh_personality"] - #[no_mangle] - extern fn rust_eh_personality( - exceptionRecord: *mut EXCEPTION_RECORD, - establisherFrame: *mut c_void, - contextRecord: *mut CONTEXT, - dispatcherContext: *mut DISPATCHER_CONTEXT - ) -> EXCEPTION_DISPOSITION - { - unsafe { - __gcc_personality_seh0(exceptionRecord, establisherFrame, - contextRecord, dispatcherContext) - } - } - - #[lang = "eh_personality_catch"] - #[no_mangle] - pub extern fn rust_eh_personality_catch( - exceptionRecord: *mut EXCEPTION_RECORD, - establisherFrame: *mut c_void, - contextRecord: *mut CONTEXT, - dispatcherContext: *mut DISPATCHER_CONTEXT - ) -> EXCEPTION_DISPOSITION - { - extern fn inner( - _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 - uw::_URC_INSTALL_CONTEXT + unsafe { + __gcc_personality_v0(state, ue_header, context) } } - - unsafe { - _GCC_specific_handler(exceptionRecord, establisherFrame, - contextRecord, dispatcherContext, - inner) - } } } - diff --git a/src/libstd/rt/unwind/mod.rs b/src/libstd/rt/unwind/mod.rs index 60eced014de..59b2e14643d 100644 --- a/src/libstd/rt/unwind/mod.rs +++ b/src/libstd/rt/unwind/mod.rs @@ -14,7 +14,7 @@ //! "Exception Handling in LLVM" (llvm.org/docs/ExceptionHandling.html) and //! documents linked from it. //! These are also good reads: -//! http://theofilos.cs.columbia.edu/blog/2013/09/22/base_abi/ +//! 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 //! @@ -76,9 +76,20 @@ use sys_common::mutex::Mutex; // The actual unwinding implementation is cfg'd here, and we've got two current // implementations. One goes through SEH on Windows and the other goes through // libgcc via the libunwind-like API. -#[cfg(target_env = "msvc")] #[path = "seh.rs"] #[doc(hidden)] + +// *-pc-windows-msvc +#[cfg(all(windows, target_env = "msvc"))] +#[path = "seh.rs"] #[doc(hidden)] +pub mod imp; + +// x86_64-pc-windows-gnu +#[cfg(all(windows, target_arch="x86_64", target_env="gnu"))] +#[path = "seh64_gnu.rs"] #[doc(hidden)] pub mod imp; -#[cfg(not(target_env = "msvc"))] #[path = "gcc.rs"] #[doc(hidden)] + +// i686-pc-windows-gnu and all others +#[cfg(any(unix, all(windows, target_arch="x86", target_env="gnu")))] +#[path = "gcc.rs"] #[doc(hidden)] pub mod imp; pub type Callback = fn(msg: &(Any + Send), file: &'static str, line: u32); diff --git a/src/libstd/rt/unwind/seh64_gnu.rs b/src/libstd/rt/unwind/seh64_gnu.rs new file mode 100644 index 00000000000..6a061a55fc2 --- /dev/null +++ b/src/libstd/rt/unwind/seh64_gnu.rs @@ -0,0 +1,227 @@ +// 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 prelude::v1::*; + +use any::Any; +use self::EXCEPTION_DISPOSITION::*; +use rt::dwarf::eh; +use core::mem; +use core::ptr; +use simd; +use libc::{c_void, c_ulonglong, DWORD, LPVOID}; +type ULONG_PTR = c_ulonglong; + +// 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: DWORD = 0b1110_u32 << 28; +const MAGIC: DWORD = 0x525354; // "RST" + +const RUST_PANIC: DWORD = ETYPE | (1 << 24) | MAGIC; + +const EXCEPTION_NONCONTINUABLE: DWORD = 0x1; // Noncontinuable exception +const EXCEPTION_UNWINDING: DWORD = 0x2; // Unwind is in progress +const EXCEPTION_EXIT_UNWIND: DWORD = 0x4; // Exit unwind is in progress +const EXCEPTION_STACK_INVALID: DWORD = 0x8; // Stack out of limits or unaligned +const EXCEPTION_NESTED_CALL: DWORD = 0x10; // Nested exception handler call +const EXCEPTION_TARGET_UNWIND: DWORD = 0x20; // Target unwind in progress +const EXCEPTION_COLLIDED_UNWIND: DWORD = 0x40; // Collided exception handler call +const EXCEPTION_UNWIND: DWORD = EXCEPTION_UNWINDING | + EXCEPTION_EXIT_UNWIND | + EXCEPTION_TARGET_UNWIND | + EXCEPTION_COLLIDED_UNWIND; + +#[repr(C)] +pub struct EXCEPTION_RECORD { + ExceptionCode: DWORD, + ExceptionFlags: DWORD, + ExceptionRecord: *const EXCEPTION_RECORD, + ExceptionAddress: LPVOID, + NumberParameters: DWORD, + ExceptionInformation: [ULONG_PTR; 15], +} + +pub type CONTEXT = c_void; +pub type UNWIND_HISTORY_TABLE = c_void; + +#[repr(C)] +pub struct RUNTIME_FUNCTION { + BeginAddress: DWORD, + EndAddress: DWORD, + UnwindData: DWORD, +} + +#[repr(C)] +pub struct DISPATCHER_CONTEXT { + ControlPc: LPVOID, + ImageBase: LPVOID, + FunctionEntry: *const RUNTIME_FUNCTION, + EstablisherFrame: LPVOID, + TargetIp: LPVOID, + ContextRecord: *const CONTEXT, + LanguageHandler: LPVOID, + HandlerData: *const u8, + HistoryTable: *const UNWIND_HISTORY_TABLE, +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub enum EXCEPTION_DISPOSITION { + ExceptionContinueExecution, + ExceptionContinueSearch, + ExceptionNestedException, + ExceptionCollidedUnwind +} + +// From kernel32.dll +extern "system" { + fn RaiseException(dwExceptionCode: DWORD, + dwExceptionFlags: DWORD, + nNumberOfArguments: DWORD, + lpArguments: *const ULONG_PTR); + + fn RtlUnwindEx(TargetFrame: LPVOID, + TargetIp: LPVOID, + ExceptionRecord: *const EXCEPTION_RECORD, + ReturnValue: LPVOID, + OriginalContext: *const CONTEXT, + HistoryTable: *const UNWIND_HISTORY_TABLE); +} + +#[repr(C)] +struct PanicData { + data: Box<Any + Send + 'static> +} + +pub unsafe fn panic(data: Box<Any + Send + 'static>) -> ! { + let panic_ctx = Box::new(PanicData { data: data }); + let params = [Box::into_raw(panic_ctx) as ULONG_PTR]; + rtdebug!("panic: ctx={:X}", params[0]); + RaiseException(RUST_PANIC, + EXCEPTION_NONCONTINUABLE, + params.len() as DWORD, + ¶ms as *const ULONG_PTR); + rtabort!("could not unwind stack"); +} + +pub unsafe fn cleanup(ptr: *mut u8) -> Box<Any + Send + 'static> { + rtdebug!("cleanup: ctx={:X}", ptr as usize); + 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 EXCEPTION_RECORD, + establisherFrame: LPVOID, + contextRecord: *mut CONTEXT, + dispatcherContext: *mut DISPATCHER_CONTEXT +) -> EXCEPTION_DISPOSITION +{ + rust_eh_personality(exceptionRecord, establisherFrame, + contextRecord, dispatcherContext) +} + +#[lang = "eh_personality"] +#[cfg(not(test))] +unsafe extern fn rust_eh_personality( + exceptionRecord: *mut EXCEPTION_RECORD, + establisherFrame: LPVOID, + contextRecord: *mut CONTEXT, + dispatcherContext: *mut DISPATCHER_CONTEXT +) -> EXCEPTION_DISPOSITION +{ + let er = &*exceptionRecord; + let dc = &*dispatcherContext; + rtdebug!("rust_eh_personality: code={:X}, flags={:X}, frame={:X}, ip={:X}", + er.ExceptionCode, er.ExceptionFlags, + establisherFrame as usize, dc.ControlPc as usize); + + if er.ExceptionFlags & EXCEPTION_UNWIND == 0 { // we are in the dispatch phase + if er.ExceptionCode == RUST_PANIC { + if let Some(lpad) = find_landing_pad(dc) { + rtdebug!("unwinding to landing pad {:X}", lpad); + + RtlUnwindEx(establisherFrame, + lpad as LPVOID, + exceptionRecord, + er.ExceptionInformation[0] as LPVOID, // pointer to PanicData + contextRecord, + dc.HistoryTable); + rtabort!("could not unwind"); + } + } + } + ExceptionContinueSearch +} + +// The `resume` instruction, found at the end of the landing pads, and whose job +// is to resume stack unwinding, is typically lowered by LLVM into a call to +// `_Unwind_Resume` routine. To avoid confusion with the same symbol exported +// from libgcc, we redirect it to `rust_eh_unwind_resume`. +// Since resolution of this symbol is done by the linker, `rust_eh_unwind_resume` +// must be marked `pub` + `#[no_mangle]`. (Can we make it a lang item?) + +#[lang = "eh_unwind_resume"] +#[cfg(not(test))] +unsafe extern fn rust_eh_unwind_resume(panic_ctx: LPVOID) { + rtdebug!("rust_eh_unwind_resume: ctx={:X}", panic_ctx as usize); + let params = [panic_ctx as ULONG_PTR]; + RaiseException(RUST_PANIC, + EXCEPTION_NONCONTINUABLE, + params.len() as DWORD, + ¶ms as *const ULONG_PTR); + rtabort!("could not resume unwind"); +} + +unsafe fn find_landing_pad(dc: &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) +} |
