diff options
| author | Vadim Chugunov <vadimcn@gmail.com> | 2016-07-23 17:25:25 -0700 |
|---|---|---|
| committer | Vadim Chugunov <vadimcn@gmail.com> | 2016-07-24 01:53:27 -0700 |
| commit | 6cef93d400b183d70c6c37856b46bb36d7bd8e16 (patch) | |
| tree | 19950ce8c9902c3960c6abc8f180e51da7920176 /src/libpanic_unwind | |
| parent | 2c50f4e484d1c871538ee99032ec2986177b8062 (diff) | |
| download | rust-6cef93d400b183d70c6c37856b46bb36d7bd8e16.tar.gz rust-6cef93d400b183d70c6c37856b46bb36d7bd8e16.zip | |
Implement ARM personality routine in Rust.
Remove the `eh_personality_catch` lang item. Use a simplified version of `cfg_if!` in libunwind.
Diffstat (limited to 'src/libpanic_unwind')
| -rw-r--r-- | src/libpanic_unwind/gcc.rs | 271 | ||||
| -rw-r--r-- | src/libpanic_unwind/seh64_gnu.rs | 1 |
2 files changed, 160 insertions, 112 deletions
diff --git a/src/libpanic_unwind/gcc.rs b/src/libpanic_unwind/gcc.rs index cdf772ad3b8..fdae8f69a9c 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 <arch>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 <arch>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 7dc428871b3..3642e248895 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<Any + Send> { // 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))] |
