-
Notifications
You must be signed in to change notification settings - Fork 12.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement ARM personality routine in Rust. #35032
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This uses libgcc specific functions in a block that does not discriminate on There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This whole file is gcc-specific. |
||
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. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Preexisting, but why is it a lang item and a
no_mangle
? It being a lang item already decides the symbol for it, no_mangle notwithstanding.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
rust_eh_personality
is a "weak" lang item, which means that if it is not defined in the current crate, the compiler will create an external symbol declaration rather than emitting an error. The symbol is then resolved by the linker.This is what allows libcore to use panics, even though the personality routine is defined later in crate dependency chain.