2013-12-15 19:17:07 -06:00
|
|
|
// 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.
|
|
|
|
|
2014-04-27 21:05:41 -05:00
|
|
|
//! Stack unwinding
|
|
|
|
|
2013-12-15 19:17:07 -06:00
|
|
|
// Implementation of Rust stack unwinding
|
|
|
|
//
|
2013-12-18 11:57:58 -06:00
|
|
|
// For background on exception handling and stack unwinding please see
|
|
|
|
// "Exception Handling in LLVM" (llvm.org/docs/ExceptionHandling.html) and
|
|
|
|
// documents linked from it.
|
2013-12-15 19:17:07 -06:00
|
|
|
// 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.
|
|
|
|
//
|
2013-12-18 11:57:58 -06:00
|
|
|
// 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).
|
2013-12-15 19:17:07 -06:00
|
|
|
//
|
2013-12-18 11:57:58 -06:00
|
|
|
// For each stack frame, it invokes the associated "personality routine", whose
|
|
|
|
// address is also stored in the unwind info section.
|
2013-12-15 19:17:07 -06:00
|
|
|
//
|
2013-12-18 11:57:58 -06:00
|
|
|
// 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.
|
2013-12-15 19:17:07 -06:00
|
|
|
//
|
2013-12-18 11:57:58 -06:00
|
|
|
// 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.
|
2013-12-15 19:17:07 -06:00
|
|
|
//
|
|
|
|
// ~~~ Frame unwind info registration ~~~
|
2013-12-18 11:57:58 -06:00
|
|
|
// 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.
|
2013-12-15 19:17:07 -06:00
|
|
|
//
|
2013-12-18 11:57:58 -06:00
|
|
|
// 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.
|
2013-12-15 19:17:07 -06:00
|
|
|
//
|
2013-12-18 11:57:58 -06:00
|
|
|
// 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.
|
2013-12-15 19:17:07 -06:00
|
|
|
//
|
|
|
|
// Currently Rust uses unwind runtime provided by libgcc.
|
|
|
|
|
2013-12-18 11:57:58 -06:00
|
|
|
use any::{Any, AnyRefExt};
|
2014-01-27 05:37:55 -06:00
|
|
|
use fmt;
|
core: Remove the cast module
This commit revisits the `cast` module in libcore and libstd, and scrutinizes
all functions inside of it. The result was to remove the `cast` module entirely,
folding all functionality into the `mem` module. Specifically, this is the fate
of each function in the `cast` module.
* transmute - This function was moved to `mem`, but it is now marked as
#[unstable]. This is due to planned changes to the `transmute`
function and how it can be invoked (see the #[unstable] comment).
For more information, see RFC 5 and #12898
* transmute_copy - This function was moved to `mem`, with clarification that is
is not an error to invoke it with T/U that are different
sizes, but rather that it is strongly discouraged. This
function is now #[stable]
* forget - This function was moved to `mem` and marked #[stable]
* bump_box_refcount - This function was removed due to the deprecation of
managed boxes as well as its questionable utility.
* transmute_mut - This function was previously deprecated, and removed as part
of this commit.
* transmute_mut_unsafe - This function doesn't serve much of a purpose when it
can be achieved with an `as` in safe code, so it was
removed.
* transmute_lifetime - This function was removed because it is likely a strong
indication that code is incorrect in the first place.
* transmute_mut_lifetime - This function was removed for the same reasons as
`transmute_lifetime`
* copy_lifetime - This function was moved to `mem`, but it is marked
`#[unstable]` now due to the likelihood of being removed in
the future if it is found to not be very useful.
* copy_mut_lifetime - This function was also moved to `mem`, but had the same
treatment as `copy_lifetime`.
* copy_lifetime_vec - This function was removed because it is not used today,
and its existence is not necessary with DST
(copy_lifetime will suffice).
In summary, the cast module was stripped down to these functions, and then the
functions were moved to the `mem` module.
transmute - #[unstable]
transmute_copy - #[stable]
forget - #[stable]
copy_lifetime - #[unstable]
copy_mut_lifetime - #[unstable]
[breaking-change]
2014-05-09 12:34:51 -05:00
|
|
|
use intrinsics;
|
2013-12-18 11:57:58 -06:00
|
|
|
use kinds::Send;
|
2014-01-31 14:35:36 -06:00
|
|
|
use mem;
|
2013-12-18 11:57:58 -06:00
|
|
|
use option::{Some, None, Option};
|
2014-05-05 20:56:44 -05:00
|
|
|
use owned::Box;
|
2014-01-06 12:26:11 -06:00
|
|
|
use prelude::drop;
|
2014-01-06 18:48:51 -06:00
|
|
|
use ptr::RawPtr;
|
2013-12-18 11:57:58 -06:00
|
|
|
use result::{Err, Ok};
|
2014-02-05 17:19:40 -06:00
|
|
|
use rt::backtrace;
|
2013-12-18 11:57:58 -06:00
|
|
|
use rt::local::Local;
|
|
|
|
use rt::task::Task;
|
|
|
|
use str::Str;
|
2014-05-22 18:57:53 -05:00
|
|
|
use string::String;
|
2013-12-18 11:57:58 -06:00
|
|
|
use task::TaskResult;
|
|
|
|
|
2014-02-05 17:19:40 -06:00
|
|
|
use uw = rt::libunwind;
|
2013-12-15 19:17:07 -06:00
|
|
|
|
|
|
|
pub struct Unwinder {
|
2014-03-27 17:09:47 -05:00
|
|
|
unwinding: bool,
|
2014-05-05 20:56:44 -05:00
|
|
|
cause: Option<Box<Any:Send>>
|
2013-12-15 19:17:07 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Unwinder {
|
2013-12-18 11:57:58 -06:00
|
|
|
pub fn new() -> Unwinder {
|
|
|
|
Unwinder {
|
|
|
|
unwinding: false,
|
|
|
|
cause: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn unwinding(&self) -> bool {
|
|
|
|
self.unwinding
|
|
|
|
}
|
2013-12-15 19:17:07 -06:00
|
|
|
|
|
|
|
pub fn try(&mut self, f: ||) {
|
2014-02-16 02:04:33 -06:00
|
|
|
use raw::Closure;
|
2013-12-12 15:27:26 -06:00
|
|
|
use libc::{c_void};
|
2013-12-15 19:17:07 -06:00
|
|
|
|
|
|
|
unsafe {
|
core: Remove the cast module
This commit revisits the `cast` module in libcore and libstd, and scrutinizes
all functions inside of it. The result was to remove the `cast` module entirely,
folding all functionality into the `mem` module. Specifically, this is the fate
of each function in the `cast` module.
* transmute - This function was moved to `mem`, but it is now marked as
#[unstable]. This is due to planned changes to the `transmute`
function and how it can be invoked (see the #[unstable] comment).
For more information, see RFC 5 and #12898
* transmute_copy - This function was moved to `mem`, with clarification that is
is not an error to invoke it with T/U that are different
sizes, but rather that it is strongly discouraged. This
function is now #[stable]
* forget - This function was moved to `mem` and marked #[stable]
* bump_box_refcount - This function was removed due to the deprecation of
managed boxes as well as its questionable utility.
* transmute_mut - This function was previously deprecated, and removed as part
of this commit.
* transmute_mut_unsafe - This function doesn't serve much of a purpose when it
can be achieved with an `as` in safe code, so it was
removed.
* transmute_lifetime - This function was removed because it is likely a strong
indication that code is incorrect in the first place.
* transmute_mut_lifetime - This function was removed for the same reasons as
`transmute_lifetime`
* copy_lifetime - This function was moved to `mem`, but it is marked
`#[unstable]` now due to the likelihood of being removed in
the future if it is found to not be very useful.
* copy_mut_lifetime - This function was also moved to `mem`, but had the same
treatment as `copy_lifetime`.
* copy_lifetime_vec - This function was removed because it is not used today,
and its existence is not necessary with DST
(copy_lifetime will suffice).
In summary, the cast module was stripped down to these functions, and then the
functions were moved to the `mem` module.
transmute - #[unstable]
transmute_copy - #[stable]
forget - #[stable]
copy_lifetime - #[unstable]
copy_mut_lifetime - #[unstable]
[breaking-change]
2014-05-09 12:34:51 -05:00
|
|
|
let closure: Closure = mem::transmute(f);
|
2013-12-18 11:57:58 -06:00
|
|
|
let ep = rust_try(try_fn, closure.code as *c_void,
|
|
|
|
closure.env as *c_void);
|
2013-12-15 19:17:07 -06:00
|
|
|
if !ep.is_null() {
|
2014-01-04 01:34:15 -06:00
|
|
|
rtdebug!("caught {}", (*ep).exception_class);
|
2013-12-18 11:57:58 -06:00
|
|
|
uw::_Unwind_DeleteException(ep);
|
2013-12-15 19:17:07 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
extern fn try_fn(code: *c_void, env: *c_void) {
|
|
|
|
unsafe {
|
core: Remove the cast module
This commit revisits the `cast` module in libcore and libstd, and scrutinizes
all functions inside of it. The result was to remove the `cast` module entirely,
folding all functionality into the `mem` module. Specifically, this is the fate
of each function in the `cast` module.
* transmute - This function was moved to `mem`, but it is now marked as
#[unstable]. This is due to planned changes to the `transmute`
function and how it can be invoked (see the #[unstable] comment).
For more information, see RFC 5 and #12898
* transmute_copy - This function was moved to `mem`, with clarification that is
is not an error to invoke it with T/U that are different
sizes, but rather that it is strongly discouraged. This
function is now #[stable]
* forget - This function was moved to `mem` and marked #[stable]
* bump_box_refcount - This function was removed due to the deprecation of
managed boxes as well as its questionable utility.
* transmute_mut - This function was previously deprecated, and removed as part
of this commit.
* transmute_mut_unsafe - This function doesn't serve much of a purpose when it
can be achieved with an `as` in safe code, so it was
removed.
* transmute_lifetime - This function was removed because it is likely a strong
indication that code is incorrect in the first place.
* transmute_mut_lifetime - This function was removed for the same reasons as
`transmute_lifetime`
* copy_lifetime - This function was moved to `mem`, but it is marked
`#[unstable]` now due to the likelihood of being removed in
the future if it is found to not be very useful.
* copy_mut_lifetime - This function was also moved to `mem`, but had the same
treatment as `copy_lifetime`.
* copy_lifetime_vec - This function was removed because it is not used today,
and its existence is not necessary with DST
(copy_lifetime will suffice).
In summary, the cast module was stripped down to these functions, and then the
functions were moved to the `mem` module.
transmute - #[unstable]
transmute_copy - #[stable]
forget - #[stable]
copy_lifetime - #[unstable]
copy_mut_lifetime - #[unstable]
[breaking-change]
2014-05-09 12:34:51 -05:00
|
|
|
let closure: || = mem::transmute(Closure {
|
2013-12-18 11:57:58 -06:00
|
|
|
code: code as *(),
|
|
|
|
env: env as *(),
|
|
|
|
});
|
2013-12-15 19:17:07 -06:00
|
|
|
closure();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
extern {
|
|
|
|
// Rust's try-catch
|
|
|
|
// When f(...) returns normally, the return value is null.
|
2013-12-18 11:57:58 -06:00
|
|
|
// When f(...) throws, the return value is a pointer to the caught
|
|
|
|
// exception object.
|
2013-12-15 19:17:07 -06:00
|
|
|
fn rust_try(f: extern "C" fn(*c_void, *c_void),
|
|
|
|
code: *c_void,
|
2013-12-18 11:57:58 -06:00
|
|
|
data: *c_void) -> *uw::_Unwind_Exception;
|
2013-12-15 19:17:07 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-05 20:56:44 -05:00
|
|
|
pub fn begin_unwind(&mut self, cause: Box<Any:Send>) -> ! {
|
2013-12-15 19:17:07 -06:00
|
|
|
rtdebug!("begin_unwind()");
|
|
|
|
|
|
|
|
self.unwinding = true;
|
|
|
|
self.cause = Some(cause);
|
|
|
|
|
2013-12-30 20:43:03 -06:00
|
|
|
rust_fail();
|
2013-12-15 19:17:07 -06:00
|
|
|
|
2013-12-30 20:43:03 -06:00
|
|
|
// An uninlined, unmangled function upon which to slap yer breakpoints
|
|
|
|
#[inline(never)]
|
|
|
|
#[no_mangle]
|
|
|
|
fn rust_fail() -> ! {
|
2013-12-15 19:17:07 -06:00
|
|
|
unsafe {
|
2014-04-25 03:08:02 -05:00
|
|
|
let exception = box uw::_Unwind_Exception {
|
2013-12-30 20:43:03 -06:00
|
|
|
exception_class: rust_exception_class(),
|
|
|
|
exception_cleanup: exception_cleanup,
|
2014-01-04 18:50:57 -06:00
|
|
|
private: [0, ..uw::unwinder_private_data_size],
|
2013-12-30 20:43:03 -06:00
|
|
|
};
|
core: Remove the cast module
This commit revisits the `cast` module in libcore and libstd, and scrutinizes
all functions inside of it. The result was to remove the `cast` module entirely,
folding all functionality into the `mem` module. Specifically, this is the fate
of each function in the `cast` module.
* transmute - This function was moved to `mem`, but it is now marked as
#[unstable]. This is due to planned changes to the `transmute`
function and how it can be invoked (see the #[unstable] comment).
For more information, see RFC 5 and #12898
* transmute_copy - This function was moved to `mem`, with clarification that is
is not an error to invoke it with T/U that are different
sizes, but rather that it is strongly discouraged. This
function is now #[stable]
* forget - This function was moved to `mem` and marked #[stable]
* bump_box_refcount - This function was removed due to the deprecation of
managed boxes as well as its questionable utility.
* transmute_mut - This function was previously deprecated, and removed as part
of this commit.
* transmute_mut_unsafe - This function doesn't serve much of a purpose when it
can be achieved with an `as` in safe code, so it was
removed.
* transmute_lifetime - This function was removed because it is likely a strong
indication that code is incorrect in the first place.
* transmute_mut_lifetime - This function was removed for the same reasons as
`transmute_lifetime`
* copy_lifetime - This function was moved to `mem`, but it is marked
`#[unstable]` now due to the likelihood of being removed in
the future if it is found to not be very useful.
* copy_mut_lifetime - This function was also moved to `mem`, but had the same
treatment as `copy_lifetime`.
* copy_lifetime_vec - This function was removed because it is not used today,
and its existence is not necessary with DST
(copy_lifetime will suffice).
In summary, the cast module was stripped down to these functions, and then the
functions were moved to the `mem` module.
transmute - #[unstable]
transmute_copy - #[stable]
forget - #[stable]
copy_lifetime - #[unstable]
copy_mut_lifetime - #[unstable]
[breaking-change]
2014-05-09 12:34:51 -05:00
|
|
|
let error = uw::_Unwind_RaiseException(mem::transmute(exception));
|
2013-12-30 20:43:03 -06:00
|
|
|
rtabort!("Could not unwind stack, error = {}", error as int)
|
|
|
|
}
|
|
|
|
|
|
|
|
extern "C" fn exception_cleanup(_unwind_code: uw::_Unwind_Reason_Code,
|
|
|
|
exception: *uw::_Unwind_Exception) {
|
|
|
|
rtdebug!("exception_cleanup()");
|
|
|
|
unsafe {
|
2014-05-05 20:56:44 -05:00
|
|
|
let _: Box<uw::_Unwind_Exception> =
|
core: Remove the cast module
This commit revisits the `cast` module in libcore and libstd, and scrutinizes
all functions inside of it. The result was to remove the `cast` module entirely,
folding all functionality into the `mem` module. Specifically, this is the fate
of each function in the `cast` module.
* transmute - This function was moved to `mem`, but it is now marked as
#[unstable]. This is due to planned changes to the `transmute`
function and how it can be invoked (see the #[unstable] comment).
For more information, see RFC 5 and #12898
* transmute_copy - This function was moved to `mem`, with clarification that is
is not an error to invoke it with T/U that are different
sizes, but rather that it is strongly discouraged. This
function is now #[stable]
* forget - This function was moved to `mem` and marked #[stable]
* bump_box_refcount - This function was removed due to the deprecation of
managed boxes as well as its questionable utility.
* transmute_mut - This function was previously deprecated, and removed as part
of this commit.
* transmute_mut_unsafe - This function doesn't serve much of a purpose when it
can be achieved with an `as` in safe code, so it was
removed.
* transmute_lifetime - This function was removed because it is likely a strong
indication that code is incorrect in the first place.
* transmute_mut_lifetime - This function was removed for the same reasons as
`transmute_lifetime`
* copy_lifetime - This function was moved to `mem`, but it is marked
`#[unstable]` now due to the likelihood of being removed in
the future if it is found to not be very useful.
* copy_mut_lifetime - This function was also moved to `mem`, but had the same
treatment as `copy_lifetime`.
* copy_lifetime_vec - This function was removed because it is not used today,
and its existence is not necessary with DST
(copy_lifetime will suffice).
In summary, the cast module was stripped down to these functions, and then the
functions were moved to the `mem` module.
transmute - #[unstable]
transmute_copy - #[stable]
forget - #[stable]
copy_lifetime - #[unstable]
copy_mut_lifetime - #[unstable]
[breaking-change]
2014-05-09 12:34:51 -05:00
|
|
|
mem::transmute(exception);
|
2013-12-30 20:43:03 -06:00
|
|
|
}
|
2013-12-15 19:17:07 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn result(&mut self) -> TaskResult {
|
|
|
|
if self.unwinding {
|
|
|
|
Err(self.cause.take().unwrap())
|
|
|
|
} else {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Rust's exception class identifier. This is used by personality routines to
|
|
|
|
// determine whether the exception was thrown by their own runtime.
|
2013-12-18 11:57:58 -06:00
|
|
|
fn rust_exception_class() -> uw::_Unwind_Exception_Class {
|
|
|
|
// M O Z \0 R U S T -- vendor, language
|
|
|
|
0x4d4f5a_00_52555354
|
2013-12-15 19:17:07 -06:00
|
|
|
}
|
|
|
|
|
2013-12-18 11:57:58 -06:00
|
|
|
// 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.
|
2013-12-15 19:17:07 -06:00
|
|
|
//
|
2013-12-18 11:57:58 -06:00
|
|
|
// 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.
|
2013-12-15 19:17:07 -06:00
|
|
|
//
|
2013-12-18 11:57:58 -06:00
|
|
|
// 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 task's stack.
|
2013-12-15 19:17:07 -06:00
|
|
|
// So we have two versions:
|
2013-12-18 11:57:58 -06:00
|
|
|
// - 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.
|
|
|
|
// This is achieved by overriding the return value in search phase to always
|
|
|
|
// say "catch!".
|
2013-12-15 19:17:07 -06:00
|
|
|
|
2014-01-07 00:33:37 -06:00
|
|
|
#[cfg(not(target_arch = "arm"), not(test))]
|
|
|
|
#[doc(hidden)]
|
2014-02-27 01:48:21 -06:00
|
|
|
#[allow(visible_private_types)]
|
2014-01-04 01:34:15 -06:00
|
|
|
pub mod eabi {
|
2014-02-05 17:19:40 -06:00
|
|
|
use uw = rt::libunwind;
|
2014-01-04 01:34:15 -06:00
|
|
|
use libc::c_int;
|
2013-12-15 19:17:07 -06:00
|
|
|
|
2014-01-04 01:34:15 -06:00
|
|
|
extern "C" {
|
|
|
|
fn __gcc_personality_v0(version: c_int,
|
|
|
|
actions: uw::_Unwind_Action,
|
|
|
|
exception_class: uw::_Unwind_Exception_Class,
|
|
|
|
ue_header: *uw::_Unwind_Exception,
|
|
|
|
context: *uw::_Unwind_Context)
|
|
|
|
-> uw::_Unwind_Reason_Code;
|
|
|
|
}
|
|
|
|
|
2014-05-19 11:30:09 -05:00
|
|
|
#[lang="eh_personality"]
|
|
|
|
extern fn eh_personality(
|
|
|
|
version: c_int,
|
|
|
|
actions: uw::_Unwind_Action,
|
|
|
|
exception_class: uw::_Unwind_Exception_Class,
|
|
|
|
ue_header: *uw::_Unwind_Exception,
|
|
|
|
context: *uw::_Unwind_Context
|
|
|
|
) -> uw::_Unwind_Reason_Code
|
|
|
|
{
|
|
|
|
unsafe {
|
|
|
|
__gcc_personality_v0(version, actions, exception_class, ue_header,
|
|
|
|
context)
|
|
|
|
}
|
|
|
|
}
|
2014-01-04 01:34:15 -06:00
|
|
|
|
|
|
|
#[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: *uw::_Unwind_Exception,
|
|
|
|
context: *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)
|
|
|
|
}
|
|
|
|
}
|
2013-12-15 19:17:07 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-04 01:34:15 -06:00
|
|
|
// ARM EHABI uses a slightly different personality routine signature,
|
|
|
|
// but otherwise works the same.
|
2014-01-07 00:33:37 -06:00
|
|
|
#[cfg(target_arch = "arm", not(test))]
|
2014-02-27 01:48:21 -06:00
|
|
|
#[allow(visible_private_types)]
|
2014-01-04 01:34:15 -06:00
|
|
|
pub mod eabi {
|
2014-02-05 17:19:40 -06:00
|
|
|
use uw = rt::libunwind;
|
2014-01-04 01:34:15 -06:00
|
|
|
use libc::c_int;
|
|
|
|
|
|
|
|
extern "C" {
|
|
|
|
fn __gcc_personality_v0(state: uw::_Unwind_State,
|
|
|
|
ue_header: *uw::_Unwind_Exception,
|
|
|
|
context: *uw::_Unwind_Context)
|
|
|
|
-> uw::_Unwind_Reason_Code;
|
2013-12-15 19:17:07 -06:00
|
|
|
}
|
2014-01-04 01:34:15 -06:00
|
|
|
|
2014-05-19 11:30:09 -05:00
|
|
|
#[lang="eh_personality"]
|
|
|
|
extern "C" fn eh_personality(
|
|
|
|
state: uw::_Unwind_State,
|
|
|
|
ue_header: *uw::_Unwind_Exception,
|
|
|
|
context: *uw::_Unwind_Context
|
|
|
|
) -> uw::_Unwind_Reason_Code
|
|
|
|
{
|
|
|
|
unsafe {
|
|
|
|
__gcc_personality_v0(state, ue_header, context)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-04 01:34:15 -06:00
|
|
|
#[no_mangle] // referenced from rust_try.ll
|
|
|
|
pub extern "C" fn rust_eh_personality_catch(
|
|
|
|
state: uw::_Unwind_State,
|
|
|
|
ue_header: *uw::_Unwind_Exception,
|
|
|
|
context: *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)
|
|
|
|
}
|
2013-12-15 19:17:07 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-12-12 20:01:59 -06:00
|
|
|
|
2014-05-01 12:47:18 -05:00
|
|
|
// Entry point of failure from the libcore crate
|
2014-05-29 19:32:50 -05:00
|
|
|
#[cfg(not(test))]
|
2014-05-19 11:30:09 -05:00
|
|
|
#[lang = "begin_unwind"]
|
|
|
|
pub extern fn rust_begin_unwind(msg: &fmt::Arguments,
|
|
|
|
file: &'static str, line: uint) -> ! {
|
|
|
|
begin_unwind_fmt(msg, file, line)
|
|
|
|
}
|
|
|
|
|
2014-01-27 05:37:55 -06:00
|
|
|
/// The entry point for unwinding with a formatted message.
|
|
|
|
///
|
|
|
|
/// This is designed to reduce the amount of code required at the call
|
2014-02-17 05:53:45 -06:00
|
|
|
/// site as much as possible (so that `fail!()` has as low an impact
|
2014-01-27 05:37:55 -06:00
|
|
|
/// on (e.g.) the inlining of other functions as possible), by moving
|
|
|
|
/// the actual formatting into this shared place.
|
|
|
|
#[inline(never)] #[cold]
|
2014-05-19 11:30:09 -05:00
|
|
|
pub fn begin_unwind_fmt(msg: &fmt::Arguments, file: &'static str,
|
|
|
|
line: uint) -> ! {
|
2014-01-27 19:19:17 -06:00
|
|
|
// We do two allocations here, unfortunately. But (a) they're
|
|
|
|
// required with the current scheme, and (b) we don't handle
|
|
|
|
// failure + OOM properly anyway (see comment in begin_unwind
|
|
|
|
// below).
|
2014-05-27 22:44:58 -05:00
|
|
|
begin_unwind_inner(box fmt::format(msg), file, line)
|
2014-01-27 05:37:55 -06:00
|
|
|
}
|
|
|
|
|
2013-12-12 20:01:59 -06:00
|
|
|
/// This is the entry point of unwinding for fail!() and assert!().
|
2014-01-27 01:03:37 -06:00
|
|
|
#[inline(never)] #[cold] // avoid code bloat at the call sites as much as possible
|
2013-12-12 20:01:59 -06:00
|
|
|
pub fn begin_unwind<M: Any + Send>(msg: M, file: &'static str, line: uint) -> ! {
|
2014-01-27 01:03:37 -06:00
|
|
|
// Note that this should be the only allocation performed in this code path.
|
2014-01-06 12:26:11 -06:00
|
|
|
// Currently this means that fail!() on OOM will invoke this code path,
|
|
|
|
// but then again we're not really ready for failing on OOM anyway. If
|
|
|
|
// we do start doing this, then we should propagate this allocation to
|
|
|
|
// be performed in the parent of this task instead of the task that's
|
|
|
|
// failing.
|
|
|
|
|
2014-01-27 01:03:37 -06:00
|
|
|
// see below for why we do the `Any` coercion here.
|
2014-04-25 03:08:02 -05:00
|
|
|
begin_unwind_inner(box msg, file, line)
|
2014-01-27 01:03:37 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// The core of the unwinding.
|
|
|
|
///
|
|
|
|
/// This is non-generic to avoid instantiation bloat in other crates
|
|
|
|
/// (which makes compilation of small crates noticably slower). (Note:
|
|
|
|
/// we need the `Any` object anyway, we're not just creating it to
|
|
|
|
/// avoid being generic.)
|
|
|
|
///
|
|
|
|
/// Do this split took the LLVM IR line counts of `fn main() { fail!()
|
|
|
|
/// }` from ~1900/3700 (-O/no opts) to 180/590.
|
|
|
|
#[inline(never)] #[cold] // this is the slow path, please never inline this
|
2014-05-05 20:56:44 -05:00
|
|
|
fn begin_unwind_inner(msg: Box<Any:Send>,
|
|
|
|
file: &'static str,
|
|
|
|
line: uint) -> ! {
|
2014-01-06 12:26:11 -06:00
|
|
|
let mut task;
|
|
|
|
{
|
|
|
|
let msg_s = match msg.as_ref::<&'static str>() {
|
|
|
|
Some(s) => *s,
|
2014-05-22 18:57:53 -05:00
|
|
|
None => match msg.as_ref::<String>() {
|
2014-01-06 12:26:11 -06:00
|
|
|
Some(s) => s.as_slice(),
|
2014-05-05 20:56:44 -05:00
|
|
|
None => "Box<Any>",
|
2014-01-06 12:26:11 -06:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// It is assumed that all reasonable rust code will have a local task at
|
|
|
|
// all times. This means that this `try_take` will succeed almost all of
|
|
|
|
// the time. There are border cases, however, when the runtime has
|
|
|
|
// *almost* set up the local task, but hasn't quite gotten there yet. In
|
|
|
|
// order to get some better diagnostics, we print on failure and
|
|
|
|
// immediately abort the whole process if there is no local task
|
|
|
|
// available.
|
2014-05-05 20:56:44 -05:00
|
|
|
let opt_task: Option<Box<Task>> = Local::try_take();
|
2014-01-06 12:26:11 -06:00
|
|
|
task = match opt_task {
|
|
|
|
Some(t) => t,
|
|
|
|
None => {
|
|
|
|
rterrln!("failed at '{}', {}:{}", msg_s, file, line);
|
2014-02-05 17:19:40 -06:00
|
|
|
if backtrace::log_enabled() {
|
|
|
|
let mut err = ::rt::util::Stderr;
|
|
|
|
let _err = backtrace::write(&mut err);
|
2014-03-13 01:34:31 -05:00
|
|
|
} else {
|
|
|
|
rterrln!("run with `RUST_BACKTRACE=1` to see a backtrace");
|
2014-02-05 17:19:40 -06:00
|
|
|
}
|
2014-01-06 12:26:11 -06:00
|
|
|
unsafe { intrinsics::abort() }
|
|
|
|
}
|
|
|
|
};
|
2013-12-12 20:01:59 -06:00
|
|
|
|
2014-01-06 12:26:11 -06:00
|
|
|
// See comments in io::stdio::with_task_stdout as to why we have to be
|
|
|
|
// careful when using an arbitrary I/O handle from the task. We
|
|
|
|
// essentially need to dance to make sure when a task is in TLS when
|
|
|
|
// running user code.
|
|
|
|
let name = task.name.take();
|
2013-12-12 20:01:59 -06:00
|
|
|
{
|
2014-01-06 12:26:11 -06:00
|
|
|
let n = name.as_ref().map(|n| n.as_slice()).unwrap_or("<unnamed>");
|
|
|
|
|
|
|
|
match task.stderr.take() {
|
|
|
|
Some(mut stderr) => {
|
|
|
|
Local::put(task);
|
2014-01-29 18:33:57 -06:00
|
|
|
// FIXME: what to do when the task printing fails?
|
2014-05-10 16:05:06 -05:00
|
|
|
let _err = write!(stderr,
|
|
|
|
"task '{}' failed at '{}', {}:{}\n",
|
|
|
|
n, msg_s, file, line);
|
2014-02-05 17:19:40 -06:00
|
|
|
if backtrace::log_enabled() {
|
|
|
|
let _err = backtrace::write(stderr);
|
|
|
|
}
|
2014-01-06 12:26:11 -06:00
|
|
|
task = Local::take();
|
|
|
|
|
2014-01-31 14:35:36 -06:00
|
|
|
match mem::replace(&mut task.stderr, Some(stderr)) {
|
2014-01-06 12:26:11 -06:00
|
|
|
Some(prev) => {
|
|
|
|
Local::put(task);
|
|
|
|
drop(prev);
|
|
|
|
task = Local::take();
|
|
|
|
}
|
|
|
|
None => {}
|
|
|
|
}
|
2013-12-12 20:01:59 -06:00
|
|
|
}
|
2014-01-06 12:26:11 -06:00
|
|
|
None => {
|
2013-12-18 11:57:58 -06:00
|
|
|
rterrln!("task '{}' failed at '{}', {}:{}", n, msg_s,
|
2013-12-12 20:01:59 -06:00
|
|
|
file, line);
|
2014-02-05 17:19:40 -06:00
|
|
|
if backtrace::log_enabled() {
|
|
|
|
let mut err = ::rt::util::Stderr;
|
|
|
|
let _err = backtrace::write(&mut err);
|
|
|
|
}
|
2013-12-12 20:01:59 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-01-06 12:26:11 -06:00
|
|
|
task.name = name;
|
|
|
|
|
|
|
|
if task.unwinder.unwinding {
|
|
|
|
// If a task fails 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 task cleanly.
|
|
|
|
rterrln!("task failed during unwinding (double-failure - total drag!)")
|
|
|
|
rterrln!("rust must abort now. so sorry.");
|
2014-02-05 17:19:40 -06:00
|
|
|
|
|
|
|
// Don't print the backtrace twice (it would have already been
|
|
|
|
// printed if logging was enabled).
|
|
|
|
if !backtrace::log_enabled() {
|
|
|
|
let mut err = ::rt::util::Stderr;
|
|
|
|
let _err = backtrace::write(&mut err);
|
|
|
|
}
|
2014-01-06 12:26:11 -06:00
|
|
|
unsafe { intrinsics::abort() }
|
|
|
|
}
|
|
|
|
}
|
2013-12-12 20:01:59 -06:00
|
|
|
|
2014-01-06 12:26:11 -06:00
|
|
|
// The unwinder won't actually use the task at all, so we put the task back
|
|
|
|
// into TLS right before we invoke the unwinder, but this means we need an
|
|
|
|
// unsafe reference back to the unwinder once it's in TLS.
|
|
|
|
Local::put(task);
|
|
|
|
unsafe {
|
|
|
|
let task: *mut Task = Local::unsafe_borrow();
|
2013-12-12 20:01:59 -06:00
|
|
|
(*task).unwinder.begin_unwind(msg);
|
|
|
|
}
|
|
|
|
}
|