276 lines
9.4 KiB
Rust
Raw Normal View History

// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![allow(private_no_mangle_fns)]
use prelude::v1::*;
use any::Any;
use sys_common::libunwind as uw;
struct Exception {
uwe: uw::_Unwind_Exception,
cause: Option<Box<Any + Send + 'static>>,
}
pub unsafe fn panic(data: Box<Any + Send + 'static>) -> ! {
let exception: Box<_> = box Exception {
uwe: uw::_Unwind_Exception {
exception_class: rust_exception_class(),
exception_cleanup: exception_cleanup,
private: [0; uw::unwinder_private_data_size],
},
cause: Some(data),
};
let exception_param = Box::into_raw(exception) as *mut uw::_Unwind_Exception;
let error = uw::_Unwind_RaiseException(exception_param);
rtabort!("Could not unwind stack, error = {}", error as isize);
extern fn exception_cleanup(_unwind_code: uw::_Unwind_Reason_Code,
exception: *mut uw::_Unwind_Exception) {
unsafe {
let _: Box<Exception> = Box::from_raw(exception as *mut Exception);
}
}
}
trans: Reimplement unwinding on MSVC This commit transitions the compiler to using the new exception handling instructions in LLVM for implementing unwinding for MSVC. This affects both 32 and 64-bit MSVC as they're both now using SEH-based strategies. In terms of standard library support, lots more details about how SEH unwinding is implemented can be found in the commits. In terms of trans, this change necessitated a few modifications: * Branches were added to detect when the old landingpad instruction is used or the new cleanuppad instruction is used to `trans::cleanup`. * The return value from `cleanuppad` is not stored in an `alloca` (because it cannot be). * Each block in trans now has an `Option<LandingPad>` instead of `is_lpad: bool` for indicating whether it's in a landing pad or not. The new exception handling intrinsics require that on MSVC each `call` inside of a landing pad is annotated with which landing pad that it's in. This change to the basic block means that whenever a `call` or `invoke` instruction is generated we know whether to annotate it as part of a cleanuppad or not. * Lots of modifications were made to the instruction builders to construct the new instructions as well as pass the tagging information for the call/invoke instructions. * The translation of the `try` intrinsics for MSVC has been overhauled to use the new `catchpad` instruction. The filter function is now also a rustc-generated function instead of a purely libstd-defined function. The libstd definition still exists, it just has a stable ABI across architectures and leaves some of the really weird implementation details to the compiler (e.g. the `localescape` and `localrecover` intrinsics).
2015-10-23 18:18:44 -07:00
pub fn payload() -> *mut u8 {
0 as *mut u8
}
pub unsafe fn cleanup(ptr: *mut u8) -> Box<Any + Send + 'static> {
let my_ep = ptr as *mut Exception;
let cause = (*my_ep).cause.take();
uw::_Unwind_DeleteException(ptr as *mut _);
cause.unwrap()
}
// Rust's exception class identifier. This is used by personality routines to
// determine whether the exception was thrown by their own runtime.
fn rust_exception_class() -> uw::_Unwind_Exception_Class {
// M O Z \0 R U S T -- vendor, language
0x4d4f5a_00_52555354
}
// We could implement our personality routine in pure Rust, however exception
// info decoding is tedious. More importantly, personality routines have to
// handle various platform quirks, which are not fun to maintain. For this
// reason, we attempt to reuse personality routine of the C language:
// __gcc_personality_v0.
//
// Since C does not support exception catching, __gcc_personality_v0 simply
// always returns _URC_CONTINUE_UNWIND in search phase, and always returns
// _URC_INSTALL_CONTEXT (i.e. "invoke cleanup code") in cleanup phase.
//
// This is pretty close to Rust's exception handling approach, except that Rust
// does have a single "catch-all" handler at the bottom of each thread's stack.
// So we have two versions of the personality routine:
// - rust_eh_personality, used by all cleanup landing pads, which never catches,
// so the behavior of __gcc_personality_v0 is perfectly adequate there, and
// - rust_eh_personality_catch, used only by rust_try(), which always catches.
//
// See also: rustc_trans::trans::intrinsic::trans_gnu_try
#[cfg(all(not(target_arch = "arm"),
not(all(windows, target_arch = "x86_64")),
not(test)))]
pub mod eabi {
use sys_common::libunwind as uw;
use libc::c_int;
extern {
fn __gcc_personality_v0(version: c_int,
actions: uw::_Unwind_Action,
exception_class: uw::_Unwind_Exception_Class,
ue_header: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context)
-> uw::_Unwind_Reason_Code;
}
#[lang = "eh_personality"]
#[no_mangle]
extern fn rust_eh_personality(
version: c_int,
actions: uw::_Unwind_Action,
exception_class: uw::_Unwind_Exception_Class,
ue_header: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context
) -> uw::_Unwind_Reason_Code
{
unsafe {
__gcc_personality_v0(version, actions, exception_class, ue_header,
context)
}
}
#[lang = "eh_personality_catch"]
#[no_mangle]
pub extern fn rust_eh_personality_catch(
version: c_int,
actions: uw::_Unwind_Action,
exception_class: uw::_Unwind_Exception_Class,
ue_header: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context
) -> uw::_Unwind_Reason_Code
{
if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase
uw::_URC_HANDLER_FOUND // catch!
}
else { // cleanup phase
unsafe {
__gcc_personality_v0(version, actions, exception_class, ue_header,
context)
}
}
}
}
// iOS on armv7 is using SjLj exceptions and therefore requires to use
// a specialized personality routine: __gcc_personality_sj0
#[cfg(all(target_os = "ios", target_arch = "arm", not(test)))]
pub mod eabi {
use sys_common::libunwind as uw;
use libc::c_int;
extern {
fn __gcc_personality_sj0(version: c_int,
actions: uw::_Unwind_Action,
exception_class: uw::_Unwind_Exception_Class,
ue_header: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context)
-> uw::_Unwind_Reason_Code;
}
#[lang = "eh_personality"]
#[no_mangle]
pub extern fn rust_eh_personality(
version: c_int,
actions: uw::_Unwind_Action,
exception_class: uw::_Unwind_Exception_Class,
ue_header: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context
) -> uw::_Unwind_Reason_Code
{
unsafe {
__gcc_personality_sj0(version, actions, exception_class, ue_header,
context)
}
}
#[lang = "eh_personality_catch"]
#[no_mangle]
pub extern fn rust_eh_personality_catch(
version: c_int,
actions: uw::_Unwind_Action,
exception_class: uw::_Unwind_Exception_Class,
ue_header: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context
) -> uw::_Unwind_Reason_Code
{
if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase
uw::_URC_HANDLER_FOUND // catch!
}
else { // cleanup phase
unsafe {
2015-08-02 21:15:01 -07:00
__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 sys_common::libunwind as uw;
use libc::c_int;
extern {
fn __gcc_personality_v0(state: uw::_Unwind_State,
ue_header: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context)
-> uw::_Unwind_Reason_Code;
}
#[lang = "eh_personality"]
#[no_mangle]
extern fn rust_eh_personality(
state: uw::_Unwind_State,
ue_header: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context
) -> uw::_Unwind_Reason_Code
{
unsafe {
__gcc_personality_v0(state, ue_header, context)
}
}
#[lang = "eh_personality_catch"]
#[no_mangle]
pub extern fn rust_eh_personality_catch(
state: uw::_Unwind_State,
ue_header: *mut uw::_Unwind_Exception,
context: *mut uw::_Unwind_Context
) -> uw::_Unwind_Reason_Code
{
if (state as c_int & uw::_US_ACTION_MASK as c_int)
== uw::_US_VIRTUAL_UNWIND_FRAME as c_int { // search phase
uw::_URC_HANDLER_FOUND // catch!
}
else { // cleanup phase
unsafe {
__gcc_personality_v0(state, ue_header, context)
}
}
}
}
2015-10-21 09:59:24 -07:00
// See docs in the `unwind` module.
#[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu", not(test)))]
#[lang = "eh_unwind_resume"]
#[unwind]
unsafe extern fn rust_eh_unwind_resume(panic_ctx: *mut u8) -> ! {
uw::_Unwind_Resume(panic_ctx as *mut uw::_Unwind_Exception);
}
#[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))]
pub mod eh_frame_registry {
2015-10-21 09:59:24 -07:00
// The implementation of stack unwinding is (for now) deferred to libgcc_eh, however Rust
// crates use these Rust-specific entry points to avoid potential clashes with GCC runtime.
// See also: rtbegin.rs, `unwind` module.
#[link(name = "gcc_eh")]
#[cfg(not(cargobuild))]
extern {}
extern {
fn __register_frame_info(eh_frame_begin: *const u8, object: *mut u8);
fn __deregister_frame_info(eh_frame_begin: *const u8, object: *mut u8);
}
#[cfg(not(test))]
#[no_mangle]
2015-11-18 21:16:20 +03:00
#[unstable(feature = "libstd_sys_internals", issue = "0")]
pub unsafe extern fn rust_eh_register_frames(eh_frame_begin: *const u8,
object: *mut u8) {
__register_frame_info(eh_frame_begin, object);
}
#[cfg(not(test))]
#[no_mangle]
2015-11-18 21:16:20 +03:00
#[unstable(feature = "libstd_sys_internals", issue = "0")]
pub unsafe extern fn rust_eh_unregister_frames(eh_frame_begin: *const u8,
object: *mut u8) {
__deregister_frame_info(eh_frame_begin, object);
}
}