]> git.lizzy.rs Git - rust.git/commitdiff
Implement unwinding for Win64.
authorVadim Chugunov <vadimcn@gmail.com>
Thu, 10 Apr 2014 17:48:38 +0000 (10:48 -0700)
committerVadim Chugunov <vadimcn@gmail.com>
Tue, 5 Aug 2014 01:27:23 +0000 (18:27 -0700)
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.

src/librustrt/libunwind.rs
src/librustrt/unwind.rs
src/rt/rust_try.ll

index 789723af1b1332e8fc73bb58cf0c70e2dbcaebb1..db6308c10dcf2b547cd76472d259047a27531ce6 100644 (file)
@@ -60,7 +60,7 @@ pub enum _Unwind_Reason_Code {
 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;
index 52b02479f7fc7a08f8fe409c56b5d7d440895906..9c62936ef9a4922c253a521c70e7f9bea1ce3cc5 100644 (file)
@@ -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 @@ fn __gcc_personality_v0(version: c_int,
     }
 
     #[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 @@ fn __gcc_personality_v0(version: c_int,
 
     #[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 @@ fn __gcc_personality_sj0(version: c_int,
     }
 
     #[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 extern "C" fn rust_eh_personality(
 
     #[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 extern "C" fn rust_eh_personality_catch(
 
 // 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 @@ fn __gcc_personality_v0(state: uw::_Unwind_State,
     }
 
     #[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 @@ extern "C" fn eh_personality(
     #[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 extern "C" fn rust_eh_personality_catch(
             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)
+        }
     }
 }
 
index c912aa789bf6bbe83c8d450eab04b71850ccd26c..08bf5e3dface47ced6d9f958cfd50ca16ce94d2f 100644 (file)
 ; 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(...)