diff options
| author | Alex Crichton <alex@alexcrichton.com> | 2015-09-08 15:53:46 -0700 |
|---|---|---|
| committer | Alex Crichton <alex@alexcrichton.com> | 2015-09-11 11:19:20 -0700 |
| commit | f4be2026dfb507e5db919cc5df8fd934e05fa0b8 (patch) | |
| tree | 278d5b3916042e7232b7ba98a5ead399a52057eb /src/libstd/rt/unwind | |
| parent | 192c37537bc6ffaee06e8b4099dd09ae43bcfee7 (diff) | |
| download | rust-f4be2026dfb507e5db919cc5df8fd934e05fa0b8.tar.gz rust-f4be2026dfb507e5db919cc5df8fd934e05fa0b8.zip | |
std: Internalize almost all of `std::rt`
This commit does some refactoring to make almost all of the `std::rt` private. Specifically, the following items are no longer part of its API: * DEFAULT_ERROR_CODE * backtrace * unwind * args * at_exit * cleanup * heap (this is just alloc::heap) * min_stack * util The module is now tagged as `#[doc(hidden)]` as the only purpose it's serve is an entry point for the `panic!` macro via the `begin_unwind` and `begin_unwind_fmt` reexports.
Diffstat (limited to 'src/libstd/rt/unwind')
| -rw-r--r-- | src/libstd/rt/unwind/gcc.rs | 235 | ||||
| -rw-r--r-- | src/libstd/rt/unwind/mod.rs | 325 | ||||
| -rw-r--r-- | src/libstd/rt/unwind/seh.rs | 145 | ||||
| -rw-r--r-- | src/libstd/rt/unwind/seh64_gnu.rs | 226 |
4 files changed, 0 insertions, 931 deletions
diff --git a/src/libstd/rt/unwind/gcc.rs b/src/libstd/rt/unwind/gcc.rs deleted file mode 100644 index 55deb048b7e..00000000000 --- a/src/libstd/rt/unwind/gcc.rs +++ /dev/null @@ -1,235 +0,0 @@ -// 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. - -#![allow(private_no_mangle_fns)] - -use prelude::v1::*; - -use any::Any; -use rt::libunwind as uw; - -struct Exception { - uwe: uw::_Unwind_Exception, - cause: Option<Box<Any + Send + 'static>>, -} - -pub unsafe fn panic(data: Box<Any + Send + 'static>) -> ! { - let exception: Box<_> = box 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; - let error = uw::_Unwind_RaiseException(exception_param); - rtabort!("Could not unwind stack, error = {}", error as isize); - - extern fn exception_cleanup(_unwind_code: uw::_Unwind_Reason_Code, - exception: *mut uw::_Unwind_Exception) { - rtdebug!("exception_cleanup()"); - unsafe { - let _: Box<Exception> = Box::from_raw(exception as *mut Exception); - } - } -} - -pub unsafe fn cleanup(ptr: *mut u8) -> Box<Any + Send + 'static> { - let my_ep = ptr as *mut Exception; - rtdebug!("caught {}", (*my_ep).uwe.exception_class); - 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 pure 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")), - not(test)))] -pub mod eabi { - use rt::libunwind 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", not(test)))] -pub mod eabi { - use rt::libunwind 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"), not(test)))] -pub mod eabi { - use rt::libunwind 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 - { - if (state as c_int & uw::_US_ACTION_MASK as c_int) - == uw::_US_VIRTUAL_UNWIND_FRAME as c_int { // search phase - uw::_URC_HANDLER_FOUND // catch! - } - else { // cleanup phase - unsafe { - __gcc_personality_v0(state, ue_header, context) - } - } - } -} diff --git a/src/libstd/rt/unwind/mod.rs b/src/libstd/rt/unwind/mod.rs deleted file mode 100644 index 4feb2d49a98..00000000000 --- a/src/libstd/rt/unwind/mod.rs +++ /dev/null @@ -1,325 +0,0 @@ -// Copyright 2013 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 Rust stack unwinding -//! -//! 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, personality routines invoke cleanup code associated -//! with their stack frames (i.e. destructors). Once stack has been unwound down -//! to the handler frame level, unwinding stops and the last personality routine -//! transfers control to its catch block. -//! -//! ## Frame unwind info registration -//! -//! Each module has its own frame unwind info section (usually ".eh_frame"), and -//! unwinder needs to know about all of them in order for unwinding to be able to -//! cross module boundaries. -//! -//! On some platforms, like Linux, this is achieved by dynamically enumerating -//! currently loaded modules via the dl_iterate_phdr() API and finding all -//! .eh_frame sections. -//! -//! Others, like Windows, require modules to actively register their unwind info -//! sections by calling __register_frame_info() API at startup. In the latter -//! case it is essential that there is only one copy of the unwinder runtime in -//! the process. This is usually achieved by linking to the dynamic version of -//! the unwind runtime. -//! -//! Currently Rust uses unwind runtime provided by libgcc. - -#![allow(dead_code)] -#![allow(unused_imports)] - -use prelude::v1::*; - -use any::Any; -use boxed; -use cell::Cell; -use cmp; -use panicking; -use fmt; -use intrinsics; -use mem; -use sync::atomic::{self, Ordering}; -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. - -// i686-pc-windows-msvc -#[cfg(all(windows, target_arch = "x86", target_env = "msvc"))] -#[path = "seh.rs"] #[doc(hidden)] -pub mod imp; - -// x86_64-pc-windows-* -#[cfg(all(windows, target_arch = "x86_64"))] -#[path = "seh64_gnu.rs"] #[doc(hidden)] -pub mod imp; - -// 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); - -// Variables used for invoking callbacks when a thread starts to unwind. -// -// For more information, see below. -const MAX_CALLBACKS: usize = 16; -static CALLBACKS: [atomic::AtomicUsize; MAX_CALLBACKS] = - [atomic::AtomicUsize::new(0), atomic::AtomicUsize::new(0), - atomic::AtomicUsize::new(0), atomic::AtomicUsize::new(0), - atomic::AtomicUsize::new(0), atomic::AtomicUsize::new(0), - atomic::AtomicUsize::new(0), atomic::AtomicUsize::new(0), - atomic::AtomicUsize::new(0), atomic::AtomicUsize::new(0), - atomic::AtomicUsize::new(0), atomic::AtomicUsize::new(0), - atomic::AtomicUsize::new(0), atomic::AtomicUsize::new(0), - atomic::AtomicUsize::new(0), atomic::AtomicUsize::new(0)]; -static CALLBACK_CNT: atomic::AtomicUsize = atomic::AtomicUsize::new(0); - -thread_local! { static PANICKING: Cell<bool> = Cell::new(false) } - -/// Invoke a closure, capturing the cause of panic if one occurs. -/// -/// This function will return `Ok(())` if the closure did not panic, and will -/// return `Err(cause)` if the closure panics. The `cause` returned is the -/// object with which panic was originally invoked. -/// -/// This function also is unsafe for a variety of reasons: -/// -/// * This is not safe to call in a nested fashion. The unwinding -/// interface for Rust is designed to have at most one try/catch block per -/// thread, not multiple. No runtime checking is currently performed to uphold -/// this invariant, so this function is not safe. A nested try/catch block -/// may result in corruption of the outer try/catch block's state, especially -/// if this is used within a thread itself. -/// -/// * It is not sound to trigger unwinding while already unwinding. Rust threads -/// have runtime checks in place to ensure this invariant, but it is not -/// guaranteed that a rust thread is in place when invoking this function. -/// Unwinding twice can lead to resource leaks where some destructors are not -/// run. -pub unsafe fn try<F: FnOnce()>(f: F) -> Result<(), Box<Any + Send>> { - let mut f = Some(f); - return inner_try(try_fn::<F>, &mut f as *mut _ as *mut u8); - - // If an inner function were not used here, then this generic function `try` - // uses the native symbol `rust_try`, for which the code is statically - // linked into the standard library. This means that the DLL for the - // standard library must have `rust_try` as an exposed symbol that - // downstream crates can link against (because monomorphizations of `try` in - // downstream crates will have a reference to the `rust_try` symbol). - // - // On MSVC this requires the symbol `rust_try` to be tagged with - // `dllexport`, but it's easier to not have conditional `src/rt/rust_try.ll` - // files and instead just have this non-generic shim the compiler can take - // care of exposing correctly. - unsafe fn inner_try(f: fn(*mut u8), data: *mut u8) - -> Result<(), Box<Any + Send>> { - let prev = PANICKING.with(|s| s.get()); - PANICKING.with(|s| s.set(false)); - let ep = intrinsics::try(f, data); - PANICKING.with(|s| s.set(prev)); - if ep.is_null() { - Ok(()) - } else { - Err(imp::cleanup(ep)) - } - } - - fn try_fn<F: FnOnce()>(opt_closure: *mut u8) { - let opt_closure = opt_closure as *mut Option<F>; - unsafe { (*opt_closure).take().unwrap()(); } - } - - extern { - // Rust's try-catch - // When f(...) returns normally, the return value is null. - // When f(...) throws, the return value is a pointer to the caught - // exception object. - fn rust_try(f: extern fn(*mut u8), - data: *mut u8) -> *mut u8; - } -} - -/// Determines whether the current thread is unwinding because of panic. -pub fn panicking() -> bool { - PANICKING.with(|s| s.get()) -} - -// An uninlined, unmangled function upon which to slap yer breakpoints -#[inline(never)] -#[no_mangle] -#[allow(private_no_mangle_fns)] -fn rust_panic(cause: Box<Any + Send + 'static>) -> ! { - rtdebug!("begin_unwind()"); - unsafe { - imp::panic(cause) - } -} - -#[cfg(not(test))] -/// Entry point of panic from the libcore crate. -#[lang = "panic_fmt"] -pub extern fn rust_begin_unwind(msg: fmt::Arguments, - file: &'static str, line: u32) -> ! { - begin_unwind_fmt(msg, &(file, line)) -} - -/// The entry point for unwinding with a formatted message. -/// -/// This is designed to reduce the amount of code required at the call -/// site as much as possible (so that `panic!()` has as low an impact -/// on (e.g.) the inlining of other functions as possible), by moving -/// the actual formatting into this shared place. -#[inline(never)] #[cold] -pub fn begin_unwind_fmt(msg: fmt::Arguments, file_line: &(&'static str, u32)) -> ! { - use fmt::Write; - - // We do two allocations here, unfortunately. But (a) they're - // required with the current scheme, and (b) we don't handle - // panic + OOM properly anyway (see comment in begin_unwind - // below). - - let mut s = String::new(); - let _ = s.write_fmt(msg); - begin_unwind_inner(Box::new(s), file_line) -} - -/// This is the entry point of unwinding for panic!() and assert!(). -#[inline(never)] #[cold] // avoid code bloat at the call sites as much as possible -pub fn begin_unwind<M: Any + Send>(msg: M, file_line: &(&'static str, u32)) -> ! { - // Note that this should be the only allocation performed in this code path. - // Currently this means that panic!() on OOM will invoke this code path, - // but then again we're not really ready for panic on OOM anyway. If - // we do start doing this, then we should propagate this allocation to - // be performed in the parent of this thread instead of the thread that's - // panicking. - - // see below for why we do the `Any` coercion here. - begin_unwind_inner(Box::new(msg), file_line) -} - -/// The core of the unwinding. -/// -/// This is non-generic to avoid instantiation bloat in other crates -/// (which makes compilation of small crates noticeably slower). (Note: -/// we need the `Any` object anyway, we're not just creating it to -/// avoid being generic.) -/// -/// Doing this split took the LLVM IR line counts of `fn main() { panic!() -/// }` from ~1900/3700 (-O/no opts) to 180/590. -#[inline(never)] #[cold] // this is the slow path, please never inline this -fn begin_unwind_inner(msg: Box<Any + Send>, - file_line: &(&'static str, u32)) -> ! { - // Make sure the default failure handler is registered before we look at the - // callbacks. We also use a raw sys-based mutex here instead of a - // `std::sync` one as accessing TLS can cause weird recursive problems (and - // we don't need poison checking). - unsafe { - static LOCK: Mutex = Mutex::new(); - static mut INIT: bool = false; - LOCK.lock(); - if !INIT { - register(panicking::on_panic); - INIT = true; - } - LOCK.unlock(); - } - - // First, invoke call the user-defined callbacks triggered on thread panic. - // - // By the time that we see a callback has been registered (by reading - // MAX_CALLBACKS), the actual callback itself may have not been stored yet, - // so we just chalk it up to a race condition and move on to the next - // callback. Additionally, CALLBACK_CNT may briefly be higher than - // MAX_CALLBACKS, so we're sure to clamp it as necessary. - let callbacks = { - let amt = CALLBACK_CNT.load(Ordering::SeqCst); - &CALLBACKS[..cmp::min(amt, MAX_CALLBACKS)] - }; - for cb in callbacks { - match cb.load(Ordering::SeqCst) { - 0 => {} - n => { - let f: Callback = unsafe { mem::transmute(n) }; - let (file, line) = *file_line; - f(&*msg, file, line); - } - } - }; - - // Now that we've run all the necessary unwind callbacks, we actually - // perform the unwinding. - if panicking() { - // If a thread panics while it's already unwinding then we - // have limited options. Currently our preference is to - // just abort. In the future we may consider resuming - // unwinding or otherwise exiting the thread cleanly. - rterrln!("thread panicked while panicking. aborting."); - unsafe { intrinsics::abort() } - } - PANICKING.with(|s| s.set(true)); - rust_panic(msg); -} - -/// Register a callback to be invoked when a thread unwinds. -/// -/// This is an unsafe and experimental API which allows for an arbitrary -/// callback to be invoked when a thread panics. This callback is invoked on both -/// the initial unwinding and a double unwinding if one occurs. Additionally, -/// the local `Thread` will be in place for the duration of the callback, and -/// the callback must ensure that it remains in place once the callback returns. -/// -/// Only a limited number of callbacks can be registered, and this function -/// returns whether the callback was successfully registered or not. It is not -/// currently possible to unregister a callback once it has been registered. -pub unsafe fn register(f: Callback) -> bool { - match CALLBACK_CNT.fetch_add(1, Ordering::SeqCst) { - // The invocation code has knowledge of this window where the count has - // been incremented, but the callback has not been stored. We're - // guaranteed that the slot we're storing into is 0. - n if n < MAX_CALLBACKS => { - let prev = CALLBACKS[n].swap(mem::transmute(f), Ordering::SeqCst); - rtassert!(prev == 0); - true - } - // If we accidentally bumped the count too high, pull it back. - _ => { - CALLBACK_CNT.store(MAX_CALLBACKS, Ordering::SeqCst); - false - } - } -} diff --git a/src/libstd/rt/unwind/seh.rs b/src/libstd/rt/unwind/seh.rs deleted file mode 100644 index 8c793758166..00000000000 --- a/src/libstd/rt/unwind/seh.rs +++ /dev/null @@ -1,145 +0,0 @@ -// 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. - -//! Win64 SEH (see http://msdn.microsoft.com/en-us/library/1eyas8tf.aspx) -//! -//! 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. Currently this support is somewhat lacking, so what's -//! here is the bare bones of SEH support. -//! -//! 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 (just "cleanup" landing pads) -//! use the personality function `__C_specific_handler`, a function in the -//! CRT, and the unwinding code in Windows will use this personality function -//! to execute all cleanup code on the stack. -//! 3. Eventually the "catch" code in `rust_try` (located in -//! src/rt/rust_try_msvc_64.ll) is executed, which will ensure that the -//! exception being caught is indeed a Rust exception, returning control back -//! into Rust. -//! -//! Some specific differences from the gcc-based exception handling are: -//! -//! * Rust has no custom personality function, it is instead *always* -//! __C_specific_handler, so the filtering is done in a C++-like manner -//! instead of in the personality function itself. Note that the specific -//! syntax for this (found in the rust_try_msvc_64.ll) is taken from an LLVM -//! test case for SEH. -//! * We've got some data to transmit across the unwinding boundary, -//! specifically a `Box<Any + Send + 'static>`. In Dwarf-based unwinding this -//! data is part of the payload of the exception, but I have not currently -//! figured out how to do this with LLVM's bindings. Judging by some comments -//! in the LLVM test cases this may not even be possible currently with LLVM, -//! so this is just abandoned entirely. Instead the data is stored in a -//! thread-local in `panic` and retrieved during `cleanup`. -//! -//! So given all that, the bindings here are pretty small, - -#![allow(bad_style)] - -use prelude::v1::*; - -use any::Any; -use libc::{c_ulong, DWORD, c_void}; -use ptr; -use sys_common::thread_local::StaticKey; - -// 0x R U S T -const RUST_PANIC: DWORD = 0x52555354; -static PANIC_DATA: StaticKey = StaticKey::new(None); - -// This function is provided by kernel32.dll -extern "system" { - fn RaiseException(dwExceptionCode: DWORD, - dwExceptionFlags: DWORD, - nNumberOfArguments: DWORD, - lpArguments: *const c_ulong); -} - -#[repr(C)] -pub struct EXCEPTION_POINTERS { - ExceptionRecord: *mut EXCEPTION_RECORD, - ContextRecord: *mut CONTEXT, -} - -enum CONTEXT {} - -#[repr(C)] -struct EXCEPTION_RECORD { - ExceptionCode: DWORD, - ExceptionFlags: DWORD, - ExceptionRecord: *mut _EXCEPTION_RECORD, - ExceptionAddress: *mut c_void, - NumberParameters: DWORD, - ExceptionInformation: [*mut c_ulong; EXCEPTION_MAXIMUM_PARAMETERS], -} - -enum _EXCEPTION_RECORD {} - -const EXCEPTION_MAXIMUM_PARAMETERS: usize = 15; - -pub unsafe fn panic(data: Box<Any + Send + 'static>) -> ! { - // See module docs above for an explanation of why `data` is stored in a - // thread local instead of being passed as an argument to the - // `RaiseException` function (which can in theory carry along arbitrary - // data). - let exception = Box::new(data); - rtassert!(PANIC_DATA.get().is_null()); - PANIC_DATA.set(Box::into_raw(exception) as *mut u8); - - RaiseException(RUST_PANIC, 0, 0, ptr::null()); - rtabort!("could not unwind stack"); -} - -pub unsafe fn cleanup(ptr: *mut u8) -> Box<Any + Send + 'static> { - // The `ptr` here actually corresponds to the code of the exception, and our - // real data is stored in our thread local. - rtassert!(ptr as DWORD == RUST_PANIC); - - let data = PANIC_DATA.get() as *mut Box<Any + Send + 'static>; - PANIC_DATA.set(ptr::null_mut()); - rtassert!(!data.is_null()); - - *Box::from_raw(data) -} - -// 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 is the -// personality function that is always used. Hence this is just an aborting -// stub. -#[lang = "eh_personality"] -fn rust_eh_personality() { - unsafe { ::intrinsics::abort() } -} - -// This is a function referenced from `rust_try_msvc_64.ll` which is used to -// filter the exceptions being caught by that function. -// -// In theory local variables can be accessed through the `rbp` parameter of this -// function, but a comment in an LLVM test case indicates that this is not -// implemented in LLVM, so this is just an idempotent function which doesn't -// ferry along any other information. -// -// This function just takes a look at the current EXCEPTION_RECORD being thrown -// to ensure that it's code is RUST_PANIC, which was set by the call to -// `RaiseException` above in the `panic` function. -#[no_mangle] -#[lang = "msvc_try_filter"] -pub extern fn __rust_try_filter(eh_ptrs: *mut EXCEPTION_POINTERS, - _rbp: *mut u8) -> i32 { - unsafe { - ((*(*eh_ptrs).ExceptionRecord).ExceptionCode == RUST_PANIC) as i32 - } -} diff --git a/src/libstd/rt/unwind/seh64_gnu.rs b/src/libstd/rt/unwind/seh64_gnu.rs deleted file mode 100644 index 78f969bfbeb..00000000000 --- a/src/libstd/rt/unwind/seh64_gnu.rs +++ /dev/null @@ -1,226 +0,0 @@ -// 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 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 enum CONTEXT {} -pub enum UNWIND_HISTORY_TABLE {} - -#[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) -} |
