From 6cef93d400b183d70c6c37856b46bb36d7bd8e16 Mon Sep 17 00:00:00 2001 From: Vadim Chugunov Date: Sat, 23 Jul 2016 17:25:25 -0700 Subject: [PATCH] Implement ARM personality routine in Rust. Remove the `eh_personality_catch` lang item. Use a simplified version of `cfg_if!` in libunwind. --- src/libpanic_unwind/gcc.rs | 271 +++++++++++++++------------ src/libpanic_unwind/seh64_gnu.rs | 1 + src/librustc/middle/lang_items.rs | 1 - src/librustc_trans/intrinsic.rs | 19 +- src/libunwind/libunwind.rs | 292 ++++++++++++++++-------------- 5 files changed, 314 insertions(+), 270 deletions(-) diff --git a/src/libpanic_unwind/gcc.rs b/src/libpanic_unwind/gcc.rs index cdf772ad3b825..fdae8f69a9c0e 100644 --- a/src/libpanic_unwind/gcc.rs +++ b/src/libpanic_unwind/gcc.rs @@ -61,6 +61,8 @@ use core::ptr; use alloc::boxed::Box; use unwind as uw; +use libc::{c_int, uintptr_t}; +use dwarf::eh::{self, EHContext, EHAction}; #[repr(C)] struct Exception { @@ -106,139 +108,184 @@ fn rust_exception_class() -> uw::_Unwind_Exception_Class { 0x4d4f5a_00_52555354 } -// All targets, except ARM which uses a slightly different ABI (however, iOS goes here as it uses -// SjLj unwinding). Also, 64-bit Windows implementation lives in seh64_gnu.rs -#[cfg(all(any(target_os = "ios", not(target_arch = "arm"))))] -pub mod eabi { - use unwind as uw; - use libc::{c_int, uintptr_t}; - use dwarf::eh::{EHContext, EHAction, find_eh_action}; - // Register ids were lifted from LLVM's TargetLowering::getExceptionPointerRegister() - // and TargetLowering::getExceptionSelectorRegister() for each architecture, - // then mapped to DWARF register numbers via register definition tables - // (typically RegisterInfo.td, search for "DwarfRegNum"). - // See also http://llvm.org/docs/WritingAnLLVMBackend.html#defining-a-register. +// Register ids were lifted from LLVM's TargetLowering::getExceptionPointerRegister() +// and TargetLowering::getExceptionSelectorRegister() for each architecture, +// then mapped to DWARF register numbers via register definition tables +// (typically RegisterInfo.td, search for "DwarfRegNum"). +// See also http://llvm.org/docs/WritingAnLLVMBackend.html#defining-a-register. - #[cfg(target_arch = "x86")] - const UNWIND_DATA_REG: (i32, i32) = (0, 2); // EAX, EDX +#[cfg(target_arch = "x86")] +const UNWIND_DATA_REG: (i32, i32) = (0, 2); // EAX, EDX - #[cfg(target_arch = "x86_64")] - const UNWIND_DATA_REG: (i32, i32) = (0, 1); // RAX, RDX +#[cfg(target_arch = "x86_64")] +const UNWIND_DATA_REG: (i32, i32) = (0, 1); // RAX, RDX - #[cfg(any(target_arch = "arm", target_arch = "aarch64"))] - const UNWIND_DATA_REG: (i32, i32) = (0, 1); // R0, R1 / X0, X1 +#[cfg(any(target_arch = "arm", target_arch = "aarch64"))] +const UNWIND_DATA_REG: (i32, i32) = (0, 1); // R0, R1 / X0, X1 - #[cfg(any(target_arch = "mips", target_arch = "mipsel"))] - const UNWIND_DATA_REG: (i32, i32) = (4, 5); // A0, A1 +#[cfg(any(target_arch = "mips", target_arch = "mipsel"))] +const UNWIND_DATA_REG: (i32, i32) = (4, 5); // A0, A1 - #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] - const UNWIND_DATA_REG: (i32, i32) = (3, 4); // R3, R4 / X3, X4 +#[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))] +const UNWIND_DATA_REG: (i32, i32) = (3, 4); // R3, R4 / X3, X4 - // Based on GCC's C and C++ personality routines. For reference, see: - // https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_personality.cc - // https://github.com/gcc-mirror/gcc/blob/trunk/libgcc/unwind-c.c - #[lang = "eh_personality"] - #[no_mangle] - #[allow(unused)] - unsafe extern "C" fn rust_eh_personality(version: c_int, - actions: uw::_Unwind_Action, - exception_class: uw::_Unwind_Exception_Class, - exception_object: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context) - -> uw::_Unwind_Reason_Code { - if version != 1 { - return uw::_URC_FATAL_PHASE1_ERROR; - } - let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8; - let mut ip_before_instr: c_int = 0; - let ip = uw::_Unwind_GetIPInfo(context, &mut ip_before_instr); - let eh_context = EHContext { - // The return address points 1 byte past the call instruction, - // which could be in the next IP range in LSDA range table. - ip: if ip_before_instr != 0 { ip } else { ip - 1 }, - func_start: uw::_Unwind_GetRegionStart(context), - get_text_start: &|| uw::_Unwind_GetTextRelBase(context), - get_data_start: &|| uw::_Unwind_GetDataRelBase(context), - }; - let eh_action = find_eh_action(lsda, &eh_context); +// The following code is based on GCC's C and C++ personality routines. For reference, see: +// https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_personality.cc +// https://github.com/gcc-mirror/gcc/blob/trunk/libgcc/unwind-c.c - if actions as i32 & uw::_UA_SEARCH_PHASE as i32 != 0 { - match eh_action { - EHAction::None | EHAction::Cleanup(_) => return uw::_URC_CONTINUE_UNWIND, - EHAction::Catch(_) => return uw::_URC_HANDLER_FOUND, - EHAction::Terminate => return uw::_URC_FATAL_PHASE1_ERROR, - } - } else { - match eh_action { - EHAction::None => return uw::_URC_CONTINUE_UNWIND, - EHAction::Cleanup(lpad) | EHAction::Catch(lpad) => { - uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0, exception_object as uintptr_t); - uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, 0); - uw::_Unwind_SetIP(context, lpad); - return uw::_URC_INSTALL_CONTEXT; - } - EHAction::Terminate => return uw::_URC_FATAL_PHASE2_ERROR, +// The personality routine for most of our targets, except ARM, which has a slightly different ABI +// (however, iOS goes here as it uses SjLj unwinding). Also, the 64-bit Windows implementation +// lives in seh64_gnu.rs +#[cfg(all(any(target_os = "ios", not(target_arch = "arm"))))] +#[lang = "eh_personality"] +#[no_mangle] +#[allow(unused)] +unsafe extern "C" fn rust_eh_personality(version: c_int, + actions: uw::_Unwind_Action, + exception_class: uw::_Unwind_Exception_Class, + exception_object: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context) + -> uw::_Unwind_Reason_Code { + if version != 1 { + return uw::_URC_FATAL_PHASE1_ERROR; + } + let eh_action = find_eh_action(context); + if actions as i32 & uw::_UA_SEARCH_PHASE as i32 != 0 { + match eh_action { + EHAction::None | EHAction::Cleanup(_) => return uw::_URC_CONTINUE_UNWIND, + EHAction::Catch(_) => return uw::_URC_HANDLER_FOUND, + EHAction::Terminate => return uw::_URC_FATAL_PHASE1_ERROR, + } + } else { + match eh_action { + EHAction::None => return uw::_URC_CONTINUE_UNWIND, + EHAction::Cleanup(lpad) | EHAction::Catch(lpad) => { + uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0, exception_object as uintptr_t); + uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, 0); + uw::_Unwind_SetIP(context, lpad); + return uw::_URC_INSTALL_CONTEXT; } + EHAction::Terminate => return uw::_URC_FATAL_PHASE2_ERROR, } } - - #[cfg(stage0)] - #[lang = "eh_personality_catch"] - #[no_mangle] - pub unsafe 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 { - rust_eh_personality(version, actions, exception_class, ue_header, context) - } } -// ARM EHABI uses a slightly different personality routine signature, -// but otherwise works the same. +// ARM EHABI personality routine. +// http://infocenter.arm.com/help/topic/com.arm.doc.ihi0038b/IHI0038B_ehabi.pdf #[cfg(all(target_arch = "arm", not(target_os = "ios")))] -pub mod eabi { - use unwind as uw; - use libc::c_int; +#[lang = "eh_personality"] +#[no_mangle] +unsafe extern "C" fn rust_eh_personality(state: uw::_Unwind_State, + exception_object: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context) + -> uw::_Unwind_Reason_Code { + let state = state as c_int; + let action = state & uw::_US_ACTION_MASK as c_int; + let search_phase = if action == uw::_US_VIRTUAL_UNWIND_FRAME as c_int { + // Backtraces on ARM will call the personality routine with + // state == _US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND. In those cases + // we want to continue unwinding the stack, otherwise all our backtraces + // would end at __rust_try + if state & uw::_US_FORCE_UNWIND as c_int != 0 { + return continue_unwind(exception_object, context) + } + true + } else if action == uw::_US_UNWIND_FRAME_STARTING as c_int { + false + } else if action == uw::_US_UNWIND_FRAME_RESUME as c_int { + return continue_unwind(exception_object, context); + } else { + return uw::_URC_FAILURE; + }; - 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; - } + // The DWARF unwinder assumes that _Unwind_Context holds things like the function + // and LSDA pointers, however ARM EHABI places them into the exception object. + // To preserve signatures of functions like _Unwind_GetLanguageSpecificData(), which + // take only the context pointer, GCC personality routines stash a pointer to exception_object + // in the context, using location reserved for ARM's "scratch register" (r12). + uw::_Unwind_SetGR(context, uw::UNWIND_POINTER_REG, exception_object as uw::_Unwind_Ptr); + // ...A more principled approach would be to provide the full definition of ARM's + // _Unwind_Context in our libunwind bindings and fetch the required data from there directly, + // bypassing DWARF compatibility functions. - #[lang = "eh_personality"] - #[no_mangle] - 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) } + let eh_action = find_eh_action(context); + if search_phase { + match eh_action { + EHAction::None | + EHAction::Cleanup(_) => return continue_unwind(exception_object, context), + EHAction::Catch(_) => return uw::_URC_HANDLER_FOUND, + EHAction::Terminate => return uw::_URC_FAILURE, + } + } else { + match eh_action { + EHAction::None => return continue_unwind(exception_object, context), + EHAction::Cleanup(lpad) | EHAction::Catch(lpad) => { + uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0, exception_object as uintptr_t); + uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, 0); + uw::_Unwind_SetIP(context, lpad); + return uw::_URC_INSTALL_CONTEXT; + } + EHAction::Terminate => return uw::_URC_FAILURE, + } } - #[lang = "eh_personality_catch"] - #[no_mangle] - 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 { - // Backtraces on ARM will call the personality routine with - // state == _US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND. In those cases - // we want to continue unwinding the stack, otherwise all our backtraces - // would end at __rust_try. - if (state as c_int & uw::_US_ACTION_MASK as c_int) == - uw::_US_VIRTUAL_UNWIND_FRAME as c_int && - (state as c_int & uw::_US_FORCE_UNWIND as c_int) == 0 { - // search phase - uw::_URC_HANDLER_FOUND // catch! + // On ARM EHABI the personality routine is responsible for actually + // unwinding a single stack frame before returning (ARM EHABI Sec. 6.1). + unsafe fn continue_unwind(exception_object: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context) + -> uw::_Unwind_Reason_Code { + if __gnu_unwind_frame(exception_object, context) == uw::_URC_NO_REASON { + uw::_URC_CONTINUE_UNWIND } else { - // cleanup phase - unsafe { __gcc_personality_v0(state, ue_header, context) } + uw::_URC_FAILURE } } + // defined in libgcc + extern "C" { + fn __gnu_unwind_frame(exception_object: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context) + -> uw::_Unwind_Reason_Code; + } +} + +unsafe fn find_eh_action(context: *mut uw::_Unwind_Context) -> EHAction { + let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8; + let mut ip_before_instr: c_int = 0; + let ip = uw::_Unwind_GetIPInfo(context, &mut ip_before_instr); + let eh_context = EHContext { + // The return address points 1 byte past the call instruction, + // which could be in the next IP range in LSDA range table. + ip: if ip_before_instr != 0 { ip } else { ip - 1 }, + func_start: uw::_Unwind_GetRegionStart(context), + get_text_start: &|| uw::_Unwind_GetTextRelBase(context), + get_data_start: &|| uw::_Unwind_GetDataRelBase(context), + }; + eh::find_eh_action(lsda, &eh_context) +} + +// *** Delete after a new snapshot *** +#[cfg(all(stage0, any(target_os = "ios", not(target_arch = "arm"))))] +#[lang = "eh_personality_catch"] +#[no_mangle] +pub unsafe 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 { + rust_eh_personality(version, actions, exception_class, ue_header, context) +} + +// *** Delete after a new snapshot *** +#[cfg(all(stage0, target_arch = "arm", not(target_os = "ios")))] +#[lang = "eh_personality_catch"] +#[no_mangle] +pub unsafe 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 { + rust_eh_personality(state, ue_header, context) } // See docs in the `unwind` module. diff --git a/src/libpanic_unwind/seh64_gnu.rs b/src/libpanic_unwind/seh64_gnu.rs index 7dc428871b387..3642e2488958e 100644 --- a/src/libpanic_unwind/seh64_gnu.rs +++ b/src/libpanic_unwind/seh64_gnu.rs @@ -81,6 +81,7 @@ pub unsafe fn cleanup(ptr: *mut u8) -> Box { // This is considered acceptable, because the behavior of throwing exceptions // through a C ABI boundary is undefined. +// *** Delete after a new snapshot *** #[cfg(stage0)] #[lang = "eh_personality_catch"] #[cfg(not(test))] diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index 960305e10488d..a209b1d1abd7c 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -359,7 +359,6 @@ language_item_table! { StartFnLangItem, "start", start_fn; EhPersonalityLangItem, "eh_personality", eh_personality; - EhPersonalityCatchLangItem, "eh_personality_catch", eh_personality_catch; EhUnwindResumeLangItem, "eh_unwind_resume", eh_unwind_resume; MSVCTryFilterLangItem, "msvc_try_filter", msvc_try_filter; diff --git a/src/librustc_trans/intrinsic.rs b/src/librustc_trans/intrinsic.rs index f033b278fe7f0..3ec79cf107a30 100644 --- a/src/librustc_trans/intrinsic.rs +++ b/src/librustc_trans/intrinsic.rs @@ -19,7 +19,6 @@ use rustc::ty::subst; use rustc::ty::subst::FnSpace; use abi::{Abi, FnType}; use adt; -use attributes; use base::*; use build::*; use callee::{self, Callee}; @@ -37,7 +36,6 @@ use machine; use type_::Type; use rustc::ty::{self, Ty}; use Disr; -use rustc::ty::subst::Substs; use rustc::hir; use syntax::ast; use syntax::ptr::P; @@ -1172,7 +1170,6 @@ fn trans_gnu_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, dloc: DebugLoc) -> Block<'blk, 'tcx> { let llfn = get_rust_try_fn(bcx.fcx, &mut |bcx| { let ccx = bcx.ccx(); - let tcx = ccx.tcx(); let dloc = DebugLoc::None; // Translates the shims described above: @@ -1192,20 +1189,6 @@ fn trans_gnu_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // expected to be `*mut *mut u8` for this to actually work, but that's // managed by the standard library. - attributes::emit_uwtable(bcx.fcx.llfn, true); - let target = &bcx.sess().target.target; - let catch_pers = if target.arch == "arm" && target.target_os != "ios" { - // Only ARM still uses a separate catch personality (for now) - match tcx.lang_items.eh_personality_catch() { - Some(did) => { - Callee::def(ccx, did, tcx.mk_substs(Substs::empty())).reify(ccx).val - } - None => bug!("eh_personality_catch not defined"), - } - } else { - bcx.fcx.eh_personality() - }; - let then = bcx.fcx.new_temp_block("then"); let catch = bcx.fcx.new_temp_block("catch"); @@ -1223,7 +1206,7 @@ fn trans_gnu_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // rust_try ignores the selector. let lpad_ty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)], false); - let vals = LandingPad(catch, lpad_ty, catch_pers, 1); + let vals = LandingPad(catch, lpad_ty, bcx.fcx.eh_personality(), 1); AddClause(catch, vals, C_null(Type::i8p(ccx))); let ptr = ExtractValue(catch, vals, 0); Store(catch, ptr, BitCast(catch, local_ptr, Type::i8p(ccx).ptr_to())); diff --git a/src/libunwind/libunwind.rs b/src/libunwind/libunwind.rs index d9b6c9ed74dd2..5e242db0a888d 100644 --- a/src/libunwind/libunwind.rs +++ b/src/libunwind/libunwind.rs @@ -10,38 +10,15 @@ #![allow(bad_style)] -use libc; - -#[cfg(any(not(target_arch = "arm"), target_os = "ios"))] -pub use self::_Unwind_Action::*; -#[cfg(target_arch = "arm")] -pub use self::_Unwind_State::*; -pub use self::_Unwind_Reason_Code::*; - -#[cfg(any(not(target_arch = "arm"), target_os = "ios"))] -#[repr(C)] -#[derive(Clone, Copy)] -pub enum _Unwind_Action { - _UA_SEARCH_PHASE = 1, - _UA_CLEANUP_PHASE = 2, - _UA_HANDLER_FRAME = 4, - _UA_FORCE_UNWIND = 8, - _UA_END_OF_STACK = 16, +macro_rules! cfg_if { + ( $( if #[cfg( $meta:meta )] { $($it1:item)* } else { $($it2:item)* } )* ) => + ( $( $( #[cfg($meta)] $it1)* $( #[cfg(not($meta))] $it2)* )* ) } -#[cfg(target_arch = "arm")] -#[repr(C)] -#[derive(Clone, Copy)] -pub enum _Unwind_State { - _US_VIRTUAL_UNWIND_FRAME = 0, - _US_UNWIND_FRAME_STARTING = 1, - _US_UNWIND_FRAME_RESUME = 2, - _US_ACTION_MASK = 3, - _US_FORCE_UNWIND = 8, - _US_END_OF_STACK = 16, -} +use libc::{c_int, c_void, uintptr_t}; #[repr(C)] +#[derive(Copy, Clone, PartialEq)] pub enum _Unwind_Reason_Code { _URC_NO_REASON = 0, _URC_FOREIGN_EXCEPTION_CAUGHT = 1, @@ -52,17 +29,15 @@ pub enum _Unwind_Reason_Code { _URC_HANDLER_FOUND = 6, _URC_INSTALL_CONTEXT = 7, _URC_CONTINUE_UNWIND = 8, - _URC_FAILURE = 9, // used only by ARM EABI + _URC_FAILURE = 9, // used only by ARM EHABI } +pub use self::_Unwind_Reason_Code::*; pub type _Unwind_Exception_Class = u64; - -pub type _Unwind_Word = libc::uintptr_t; -pub type _Unwind_Ptr = libc::uintptr_t; - -pub type _Unwind_Trace_Fn = extern "C" fn(ctx: *mut _Unwind_Context, arg: *mut libc::c_void) +pub type _Unwind_Word = uintptr_t; +pub type _Unwind_Ptr = uintptr_t; +pub type _Unwind_Trace_Fn = extern "C" fn(ctx: *mut _Unwind_Context, arg: *mut c_void) -> _Unwind_Reason_Code; - #[cfg(target_arch = "x86")] pub const unwinder_private_data_size: usize = 5; @@ -99,91 +74,55 @@ pub enum _Unwind_Context {} pub type _Unwind_Exception_Cleanup_Fn = extern "C" fn(unwind_code: _Unwind_Reason_Code, exception: *mut _Unwind_Exception); - -#[cfg_attr(any(all(target_os = "linux", not(target_env = "musl")), - target_os = "freebsd", - target_os = "solaris", - all(target_os = "linux", - target_env = "musl", - not(target_arch = "x86"), - not(target_arch = "x86_64"))), - link(name = "gcc_s"))] -#[cfg_attr(all(target_os = "linux", - target_env = "musl", - any(target_arch = "x86", target_arch = "x86_64"), - not(test)), - link(name = "unwind", kind = "static"))] -#[cfg_attr(any(target_os = "android", target_os = "openbsd"), - link(name = "gcc"))] -#[cfg_attr(all(target_os = "netbsd", not(target_vendor = "rumprun")), - link(name = "gcc"))] -#[cfg_attr(all(target_os = "netbsd", target_vendor = "rumprun"), - link(name = "unwind"))] -#[cfg_attr(target_os = "dragonfly", - link(name = "gcc_pic"))] -#[cfg_attr(target_os = "bitrig", - link(name = "c++abi"))] -#[cfg_attr(all(target_os = "windows", target_env = "gnu"), - link(name = "gcc_eh"))] -#[cfg(not(cargobuild))] -extern "C" {} - extern "C" { - // iOS on armv7 uses SjLj exceptions and requires to link - // against corresponding routine (..._SjLj_...) - #[cfg(not(all(target_os = "ios", target_arch = "arm")))] - #[unwind] - pub fn _Unwind_RaiseException(exception: *mut _Unwind_Exception) -> _Unwind_Reason_Code; - - #[cfg(all(target_os = "ios", target_arch = "arm"))] - #[unwind] - fn _Unwind_SjLj_RaiseException(e: *mut _Unwind_Exception) -> _Unwind_Reason_Code; - - pub fn _Unwind_DeleteException(exception: *mut _Unwind_Exception); - #[unwind] pub fn _Unwind_Resume(exception: *mut _Unwind_Exception) -> !; - - // No native _Unwind_Backtrace on iOS - #[cfg(not(all(target_os = "ios", target_arch = "arm")))] - pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn, - trace_argument: *mut libc::c_void) - -> _Unwind_Reason_Code; - - // available since GCC 4.2.0, should be fine for our purpose - #[cfg(all(not(all(target_os = "android", target_arch = "arm")), - not(all(target_os = "linux", target_arch = "arm"))))] - pub fn _Unwind_GetIPInfo(ctx: *mut _Unwind_Context, - ip_before_insn: *mut libc::c_int) - -> libc::uintptr_t; - - pub fn _Unwind_GetLanguageSpecificData(ctx: *mut _Unwind_Context) -> _Unwind_Ptr; + pub fn _Unwind_DeleteException(exception: *mut _Unwind_Exception); + pub fn _Unwind_GetLanguageSpecificData(ctx: *mut _Unwind_Context) -> *mut c_void; pub fn _Unwind_GetRegionStart(ctx: *mut _Unwind_Context) -> _Unwind_Ptr; pub fn _Unwind_GetTextRelBase(ctx: *mut _Unwind_Context) -> _Unwind_Ptr; pub fn _Unwind_GetDataRelBase(ctx: *mut _Unwind_Context) -> _Unwind_Ptr; - pub fn _Unwind_SetGR(ctx: *mut _Unwind_Context, reg_index: libc::c_int, value: _Unwind_Ptr); - pub fn _Unwind_SetIP(ctx: *mut _Unwind_Context, value: _Unwind_Ptr); - - #[cfg(all(not(target_os = "android"), - not(all(target_os = "linux", target_arch = "arm"))))] - pub fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void) -> *mut libc::c_void; } -// ... and now we just providing access to SjLj counterspart -// through a standard name to hide those details from others -// (see also comment above regarding _Unwind_RaiseException) -#[cfg(all(target_os = "ios", target_arch = "arm"))] -#[inline] -pub unsafe fn _Unwind_RaiseException(exc: *mut _Unwind_Exception) -> _Unwind_Reason_Code { - _Unwind_SjLj_RaiseException(exc) -} +cfg_if! { +if #[cfg(not(any(all(target_os = "android", target_arch = "arm"), + all(target_os = "linux", target_arch = "arm"))))] { + // Not ARM EHABI + #[repr(C)] + #[derive(Copy, Clone, PartialEq)] + pub enum _Unwind_Action { + _UA_SEARCH_PHASE = 1, + _UA_CLEANUP_PHASE = 2, + _UA_HANDLER_FRAME = 4, + _UA_FORCE_UNWIND = 8, + _UA_END_OF_STACK = 16, + } + pub use self::_Unwind_Action::*; + + extern "C" { + pub fn _Unwind_GetGR(ctx: *mut _Unwind_Context, reg_index: c_int) -> _Unwind_Word; + pub fn _Unwind_SetGR(ctx: *mut _Unwind_Context, reg_index: c_int, value: _Unwind_Word); + pub fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> _Unwind_Word; + pub fn _Unwind_SetIP(ctx: *mut _Unwind_Context, value: _Unwind_Word); + pub fn _Unwind_GetIPInfo(ctx: *mut _Unwind_Context, ip_before_insn: *mut c_int) + -> _Unwind_Word; + pub fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void; + } + +} else { + // ARM EHABI + #[repr(C)] + #[derive(Copy, Clone, PartialEq)] + pub enum _Unwind_State { + _US_VIRTUAL_UNWIND_FRAME = 0, + _US_UNWIND_FRAME_STARTING = 1, + _US_UNWIND_FRAME_RESUME = 2, + _US_ACTION_MASK = 3, + _US_FORCE_UNWIND = 8, + _US_END_OF_STACK = 16, + } + pub use self::_Unwind_State::*; -// On android, the function _Unwind_GetIP is a macro, and this is the -// expansion of the macro. This is all copy/pasted directly from the -// header file with the definition of _Unwind_GetIP. -#[cfg(any(all(target_os = "android", target_arch = "arm"), - all(target_os = "linux", target_arch = "arm")))] -pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t { #[repr(C)] enum _Unwind_VRS_Result { _UVRSR_OK = 0, @@ -198,6 +137,7 @@ pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t { _UVRSC_WMMXD = 3, _UVRSC_WMMXC = 4, } + use self::_Unwind_VRS_RegClass::*; #[repr(C)] enum _Unwind_VRS_DataRepresentation { _UVRSD_UINT32 = 0, @@ -207,42 +147,116 @@ pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t { _UVRSD_FLOAT = 4, _UVRSD_DOUBLE = 5, } + use self::_Unwind_VRS_DataRepresentation::*; + + pub const UNWIND_POINTER_REG: c_int = 12; + pub const UNWIND_IP_REG: c_int = 15; - type _Unwind_Word = libc::c_uint; extern "C" { fn _Unwind_VRS_Get(ctx: *mut _Unwind_Context, - klass: _Unwind_VRS_RegClass, - word: _Unwind_Word, + regclass: _Unwind_VRS_RegClass, + regno: _Unwind_Word, + repr: _Unwind_VRS_DataRepresentation, + data: *mut c_void) + -> _Unwind_VRS_Result; + + fn _Unwind_VRS_Set(ctx: *mut _Unwind_Context, + regclass: _Unwind_VRS_RegClass, + regno: _Unwind_Word, repr: _Unwind_VRS_DataRepresentation, - data: *mut libc::c_void) + data: *mut c_void) -> _Unwind_VRS_Result; } - let mut val: _Unwind_Word = 0; - let ptr = &mut val as *mut _Unwind_Word; - let _ = _Unwind_VRS_Get(ctx, - _Unwind_VRS_RegClass::_UVRSC_CORE, - 15, - _Unwind_VRS_DataRepresentation::_UVRSD_UINT32, - ptr as *mut libc::c_void); - (val & !1) as libc::uintptr_t -} + // On Android or ARM/Linux, these are implemented as macros: + + pub unsafe fn _Unwind_GetGR(ctx: *mut _Unwind_Context, reg_index: c_int) -> _Unwind_Word { + let mut val: _Unwind_Word = 0; + _Unwind_VRS_Get(ctx, _UVRSC_CORE, reg_index as _Unwind_Word, _UVRSD_UINT32, + &mut val as *mut _ as *mut c_void); + val + } -// This function doesn't exist on Android or ARM/Linux, so make it same -// to _Unwind_GetIP -#[cfg(any(all(target_os = "android", target_arch = "arm"), - all(target_os = "linux", target_arch = "arm")))] -pub unsafe fn _Unwind_GetIPInfo(ctx: *mut _Unwind_Context, - ip_before_insn: *mut libc::c_int) - -> libc::uintptr_t { - *ip_before_insn = 0; - _Unwind_GetIP(ctx) + pub unsafe fn _Unwind_SetGR(ctx: *mut _Unwind_Context, reg_index: c_int, value: _Unwind_Word) { + let mut value = value; + _Unwind_VRS_Set(ctx, _UVRSC_CORE, reg_index as _Unwind_Word, _UVRSD_UINT32, + &mut value as *mut _ as *mut c_void); + } + + pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) + -> _Unwind_Word { + let val = _Unwind_GetGR(ctx, UNWIND_IP_REG); + (val & !1) as _Unwind_Word + } + + pub unsafe fn _Unwind_SetIP(ctx: *mut _Unwind_Context, + value: _Unwind_Word) { + // Propagate thumb bit to instruction pointer + let thumb_state = _Unwind_GetGR(ctx, UNWIND_IP_REG) & 1; + let value = value | thumb_state; + _Unwind_SetGR(ctx, UNWIND_IP_REG, value); + } + + pub unsafe fn _Unwind_GetIPInfo(ctx: *mut _Unwind_Context, + ip_before_insn: *mut c_int) + -> _Unwind_Word { + *ip_before_insn = 0; + _Unwind_GetIP(ctx) + } + + // This function also doesn't exist on Android or ARM/Linux, so make it a no-op + pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void { + pc + } } -// This function also doesn't exist on Android or ARM/Linux, so make it -// a no-op -#[cfg(any(target_os = "android", - all(target_os = "linux", target_arch = "arm")))] -pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void) -> *mut libc::c_void { - pc +if #[cfg(not(all(target_os = "ios", target_arch = "arm")))] { + // Not 32-bit iOS + extern "C" { + #[unwind] + pub fn _Unwind_RaiseException(exception: *mut _Unwind_Exception) -> _Unwind_Reason_Code; + pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn, + trace_argument: *mut c_void) + -> _Unwind_Reason_Code; + } +} else { + // 32-bit iOS uses SjLj and does not provide _Unwind_Backtrace() + extern "C" { + #[unwind] + pub fn _Unwind_SjLj_RaiseException(e: *mut _Unwind_Exception) -> _Unwind_Reason_Code; + } + + #[inline] + pub unsafe fn _Unwind_RaiseException(exc: *mut _Unwind_Exception) -> _Unwind_Reason_Code { + _Unwind_SjLj_RaiseException(exc) + } } +} // cfg_if! + +#[cfg_attr(any(all(target_os = "linux", not(target_env = "musl")), + target_os = "freebsd", + target_os = "solaris", + all(target_os = "linux", + target_env = "musl", + not(target_arch = "x86"), + not(target_arch = "x86_64"))), + link(name = "gcc_s"))] +#[cfg_attr(all(target_os = "linux", + target_env = "musl", + any(target_arch = "x86", target_arch = "x86_64"), + not(test)), + link(name = "unwind", kind = "static"))] +#[cfg_attr(any(target_os = "android", target_os = "openbsd"), + link(name = "gcc"))] +#[cfg_attr(all(target_os = "netbsd", not(target_vendor = "rumprun")), + link(name = "gcc"))] +#[cfg_attr(all(target_os = "netbsd", target_vendor = "rumprun"), + link(name = "unwind"))] +#[cfg_attr(target_os = "dragonfly", + link(name = "gcc_pic"))] +#[cfg_attr(target_os = "bitrig", + link(name = "c++abi"))] +#[cfg_attr(all(target_os = "windows", target_env = "gnu"), + link(name = "gcc_eh"))] +#[cfg(not(cargobuild))] +extern "C" {}