1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! Win64 SEH (see http://msdn.microsoft.com/en-us/library/1eyas8tf.aspx)
13 //! On Windows (currently only on MSVC), the default exception handling
14 //! mechanism is Structured Exception Handling (SEH). This is quite different
15 //! than Dwarf-based exception handling (e.g. what other unix platforms use) in
16 //! terms of compiler internals, so LLVM is required to have a good deal of
17 //! extra support for SEH. Currently this support is somewhat lacking, so what's
18 //! here is the bare bones of SEH support.
20 //! In a nutshell, what happens here is:
22 //! 1. The `panic` function calls the standard Windows function `RaiseException`
23 //! with a Rust-specific code, triggering the unwinding process.
24 //! 2. All landing pads generated by the compiler (just "cleanup" landing pads)
25 //! use the personality function `__C_specific_handler`, a function in the
26 //! CRT, and the unwinding code in Windows will use this personality function
27 //! to execute all cleanup code on the stack.
28 //! 3. Eventually the "catch" code in `rust_try` (located in
29 //! src/rt/rust_try_msvc_64.ll) is executed, which will ensure that the
30 //! exception being caught is indeed a Rust exception, returning control back
33 //! Some specific differences from the gcc-based exception handling are:
35 //! * Rust has no custom personality function, it is instead *always*
36 //! __C_specific_handler, so the filtering is done in a C++-like manner
37 //! instead of in the personality function itself. Note that the specific
38 //! syntax for this (found in the rust_try_msvc_64.ll) is taken from an LLVM
39 //! test case for SEH.
40 //! * We've got some data to transmit across the unwinding boundary,
41 //! specifically a `Box<Any + Send + 'static>`. In Dwarf-based unwinding this
42 //! data is part of the payload of the exception, but I have not currently
43 //! figured out how to do this with LLVM's bindings. Judging by some comments
44 //! in the LLVM test cases this may not even be possible currently with LLVM,
45 //! so this is just abandoned entirely. Instead the data is stored in a
46 //! thread-local in `panic` and retrieved during `cleanup`.
48 //! So given all that, the bindings here are pretty small,
55 use libc
::{c_ulong, DWORD, c_void}
;
56 use sys_common
::thread_local
::StaticKey
;
59 const RUST_PANIC
: DWORD
= 0x52555354;
60 static PANIC_DATA
: StaticKey
= StaticKey
::new(None
);
62 // This function is provided by kernel32.dll
64 fn RaiseException(dwExceptionCode
: DWORD
,
65 dwExceptionFlags
: DWORD
,
66 nNumberOfArguments
: DWORD
,
67 lpArguments
: *const c_ulong
);
71 pub struct EXCEPTION_POINTERS
{
72 ExceptionRecord
: *mut EXCEPTION_RECORD
,
73 ContextRecord
: *mut CONTEXT
,
79 struct EXCEPTION_RECORD
{
81 ExceptionFlags
: DWORD
,
82 ExceptionRecord
: *mut _EXCEPTION_RECORD
,
83 ExceptionAddress
: *mut c_void
,
84 NumberParameters
: DWORD
,
85 ExceptionInformation
: [*mut c_ulong
; EXCEPTION_MAXIMUM_PARAMETERS
],
88 enum _EXCEPTION_RECORD {}
90 const EXCEPTION_MAXIMUM_PARAMETERS
: usize = 15;
92 pub unsafe fn panic(data
: Box
<Any
+ Send
+ '
static>) -> ! {
93 // See module docs above for an explanation of why `data` is stored in a
94 // thread local instead of being passed as an argument to the
95 // `RaiseException` function (which can in theory carry along arbitrary
97 let exception
= Box
::new(data
);
98 rtassert
!(PANIC_DATA
.get().is_null());
99 PANIC_DATA
.set(Box
::into_raw(exception
) as *mut u8);
101 RaiseException(RUST_PANIC
, 0, 0, 0 as *const _
);
102 rtabort
!("could not unwind stack");
105 pub unsafe fn cleanup(ptr
: *mut u8) -> Box
<Any
+ Send
+ '
static> {
106 // The `ptr` here actually corresponds to the code of the exception, and our
107 // real data is stored in our thread local.
108 rtassert
!(ptr
as DWORD
== RUST_PANIC
);
110 let data
= PANIC_DATA
.get() as *mut Box
<Any
+ Send
+ '
static>;
111 PANIC_DATA
.set(0 as *mut u8);
112 rtassert
!(!data
.is_null());
117 // This is required by the compiler to exist (e.g. it's a lang item), but it's
118 // never actually called by the compiler because __C_specific_handler is the
119 // personality function that is always used. Hence this is just an aborting
121 #[lang = "eh_personality"]
122 fn rust_eh_personality() {
123 unsafe { ::intrinsics::abort() }
126 // This is a function referenced from `rust_try_msvc_64.ll` which is used to
127 // filter the exceptions being caught by that function.
129 // In theory local variables can be accessed through the `rbp` parameter of this
130 // function, but a comment in an LLVM test case indicates that this is not
131 // implemented in LLVM, so this is just an idempotent function which doesn't
132 // ferry along any other information.
134 // This function just takes a look at the current EXCEPTION_RECORD being thrown
135 // to ensure that it's code is RUST_PANIC, which was set by the call to
136 // `RaiseException` above in the `panic` function.
138 #[lang = "msvc_try_filter"]
139 pub extern fn __rust_try_filter(eh_ptrs
: *mut EXCEPTION_POINTERS
,
140 _rbp
: *mut u8) -> i32 {
142 ((*(*eh_ptrs
).ExceptionRecord
).ExceptionCode
== RUST_PANIC
) as i32