diff options
| author | bors <bors@rust-lang.org> | 2015-05-20 00:31:55 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2015-05-20 00:31:55 +0000 |
| commit | 43cf733bfa5affc74485daeae179cc2f855b5512 (patch) | |
| tree | de08f6fe265dd327ffd6c3bc27167d7c6807d0b9 /src/libstd | |
| parent | c322dbbf8a518d00c6db689ca80b0047f2328890 (diff) | |
| parent | cb3071b2736244db03f800102f123f437fabc9c1 (diff) | |
| download | rust-43cf733bfa5affc74485daeae179cc2f855b5512.tar.gz rust-43cf733bfa5affc74485daeae179cc2f855b5512.zip | |
Auto merge of #25350 - alexcrichton:msvc, r=brson
Special thanks to @retep998 for the [excellent writeup](https://github.com/rust-lang/rfcs/issues/1061) of tasks to be done and @ricky26 for initially blazing the trail here!
# MSVC Support
This goal of this series of commits is to add MSVC support to the Rust compiler
and build system, allowing it more easily interoperate with Visual Studio
installations and native libraries compiled outside of MinGW.
The tl;dr; of this change is that there is a new target of the compiler,
`x86_64-pc-windows-msvc`, which will not interact with the MinGW toolchain at
all and will instead use `link.exe` to assemble output artifacts.
## Why try to use MSVC?
With today's Rust distribution, when you install a compiler on Windows you also
install `gcc.exe` and a number of supporting libraries by default (this can be
opted out of). This allows installations to remain independent of MinGW
installations, but it still generally requires native code to be linked with
MinGW instead of MSVC. Some more background can also be found in #1768 about the
incompatibilities between MinGW and MSVC.
Overall the current installation strategy is quite nice so long as you don't
interact with native code, but once you do the usage of a MinGW-based `gcc.exe`
starts to get quite painful.
Relying on a nonstandard Windows toolchain has also been a long-standing "code
smell" of Rust and has been slated for remedy for quite some time now. Using a
standard toolchain is a great motivational factor for improving the
interoperability of Rust code with the native system.
## What does it mean to use MSVC?
"Using MSVC" can be a bit of a nebulous concept, but this PR defines it as:
* The build system for Rust will build as much code as possible with the MSVC
compiler, `cl.exe`.
* The build system will use native MSVC tools for managing archives.
* The compiler will link all output with `link.exe` instead of `gcc.exe`.
None of these are currently implemented today, but all are required for the
compiler to fluently interoperate with MSVC.
## How does this all work?
At the highest level, this PR adds a new target triple to the Rust compiler:
x86_64-pc-windows-msvc
All logic for using MSVC or not is scoped within this triple and code can
conditionally build for MSVC or MinGW via:
#[cfg(target_env = "msvc")]
It is expected that auto builders will be set up for MSVC-based compiles in
addition to the existing MinGW-based compiles, and we will likely soon start
shipping MSVC nightlies where `x86_64-pc-windows-msvc` is the host target triple
of the compiler.
# Summary of changes
Here I'll explain at a high level what many of the changes made were targeted
at, but many more details can be found in the commits themselves. Many thanks to
@retep998 for the excellent writeup in rust-lang/rfcs#1061 and @rick26 for a lot
of the initial proof-of-concept work!
## Build system changes
As is probably expected, a large chunk of this PR is changes to Rust's build
system to build with MSVC. At a high level **it is an explicit non goal** to
enable building outside of a MinGW shell, instead all Makefile infrastructure we
have today is retrofitted with support to use MSVC instead of the standard MSVC
toolchain. Some of the high-level changes are:
* The configure script now detects when MSVC is being targeted and adds a number
of additional requirements about the build environment:
* The `--msvc-root` option must be specified or `cl.exe` must be in PATH to
discover where MSVC is installed. The compiler in use is also required to
target x86_64.
* Once the MSVC root is known, the INCLUDE/LIB environment variables are
scraped so they can be reexported by the build system.
* CMake is required to build LLVM with MSVC (and LLVM is also configured with
CMake instead of the normal configure script).
* jemalloc is currently unconditionally disabled for MSVC targets as jemalloc
isn't a hard requirement and I don't know how to build it with MSVC.
* Invocations of a C and/or C++ compiler are now abstracted behind macros to
appropriately call the underlying compiler with the correct format of
arguments, for example there is now a macro for "assemble an archive from
objects" instead of hard-coded invocations of `$(AR) crus liboutput.a ...`
* The output filenames for standard libraries such as morestack/compiler-rt are
now "more correct" on windows as they are shipped as `foo.lib` instead of
`libfoo.a`.
* Rust targets can now depend on native tools provided by LLVM, and as you'll
see in the commits the entire MSVC target depends on `llvm-ar.exe`.
* Support for custom arbitrary makefile dependencies of Rust targets has been
added. The MSVC target for `rustc_llvm` currently requires a custom `.DEF`
file to be passed to the linker to get further linkages to complete.
## Compiler changes
The modifications made to the compiler have so far largely been minor tweaks
here and there, mostly just adding a layer of abstraction over whether MSVC or a
GNU-like linker is being used. At a high-level these changes are:
* The section name for metadata storage in dynamic libraries is called `.rustc`
for MSVC-based platorms as section names cannot contain more than 8
characters.
* The implementation of `rustc_back::Archive` was refactored, but the
functionality has remained the same.
* Targets can now specify the default `ar` utility to use, and for MSVC this
defaults to `llvm-ar.exe`
* The building of the linker command in `rustc_trans::back::link` has been
abstracted behind a trait for the same code path to be used between GNU and
MSVC linkers.
## Standard library changes
Only a few small changes were required to the stadnard library itself, and only
for minor differences between the C runtime of msvcrt.dll and MinGW's libc.a
* Some function names for floating point functions have leading underscores, and
some are not present at all.
* Linkage to the `advapi32` library for crypto-related functions is now
explicit.
* Some small bits of C code here and there were fixed for compatibility with
MSVC's cl.exe compiler.
# Future Work
This commit is not yet a 100% complete port to using MSVC as there are still
some key components missing as well as some unimplemented optimizations. This PR
is already getting large enough that I wanted to draw the line here, but here's
a list of what is not implemented in this PR, on purpose:
## Unwinding
The revision of our LLVM submodule [does not seem to implement][llvm] does not
support lowering SEH exception handling on the Windows MSVC targets, so
unwinding support is not currently implemented for the standard library (it's
lowered to an abort).
[llvm]: https://github.com/rust-lang/llvm/blob/rust-llvm-2015-02-19/lib/CodeGen/Passes.cpp#L454-L461
It looks like, however, that upstream LLVM has quite a bit more support for SEH
unwinding and landing pads than the current revision we have, so adding support
will likely just involve updating LLVM and then adding some shims of our own
here and there.
## dllimport and dllexport
An interesting part of Windows which MSVC forces our hand on (and apparently
MinGW didn't) is the usage of `dllimport` and `dllexport` attributes in LLVM IR
as well as native dependencies (in C these correspond to
`__declspec(dllimport)`).
Whenever a dynamic library is built by MSVC it must have its public interface
specified by functions tagged with `dllexport` or otherwise they're not
available to be linked against. This poses a few problems for the compiler, some
of which are somewhat fundamental, but this commit alters the compiler to attach
the `dllexport` attribute to all LLVM functions that are reachable (e.g. they're
already tagged with external linkage). This is suboptimal for a few reasons:
* If an object file will never be included in a dynamic library, there's no need
to attach the dllexport attribute. Most object files in Rust are not destined
to become part of a dll as binaries are statically linked by default.
* If the compiler is emitting both an rlib and a dylib, the same source object
file is currently used but with MSVC this may be less feasible. The compiler
may be able to get around this, but it may involve some invasive changes to
deal with this.
The flipside of this situation is that whenever you link to a dll and you import
a function from it, the import should be tagged with `dllimport`. At this time,
however, the compiler does not emit `dllimport` for any declarations other than
constants (where it is required), which is again suboptimal for even more
reasons!
* Calling a function imported from another dll without using `dllimport` causes
the linker/compiler to have extra overhead (one `jmp` instruction on x86) when
calling the function.
* The same object file may be used in different circumstances, so a function may
be imported from a dll if the object is linked into a dll, but it may be
just linked against if linked into an rlib.
* The compiler has no knowledge about whether native functions should be tagged
dllimport or not.
For now the compiler takes the perf hit (I do not have any numbers to this
effect) by marking very little as `dllimport` and praying the linker will take
care of everything. Fixing this problem will likely require adding a few
attributes to Rust itself (feature gated at the start) and then strongly
recommending static linkage on Windows! This may also involve shipping a
statically linked compiler on Windows instead of a dynamically linked compiler,
but these sorts of changes are pretty invasive and aren't part of this PR.
## CI integration
Thankfully we don't need to set up a new snapshot bot for the changes made here as our snapshots are freestanding already, we should be able to use the same snapshot to bootstrap both MinGW and MSVC compilers (once a new snapshot is made from these changes).
I plan on setting up a new suite of auto bots which are testing MSVC configurations for now as well, for now they'll just be bootstrapping and not running tests, but once unwinding is implemented they'll start running all tests as well and we'll eventually start gating on them as well.
---
I'd love as many eyes on this as we've got as this was one of my first interactions with MSVC and Visual Studio, so there may be glaring holes that I'm missing here and there!
cc @retep998, @ricky26, @vadimcn, @klutzy
r? @brson
Diffstat (limited to 'src/libstd')
| -rw-r--r-- | src/libstd/num/f32.rs | 27 | ||||
| -rw-r--r-- | src/libstd/num/f64.rs | 9 | ||||
| -rw-r--r-- | src/libstd/rand/os.rs | 1 | ||||
| -rw-r--r-- | src/libstd/rt/unwind.rs | 615 | ||||
| -rw-r--r-- | src/libstd/rt/unwind/gcc.rs | 342 | ||||
| -rw-r--r-- | src/libstd/rt/unwind/mod.rs | 319 | ||||
| -rw-r--r-- | src/libstd/rt/unwind/seh.rs | 30 |
7 files changed, 715 insertions, 628 deletions
diff --git a/src/libstd/num/f32.rs b/src/libstd/num/f32.rs index 1ee3aab2727..e31d97b3240 100644 --- a/src/libstd/num/f32.rs +++ b/src/libstd/num/f32.rs @@ -32,7 +32,6 @@ pub use core::f32::consts; mod cmath { use libc::{c_float, c_int}; - #[link_name = "m"] extern { pub fn acosf(n: c_float) -> c_float; pub fn asinf(n: c_float) -> c_float; @@ -44,13 +43,10 @@ mod cmath { pub fn erfcf(n: c_float) -> c_float; pub fn expm1f(n: c_float) -> c_float; pub fn fdimf(a: c_float, b: c_float) -> c_float; - pub fn frexpf(n: c_float, value: &mut c_int) -> c_float; pub fn fmaxf(a: c_float, b: c_float) -> c_float; pub fn fminf(a: c_float, b: c_float) -> c_float; pub fn fmodf(a: c_float, b: c_float) -> c_float; pub fn nextafterf(x: c_float, y: c_float) -> c_float; - pub fn hypotf(x: c_float, y: c_float) -> c_float; - pub fn ldexpf(x: c_float, n: c_int) -> c_float; pub fn logbf(n: c_float) -> c_float; pub fn log1pf(n: c_float) -> c_float; pub fn ilogbf(n: c_float) -> c_int; @@ -60,12 +56,27 @@ mod cmath { pub fn tanhf(n: c_float) -> c_float; pub fn tgammaf(n: c_float) -> c_float; - #[cfg(unix)] + #[cfg_attr(all(windows, target_env = "msvc"), link_name = "__lgammaf_r")] pub fn lgammaf_r(n: c_float, sign: &mut c_int) -> c_float; + #[cfg_attr(all(windows, target_env = "msvc"), link_name = "_hypotf")] + pub fn hypotf(x: c_float, y: c_float) -> c_float; - #[cfg(windows)] - #[link_name="__lgammaf_r"] - pub fn lgammaf_r(n: c_float, sign: &mut c_int) -> c_float; + #[cfg(any(unix, all(windows, not(target_env = "msvc"))))] + pub fn frexpf(n: c_float, value: &mut c_int) -> c_float; + #[cfg(any(unix, all(windows, not(target_env = "msvc"))))] + pub fn ldexpf(x: c_float, n: c_int) -> c_float; + } + + #[cfg(all(windows, target_env = "msvc"))] + pub unsafe fn ldexpf(x: c_float, n: c_int) -> c_float { + f64::ldexp(x as f64, n as isize) as c_float + } + + #[cfg(all(windows, target_env = "msvc"))] + pub unsafe fn frexpf(x: c_float, value: &mut c_int) -> c_float { + let (a, b) = f64::frexp(x as f64); + *value = b as c_int; + a as c_float } } diff --git a/src/libstd/num/f64.rs b/src/libstd/num/f64.rs index 398afcb553c..e87855ffd4e 100644 --- a/src/libstd/num/f64.rs +++ b/src/libstd/num/f64.rs @@ -48,7 +48,6 @@ mod cmath { pub fn fmod(a: c_double, b: c_double) -> c_double; pub fn nextafter(x: c_double, y: c_double) -> c_double; pub fn frexp(n: c_double, value: &mut c_int) -> c_double; - pub fn hypot(x: c_double, y: c_double) -> c_double; pub fn ldexp(x: c_double, n: c_int) -> c_double; pub fn logb(n: c_double) -> c_double; pub fn log1p(n: c_double) -> c_double; @@ -69,11 +68,11 @@ mod cmath { pub fn y1(n: c_double) -> c_double; pub fn yn(i: c_int, n: c_double) -> c_double; - #[cfg(unix)] - pub fn lgamma_r(n: c_double, sign: &mut c_int) -> c_double; - #[cfg(windows)] - #[link_name="__lgamma_r"] + #[cfg_attr(all(windows, target_env = "msvc"), link_name = "__lgamma_r")] pub fn lgamma_r(n: c_double, sign: &mut c_int) -> c_double; + + #[cfg_attr(all(windows, target_env = "msvc"), link_name = "_hypot")] + pub fn hypot(x: c_double, y: c_double) -> c_double; } } diff --git a/src/libstd/rand/os.rs b/src/libstd/rand/os.rs index 3c36f0f1d49..885adf19ca1 100644 --- a/src/libstd/rand/os.rs +++ b/src/libstd/rand/os.rs @@ -279,6 +279,7 @@ mod imp { const CRYPT_VERIFYCONTEXT: DWORD = 0xF0000000; #[allow(non_snake_case)] + #[link(name = "advapi32")] extern "system" { fn CryptAcquireContextA(phProv: *mut HCRYPTPROV, pszContainer: LPCSTR, diff --git a/src/libstd/rt/unwind.rs b/src/libstd/rt/unwind.rs deleted file mode 100644 index b24099505ed..00000000000 --- a/src/libstd/rt/unwind.rs +++ /dev/null @@ -1,615 +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://theofilos.cs.columbia.edu/blog/2013/09/22/base_abi/ -//! 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. - -use prelude::v1::*; - -use any::Any; -use boxed; -use cell::Cell; -use cmp; -use panicking; -use fmt; -use intrinsics; -use libc::c_void; -use mem; -use sync::atomic::{self, Ordering}; -use sys_common::mutex::{Mutex, MUTEX_INIT}; - -use rt::libunwind as uw; - -struct Exception { - uwe: uw::_Unwind_Exception, - cause: Option<Box<Any + Send + 'static>>, -} - -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::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT, - atomic::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT, - atomic::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT, - atomic::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT, - atomic::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT, - atomic::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT, - atomic::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT, - atomic::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT]; -static CALLBACK_CNT: atomic::AtomicUsize = atomic::ATOMIC_USIZE_INIT; - -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); - - let prev = PANICKING.with(|s| s.get()); - PANICKING.with(|s| s.set(false)); - let ep = rust_try(try_fn::<F>, &mut f as *mut _ as *mut c_void); - PANICKING.with(|s| s.set(prev)); - return if ep.is_null() { - Ok(()) - } else { - let my_ep = ep as *mut Exception; - rtdebug!("caught {}", (*my_ep).uwe.exception_class); - let cause = (*my_ep).cause.take(); - uw::_Unwind_DeleteException(ep); - Err(cause.unwrap()) - }; - - extern fn try_fn<F: FnOnce()>(opt_closure: *mut c_void) { - let opt_closure = opt_closure as *mut Option<F>; - unsafe { (*opt_closure).take().unwrap()(); } - } - - #[link(name = "rustrt_native", kind = "static")] - #[cfg(not(test))] - extern {} - - 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 c_void), - data: *mut c_void) -> *mut uw::_Unwind_Exception; - } -} - -/// 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 { - 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(cause), - }; - let exception_param = boxed::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); - } - } -} - -// 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. -// -// 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 - -#[cfg(all(not(target_arch = "arm"), - not(all(windows, target_arch = "x86_64")), - not(test)))] -#[doc(hidden)] -pub mod eabi { - use rt::libunwind as uw; - use libc::c_int; - - extern "C" { - 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] // referenced from rust_try.ll - #[allow(private_no_mangle_fns)] - 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) - } - } - - #[no_mangle] // referenced from rust_try.ll - pub extern "C" 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 - uw::_URC_INSTALL_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)))] -#[doc(hidden)] -pub mod eabi { - use rt::libunwind as uw; - use libc::c_int; - - extern "C" { - 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] // referenced from rust_try.ll - pub extern "C" 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) - } - } - - #[no_mangle] // referenced from rust_try.ll - pub extern "C" 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)))] -#[doc(hidden)] -pub mod eabi { - use rt::libunwind as uw; - use libc::c_int; - - extern "C" { - 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] // referenced from rust_try.ll - #[allow(private_no_mangle_fns)] - extern "C" 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) - } - } - - #[no_mangle] // referenced from rust_try.ll - pub extern "C" 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 - 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}; - - #[repr(C)] - pub struct EXCEPTION_RECORD; - #[repr(C)] - pub struct CONTEXT; - #[repr(C)] - pub struct DISPATCHER_CONTEXT; - - #[repr(C)] - #[derive(Copy, Clone)] - pub enum EXCEPTION_DISPOSITION { - ExceptionContinueExecution, - ExceptionContinueSearch, - ExceptionNestedException, - ExceptionCollidedUnwind - } - - type _Unwind_Personality_Fn = - extern "C" 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 "C" { - 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] // referenced from rust_try.ll - #[allow(private_no_mangle_fns)] - extern "C" 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) - } - } - - #[no_mangle] // referenced from rust_try.ll - pub extern "C" fn rust_eh_personality_catch( - exceptionRecord: *mut EXCEPTION_RECORD, - establisherFrame: *mut c_void, - contextRecord: *mut CONTEXT, - dispatcherContext: *mut DISPATCHER_CONTEXT - ) -> EXCEPTION_DISPOSITION - { - extern "C" 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_specific_handler(exceptionRecord, establisherFrame, - contextRecord, dispatcherContext, - inner) - } - } -} - -#[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_INIT; - 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/gcc.rs b/src/libstd/rt/unwind/gcc.rs new file mode 100644 index 00000000000..39b32a3f08e --- /dev/null +++ b/src/libstd/rt/unwind/gcc.rs @@ -0,0 +1,342 @@ +// 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. + +use prelude::v1::*; + +use any::Any; +use boxed; +use libc::c_void; +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 = boxed::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 c_void) -> 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. +// +// 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 + +#[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 "C" { + 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] // referenced from rust_try.ll + #[allow(private_no_mangle_fns)] + 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) + } + } + + #[no_mangle] // referenced from rust_try.ll + pub extern "C" 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 + uw::_URC_INSTALL_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 "C" { + 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] // referenced from rust_try.ll + pub extern "C" 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) + } + } + + #[no_mangle] // referenced from rust_try.ll + pub extern "C" 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 "C" { + 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] // referenced from rust_try.ll + #[allow(private_no_mangle_fns)] + extern "C" 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) + } + } + + #[no_mangle] // referenced from rust_try.ll + pub extern "C" 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 + 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}; + + #[repr(C)] + pub struct EXCEPTION_RECORD; + #[repr(C)] + pub struct CONTEXT; + #[repr(C)] + pub struct DISPATCHER_CONTEXT; + + #[repr(C)] + #[derive(Copy, Clone)] + pub enum EXCEPTION_DISPOSITION { + ExceptionContinueExecution, + ExceptionContinueSearch, + ExceptionNestedException, + ExceptionCollidedUnwind + } + + type _Unwind_Personality_Fn = + extern "C" 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 "C" { + 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] // referenced from rust_try.ll + #[allow(private_no_mangle_fns)] + extern "C" 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) + } + } + + #[no_mangle] // referenced from rust_try.ll + pub extern "C" fn rust_eh_personality_catch( + exceptionRecord: *mut EXCEPTION_RECORD, + establisherFrame: *mut c_void, + contextRecord: *mut CONTEXT, + dispatcherContext: *mut DISPATCHER_CONTEXT + ) -> EXCEPTION_DISPOSITION + { + extern "C" 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_specific_handler(exceptionRecord, establisherFrame, + contextRecord, dispatcherContext, + inner) + } + } +} + diff --git a/src/libstd/rt/unwind/mod.rs b/src/libstd/rt/unwind/mod.rs new file mode 100644 index 00000000000..576035ffe9a --- /dev/null +++ b/src/libstd/rt/unwind/mod.rs @@ -0,0 +1,319 @@ +// 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://theofilos.cs.columbia.edu/blog/2013/09/22/base_abi/ +//! 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 libc::c_void; +use mem; +use sync::atomic::{self, Ordering}; +use sys_common::mutex::{Mutex, MUTEX_INIT}; + +// 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)] +pub mod imp; +#[cfg(not(target_env = "msvc"))] #[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::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT, + atomic::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT, + atomic::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT, + atomic::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT, + atomic::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT, + atomic::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT, + atomic::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT, + atomic::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT]; +static CALLBACK_CNT: atomic::AtomicUsize = atomic::ATOMIC_USIZE_INIT; + +thread_local! { static PANICKING: Cell<bool> = Cell::new(false) } + +#[link(name = "rustrt_native", kind = "static")] +#[cfg(not(test))] +extern {} + +/// 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 c_void); + + // 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: extern fn(*mut c_void), data: *mut c_void) + -> Result<(), Box<Any + Send>> { + let prev = PANICKING.with(|s| s.get()); + PANICKING.with(|s| s.set(false)); + let ep = rust_try(f, data); + PANICKING.with(|s| s.set(prev)); + if ep.is_null() { + Ok(()) + } else { + Err(imp::cleanup(ep)) + } + } + + extern fn try_fn<F: FnOnce()>(opt_closure: *mut c_void) { + 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 c_void), + data: *mut c_void) -> *mut c_void; + } +} + +/// 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_INIT; + 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 new file mode 100644 index 00000000000..a72c1debe14 --- /dev/null +++ b/src/libstd/rt/unwind/seh.rs @@ -0,0 +1,30 @@ +// 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. + +use prelude::v1::*; + +use any::Any; +use intrinsics; +use libc::c_void; + +pub unsafe fn panic(_data: Box<Any + Send + 'static>) -> ! { + intrinsics::abort(); +} + +pub unsafe fn cleanup(_ptr: *mut c_void) -> Box<Any + Send + 'static> { + intrinsics::abort(); +} + +#[lang = "eh_personality"] +#[no_mangle] +pub extern fn rust_eh_personality() {} + +#[no_mangle] +pub extern fn rust_eh_personality_catch() {} |
