Implement unwinding for Win64.
The original trick used to trigger unwinds would not work with GCC's implementation of SEH, so I had to invent a new one: rust_try now consists of two routines: the outer one, whose handler triggers unwinds, and the inner one, that stops unwinds by having a landing pad that swallows exceptions and passes them on to the outer routine via a normal return.
This commit is contained in:
parent
a12b23521f
commit
5a24ee8a9e
3 changed files with 148 additions and 37 deletions
|
@ -60,7 +60,7 @@ pub type _Unwind_Word = libc::uintptr_t;
|
|||
pub static unwinder_private_data_size: uint = 5;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
pub static unwinder_private_data_size: uint = 2;
|
||||
pub static unwinder_private_data_size: uint = 6;
|
||||
|
||||
#[cfg(target_arch = "arm", not(target_os = "ios"))]
|
||||
pub static unwinder_private_data_size: uint = 20;
|
||||
|
|
|
@ -227,7 +227,7 @@ fn rust_exception_class() -> uw::_Unwind_Exception_Class {
|
|||
// This is achieved by overriding the return value in search phase to always
|
||||
// say "catch!".
|
||||
|
||||
#[cfg(not(target_arch = "arm"), not(test))]
|
||||
#[cfg(not(target_arch = "arm"), not(windows, target_arch = "x86_64"), not(test))]
|
||||
#[doc(hidden)]
|
||||
#[allow(visible_private_types)]
|
||||
pub mod eabi {
|
||||
|
@ -244,7 +244,8 @@ pub mod eabi {
|
|||
}
|
||||
|
||||
#[lang="eh_personality"]
|
||||
extern fn eh_personality(
|
||||
#[no_mangle] // referenced from rust_try.ll
|
||||
extern fn rust_eh_personality(
|
||||
version: c_int,
|
||||
actions: uw::_Unwind_Action,
|
||||
exception_class: uw::_Unwind_Exception_Class,
|
||||
|
@ -260,21 +261,19 @@ pub mod eabi {
|
|||
|
||||
#[no_mangle] // referenced from rust_try.ll
|
||||
pub extern "C" fn rust_eh_personality_catch(
|
||||
version: c_int,
|
||||
_version: c_int,
|
||||
actions: uw::_Unwind_Action,
|
||||
exception_class: uw::_Unwind_Exception_Class,
|
||||
ue_header: *mut uw::_Unwind_Exception,
|
||||
context: *mut uw::_Unwind_Context
|
||||
_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)
|
||||
}
|
||||
uw::_URC_INSTALL_CONTEXT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -299,7 +298,7 @@ pub mod eabi {
|
|||
}
|
||||
|
||||
#[lang="eh_personality"]
|
||||
#[no_mangle] // so we can reference it by name from middle/trans/base.rs
|
||||
#[no_mangle] // referenced from rust_try.ll
|
||||
pub extern "C" fn rust_eh_personality(
|
||||
version: c_int,
|
||||
actions: uw::_Unwind_Action,
|
||||
|
@ -316,21 +315,18 @@ pub mod eabi {
|
|||
|
||||
#[no_mangle] // referenced from rust_try.ll
|
||||
pub extern "C" fn rust_eh_personality_catch(
|
||||
version: c_int,
|
||||
_version: c_int,
|
||||
actions: uw::_Unwind_Action,
|
||||
exception_class: uw::_Unwind_Exception_Class,
|
||||
ue_header: *mut uw::_Unwind_Exception,
|
||||
context: *mut uw::_Unwind_Context
|
||||
_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)
|
||||
}
|
||||
uw::_URC_INSTALL_CONTEXT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -338,7 +334,7 @@ pub mod eabi {
|
|||
|
||||
// ARM EHABI uses a slightly different personality routine signature,
|
||||
// but otherwise works the same.
|
||||
#[cfg(target_arch = "arm", not(test), not(target_os = "ios"))]
|
||||
#[cfg(target_arch = "arm", not(target_os = "ios", not(test)))]
|
||||
#[allow(visible_private_types)]
|
||||
pub mod eabi {
|
||||
use uw = libunwind;
|
||||
|
@ -352,7 +348,8 @@ pub mod eabi {
|
|||
}
|
||||
|
||||
#[lang="eh_personality"]
|
||||
extern "C" fn eh_personality(
|
||||
#[no_mangle] // referenced from rust_try.ll
|
||||
extern "C" fn rust_eh_personality(
|
||||
state: uw::_Unwind_State,
|
||||
ue_header: *mut uw::_Unwind_Exception,
|
||||
context: *mut uw::_Unwind_Context
|
||||
|
@ -366,8 +363,8 @@ pub mod eabi {
|
|||
#[no_mangle] // referenced from rust_try.ll
|
||||
pub extern "C" fn rust_eh_personality_catch(
|
||||
state: uw::_Unwind_State,
|
||||
ue_header: *mut uw::_Unwind_Exception,
|
||||
context: *mut uw::_Unwind_Context
|
||||
_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)
|
||||
|
@ -375,9 +372,107 @@ pub mod eabi {
|
|||
uw::_URC_HANDLER_FOUND // catch!
|
||||
}
|
||||
else { // cleanup phase
|
||||
unsafe {
|
||||
__gcc_personality_v0(state, ue_header, context)
|
||||
uw::_URC_INSTALL_CONTEXT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Win64 SEH (see http://msdn.microsoft.com/en-us/library/1eyas8tf.aspx)
|
||||
//
|
||||
// This looks a bit convoluted because rather than implementing a native SEH handler,
|
||||
// GCC reuses the same personality routine as for the other architectures by wrapping it
|
||||
// with an "API translator" layer (_GCC_specific_handler).
|
||||
|
||||
#[cfg(windows, target_arch = "x86_64", not(test))]
|
||||
#[allow(visible_private_types)]
|
||||
#[allow(non_camel_case_types)]
|
||||
#[allow(unused_variable)]
|
||||
#[allow(uppercase_variables)]
|
||||
pub mod eabi {
|
||||
use uw = libunwind;
|
||||
use libc::{c_void, c_int};
|
||||
|
||||
struct EXCEPTION_RECORD;
|
||||
struct CONTEXT;
|
||||
struct DISPATCHER_CONTEXT;
|
||||
|
||||
#[repr(C)]
|
||||
enum EXCEPTION_DISPOSITION {
|
||||
ExceptionContinueExecution,
|
||||
ExceptionContinueSearch,
|
||||
ExceptionNestedException,
|
||||
ExceptionCollidedUnwind
|
||||
}
|
||||
|
||||
type _Unwind_Personality_Fn =
|
||||
extern "C" fn(
|
||||
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;
|
||||
|
||||
extern "C" {
|
||||
fn __gcc_personality_seh0(
|
||||
exceptionRecord: *mut EXCEPTION_RECORD,
|
||||
establisherFrame: *mut c_void,
|
||||
contextRecord: *mut CONTEXT,
|
||||
dispatcherContext: *mut DISPATCHER_CONTEXT
|
||||
) -> EXCEPTION_DISPOSITION;
|
||||
|
||||
fn _GCC_specific_handler(
|
||||
exceptionRecord: *mut EXCEPTION_RECORD,
|
||||
establisherFrame: *mut c_void,
|
||||
contextRecord: *mut CONTEXT,
|
||||
dispatcherContext: *mut DISPATCHER_CONTEXT,
|
||||
personality: _Unwind_Personality_Fn
|
||||
) -> EXCEPTION_DISPOSITION;
|
||||
}
|
||||
|
||||
#[lang="eh_personality"]
|
||||
#[no_mangle] // referenced from rust_try.ll
|
||||
extern "C" fn rust_eh_personality(
|
||||
exceptionRecord: *mut EXCEPTION_RECORD,
|
||||
establisherFrame: *mut c_void,
|
||||
contextRecord: *mut CONTEXT,
|
||||
dispatcherContext: *mut DISPATCHER_CONTEXT
|
||||
) -> EXCEPTION_DISPOSITION
|
||||
{
|
||||
unsafe {
|
||||
__gcc_personality_seh0(exceptionRecord, establisherFrame,
|
||||
contextRecord, dispatcherContext)
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle] // referenced from rust_try.ll
|
||||
pub extern "C" fn rust_eh_personality_catch(
|
||||
exceptionRecord: *mut EXCEPTION_RECORD,
|
||||
establisherFrame: *mut c_void,
|
||||
contextRecord: *mut CONTEXT,
|
||||
dispatcherContext: *mut DISPATCHER_CONTEXT
|
||||
) -> EXCEPTION_DISPOSITION
|
||||
{
|
||||
extern "C" fn inner(
|
||||
_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
|
||||
uw::_URC_INSTALL_CONTEXT
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
_GCC_specific_handler(exceptionRecord, establisherFrame,
|
||||
contextRecord, dispatcherContext,
|
||||
inner)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,24 +11,40 @@
|
|||
; Rust's try-catch
|
||||
; When f(...) returns normally, the return value is null.
|
||||
; When f(...) throws, the return value is a pointer to the caught exception object.
|
||||
; See also: libstd/rt/unwind.rs
|
||||
; See also: librustrt/unwind.rs
|
||||
|
||||
define i8* @rust_try(void (i8*,i8*)* %f, i8* %fptr, i8* %env) {
|
||||
|
||||
invoke void %f(i8* %fptr, i8* %env)
|
||||
to label %normal
|
||||
unwind label %catch
|
||||
%1 = invoke i8* @rust_try_inner(void (i8*,i8*)* %f, i8* %fptr, i8* %env)
|
||||
to label %normal
|
||||
unwind label %catch
|
||||
|
||||
normal:
|
||||
ret i8* null
|
||||
ret i8* %1
|
||||
|
||||
catch:
|
||||
%1 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @rust_eh_personality_catch to i8*)
|
||||
catch i8* null ; catch everything
|
||||
|
||||
; extract and return pointer to the exception object
|
||||
%2 = extractvalue { i8*, i32 } %1, 0
|
||||
ret i8* %2
|
||||
landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @rust_eh_personality_catch to i8*)
|
||||
catch i8* null
|
||||
; execution will never reach here because rust_try_inner's landing pad does not resume unwinds
|
||||
ret i8* null
|
||||
}
|
||||
|
||||
define internal i8* @rust_try_inner(void (i8*,i8*)* %f, i8* %fptr, i8* %env) {
|
||||
|
||||
invoke void %f(i8* %fptr, i8* %env)
|
||||
to label %normal
|
||||
unwind label %catch
|
||||
|
||||
normal:
|
||||
ret i8* null
|
||||
|
||||
catch:
|
||||
%1 = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @rust_eh_personality to i8*)
|
||||
catch i8* null
|
||||
; extract and return pointer to the exception object
|
||||
%2 = extractvalue { i8*, i32 } %1, 0
|
||||
ret i8* %2
|
||||
}
|
||||
|
||||
declare i32 @rust_eh_personality(...)
|
||||
declare i32 @rust_eh_personality_catch(...)
|
||||
|
|
Loading…
Add table
Reference in a new issue