-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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 rust_eh_personality in Rust, remove rust_eh_personality_catch. #34832
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -106,117 +106,96 @@ fn rust_exception_class() -> uw::_Unwind_Exception_Class { | |
0x4d4f5a_00_52555354 | ||
} | ||
|
||
// We could implement our personality routine in 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"))))] | ||
// 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; | ||
use libc::{c_int, uintptr_t}; | ||
use dwarf::eh::{EHContext, EHAction, find_eh_action}; | ||
|
||
extern "C" { | ||
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; | ||
} | ||
// 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. | ||
|
||
#[lang = "eh_personality"] | ||
#[no_mangle] | ||
extern "C" 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) } | ||
} | ||
#[cfg(target_arch = "x86")] | ||
const UNWIND_DATA_REG: (i32, i32) = (0, 2); // EAX, EDX | ||
|
||
#[lang = "eh_personality_catch"] | ||
#[no_mangle] | ||
pub 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 { | ||
#[cfg(target_arch = "x86_64")] | ||
const UNWIND_DATA_REG: (i32, i32) = (0, 1); // RAX, RDX | ||
|
||
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(any(target_arch = "arm", target_arch = "aarch64"))] | ||
const UNWIND_DATA_REG: (i32, i32) = (0, 1); // R0, R1 / X0, X1 | ||
|
||
#[cfg(all(target_os = "ios", target_arch = "arm"))] | ||
pub mod eabi { | ||
use unwind as uw; | ||
use libc::c_int; | ||
#[cfg(any(target_arch = "mips", target_arch = "mipsel"))] | ||
const UNWIND_DATA_REG: (i32, i32) = (4, 5); // A0, A1 | ||
|
||
extern "C" { | ||
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; | ||
} | ||
#[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] | ||
pub extern "C" 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) } | ||
#[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 { | ||
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. Oh holy cow! I was figuring we'd just detect a little but but still piggy back on the system personality routines for most of the heavy lifting, but hey if we've got this looks good to me! Was all of this based off a gcc or some other implementation? If so, could this have some more comments and/or links to where all this logic is coming from? 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. Done. |
||
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); | ||
|
||
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 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 { | ||
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_sj0(version, actions, exception_class, ue_header, context) } | ||
} | ||
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. | ||
#[cfg(all(target_arch = "arm", not(target_os = "ios")))] | ||
|
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.
Should RDX here be 2? I'm a little confused by LLVM's definitions here I think, I see:
Which one of these numbers translates to the indexes here? The 0/2 for x86 match up with above, but the 0/1 I can't quite find for x86_64...
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.
RDX is defined here.
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.
Yeah I was just a little confused, the index for x86 seemed to come from the left hand side or the second/third number in
DwarfRegNum
but for x86_64 it came from the first number inDwarfRegNum
.I wonder if this may be the cause of the test failures?
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.
Yah, the docs are not very clear, but it seems that the first value is for 64 bit mode and the second - for 32. In any case, -2 means "invalid", so there really isn't any choice between the numbers.