]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/common/unwind/seh.rs
a201e406a23ead19324a66bf5a6c6221a6035080
[rust.git] / src / libstd / sys / common / unwind / seh.rs
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.
4 //
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.
10
11 //! Win64 SEH (see http://msdn.microsoft.com/en-us/library/1eyas8tf.aspx)
12 //!
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.
19 //!
20 //! In a nutshell, what happens here is:
21 //!
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
31 //!    into Rust.
32 //!
33 //! Some specific differences from the gcc-based exception handling are:
34 //!
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`.
47 //!
48 //! So given all that, the bindings here are pretty small,
49
50 #![allow(bad_style)]
51
52 use prelude::v1::*;
53
54 use any::Any;
55 use libc::{c_ulong, DWORD, c_void};
56 use ptr;
57 use sys_common::thread_local::StaticKey;
58
59 //                        0x R U S T
60 const RUST_PANIC: DWORD = 0x52555354;
61 static PANIC_DATA: StaticKey = StaticKey::new(None);
62
63 // This function is provided by kernel32.dll
64 extern "system" {
65     fn RaiseException(dwExceptionCode: DWORD,
66                       dwExceptionFlags: DWORD,
67                       nNumberOfArguments: DWORD,
68                       lpArguments: *const c_ulong);
69 }
70
71 #[repr(C)]
72 pub struct EXCEPTION_POINTERS {
73     ExceptionRecord: *mut EXCEPTION_RECORD,
74     ContextRecord: *mut CONTEXT,
75 }
76
77 enum CONTEXT {}
78
79 #[repr(C)]
80 struct EXCEPTION_RECORD {
81     ExceptionCode: DWORD,
82     ExceptionFlags: DWORD,
83     ExceptionRecord: *mut _EXCEPTION_RECORD,
84     ExceptionAddress: *mut c_void,
85     NumberParameters: DWORD,
86     ExceptionInformation: [*mut c_ulong; EXCEPTION_MAXIMUM_PARAMETERS],
87 }
88
89 enum _EXCEPTION_RECORD {}
90
91 const EXCEPTION_MAXIMUM_PARAMETERS: usize = 15;
92
93 pub unsafe fn panic(data: Box<Any + Send + 'static>) -> ! {
94     // See module docs above for an explanation of why `data` is stored in a
95     // thread local instead of being passed as an argument to the
96     // `RaiseException` function (which can in theory carry along arbitrary
97     // data).
98     let exception = Box::new(data);
99     rtassert!(PANIC_DATA.get().is_null());
100     PANIC_DATA.set(Box::into_raw(exception) as *mut u8);
101
102     RaiseException(RUST_PANIC, 0, 0, ptr::null());
103     rtabort!("could not unwind stack");
104 }
105
106 pub unsafe fn cleanup(ptr: *mut u8) -> Box<Any + Send + 'static> {
107     // The `ptr` here actually corresponds to the code of the exception, and our
108     // real data is stored in our thread local.
109     rtassert!(ptr as DWORD == RUST_PANIC);
110
111     let data = PANIC_DATA.get() as *mut Box<Any + Send + 'static>;
112     PANIC_DATA.set(ptr::null_mut());
113     rtassert!(!data.is_null());
114
115     *Box::from_raw(data)
116 }
117
118 // This is required by the compiler to exist (e.g. it's a lang item), but it's
119 // never actually called by the compiler because __C_specific_handler is the
120 // personality function that is always used. Hence this is just an aborting
121 // stub.
122 #[lang = "eh_personality"]
123 fn rust_eh_personality() {
124     unsafe { ::intrinsics::abort() }
125 }
126
127 // This is a function referenced from `rust_try_msvc_64.ll` which is used to
128 // filter the exceptions being caught by that function.
129 //
130 // In theory local variables can be accessed through the `rbp` parameter of this
131 // function, but a comment in an LLVM test case indicates that this is not
132 // implemented in LLVM, so this is just an idempotent function which doesn't
133 // ferry along any other information.
134 //
135 // This function just takes a look at the current EXCEPTION_RECORD being thrown
136 // to ensure that it's code is RUST_PANIC, which was set by the call to
137 // `RaiseException` above in the `panic` function.
138 #[lang = "msvc_try_filter"]
139 #[linkage = "external"]
140 #[allow(private_no_mangle_fns)]
141 extern fn __rust_try_filter(eh_ptrs: *mut EXCEPTION_POINTERS,
142                             _rbp: *mut u8) -> i32 {
143     unsafe {
144         ((*(*eh_ptrs).ExceptionRecord).ExceptionCode == RUST_PANIC) as i32
145     }
146 }