diff --git a/src/librustrt/libunwind.rs b/src/librustrt/libunwind.rs index 789723af1b133..db6308c10dcf2 100644 --- a/src/librustrt/libunwind.rs +++ b/src/librustrt/libunwind.rs @@ -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; diff --git a/src/librustrt/unwind.rs b/src/librustrt/unwind.rs index 52b02479f7fc7..9c62936ef9a49 100644 --- a/src/librustrt/unwind.rs +++ b/src/librustrt/unwind.rs @@ -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,10 +372,108 @@ 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) + } } } diff --git a/src/rt/rust_try.ll b/src/rt/rust_try.ll index c912aa789bf6b..08bf5e3dface4 100644 --- a/src/rt/rust_try.ll +++ b/src/rt/rust_try.ll @@ -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 + 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 - ; extract and return pointer to the exception object +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 + ret i8* %2 } +declare i32 @rust_eh_personality(...) declare i32 @rust_eh_personality_catch(...)