]> git.lizzy.rs Git - rust.git/blob - src/libstd/rt/unwind/seh64_gnu.rs
Better FFI discipline
[rust.git] / src / libstd / rt / unwind / seh64_gnu.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 //! Unwinding implementation of top of native Win64 SEH,
12 //! however the unwind handler data (aka LSDA) uses GCC-compatible encoding.
13
14 #![allow(bad_style)]
15 #![allow(private_no_mangle_fns)]
16
17 use prelude::v1::*;
18
19 use any::Any;
20 use self::EXCEPTION_DISPOSITION::*;
21 use rt::dwarf::eh;
22 use core::mem;
23 use core::ptr;
24 use simd;
25 use libc::{c_void, c_ulonglong, DWORD, LPVOID};
26 type ULONG_PTR = c_ulonglong;
27
28 // Define our exception codes:
29 // according to http://msdn.microsoft.com/en-us/library/het71c37(v=VS.80).aspx,
30 //    [31:30] = 3 (error), 2 (warning), 1 (info), 0 (success)
31 //    [29]    = 1 (user-defined)
32 //    [28]    = 0 (reserved)
33 // we define bits:
34 //    [24:27] = type
35 //    [0:23]  = magic
36 const ETYPE: DWORD = 0b1110_u32 << 28;
37 const MAGIC: DWORD = 0x525354; // "RST"
38
39 const RUST_PANIC: DWORD  = ETYPE | (1 << 24) | MAGIC;
40
41 const EXCEPTION_NONCONTINUABLE: DWORD = 0x1;   // Noncontinuable exception
42 const EXCEPTION_UNWINDING: DWORD = 0x2;        // Unwind is in progress
43 const EXCEPTION_EXIT_UNWIND: DWORD = 0x4;      // Exit unwind is in progress
44 const EXCEPTION_STACK_INVALID: DWORD = 0x8;    // Stack out of limits or unaligned
45 const EXCEPTION_NESTED_CALL: DWORD = 0x10;     // Nested exception handler call
46 const EXCEPTION_TARGET_UNWIND: DWORD = 0x20;   // Target unwind in progress
47 const EXCEPTION_COLLIDED_UNWIND: DWORD = 0x40; // Collided exception handler call
48 const EXCEPTION_UNWIND: DWORD = EXCEPTION_UNWINDING |
49                                 EXCEPTION_EXIT_UNWIND |
50                                 EXCEPTION_TARGET_UNWIND |
51                                 EXCEPTION_COLLIDED_UNWIND;
52
53 #[repr(C)]
54 pub struct EXCEPTION_RECORD {
55     ExceptionCode: DWORD,
56     ExceptionFlags: DWORD,
57     ExceptionRecord: *const EXCEPTION_RECORD,
58     ExceptionAddress: LPVOID,
59     NumberParameters: DWORD,
60     ExceptionInformation: [ULONG_PTR; 15],
61 }
62
63 pub enum CONTEXT {}
64 pub enum UNWIND_HISTORY_TABLE {}
65
66 #[repr(C)]
67 pub struct RUNTIME_FUNCTION {
68     BeginAddress: DWORD,
69     EndAddress: DWORD,
70     UnwindData: DWORD,
71 }
72
73 #[repr(C)]
74 pub struct DISPATCHER_CONTEXT {
75     ControlPc: LPVOID,
76     ImageBase: LPVOID,
77     FunctionEntry: *const RUNTIME_FUNCTION,
78     EstablisherFrame: LPVOID,
79     TargetIp: LPVOID,
80     ContextRecord: *const CONTEXT,
81     LanguageHandler: LPVOID,
82     HandlerData: *const u8,
83     HistoryTable: *const UNWIND_HISTORY_TABLE,
84 }
85
86 #[repr(C)]
87 #[derive(Copy, Clone)]
88 pub enum EXCEPTION_DISPOSITION {
89     ExceptionContinueExecution,
90     ExceptionContinueSearch,
91     ExceptionNestedException,
92     ExceptionCollidedUnwind
93 }
94
95 // From kernel32.dll
96 extern "system" {
97     fn RaiseException(dwExceptionCode: DWORD,
98                       dwExceptionFlags: DWORD,
99                       nNumberOfArguments: DWORD,
100                       lpArguments: *const ULONG_PTR);
101
102     fn RtlUnwindEx(TargetFrame: LPVOID,
103                    TargetIp: LPVOID,
104                    ExceptionRecord: *const EXCEPTION_RECORD,
105                    ReturnValue: LPVOID,
106                    OriginalContext: *const CONTEXT,
107                    HistoryTable: *const UNWIND_HISTORY_TABLE);
108 }
109
110 #[repr(C)]
111 struct PanicData {
112     data: Box<Any + Send + 'static>
113 }
114
115 pub unsafe fn panic(data: Box<Any + Send + 'static>) -> ! {
116     let panic_ctx = Box::new(PanicData { data: data });
117     let params = [Box::into_raw(panic_ctx) as ULONG_PTR];
118     rtdebug!("panic: ctx={:X}", params[0]);
119     RaiseException(RUST_PANIC,
120                    EXCEPTION_NONCONTINUABLE,
121                    params.len() as DWORD,
122                    &params as *const ULONG_PTR);
123     rtabort!("could not unwind stack");
124 }
125
126 pub unsafe fn cleanup(ptr: *mut u8) -> Box<Any + Send + 'static> {
127     rtdebug!("cleanup: ctx={:X}", ptr as usize);
128     let panic_ctx = Box::from_raw(ptr as *mut PanicData);
129     return panic_ctx.data;
130 }
131
132 // SEH doesn't support resuming unwinds after calling a landing pad like
133 // libunwind does. For this reason, MSVC compiler outlines landing pads into
134 // separate functions that can be called directly from the personality function
135 // but are nevertheless able to find and modify stack frame of the "parent"
136 // function.
137 //
138 // Since this cannot be done with libdwarf-style landing pads,
139 // rust_eh_personality instead catches RUST_PANICs, runs the landing pad, then
140 // reraises the exception.
141 //
142 // Note that it makes certain assumptions about the exception:
143 //
144 // 1. That RUST_PANIC is non-continuable, so no lower stack frame may choose to
145 //    resume execution.
146 // 2. That the first parameter of the exception is a pointer to an extra data
147 //    area (PanicData).
148 // Since these assumptions do not generally hold true for foreign exceptions
149 // (system faults, C++ exceptions, etc), we make no attempt to invoke our
150 // landing pads (and, thus, destructors!) for anything other than RUST_PANICs.
151 // This is considered acceptable, because the behavior of throwing exceptions
152 // through a C ABI boundary is undefined.
153
154 #[lang = "eh_personality_catch"]
155 #[cfg(not(test))]
156 unsafe extern fn rust_eh_personality_catch(
157     exceptionRecord: *mut EXCEPTION_RECORD,
158     establisherFrame: LPVOID,
159     contextRecord: *mut CONTEXT,
160     dispatcherContext: *mut DISPATCHER_CONTEXT
161 ) -> EXCEPTION_DISPOSITION
162 {
163     rust_eh_personality(exceptionRecord, establisherFrame,
164                         contextRecord, dispatcherContext)
165 }
166
167 #[lang = "eh_personality"]
168 #[cfg(not(test))]
169 unsafe extern fn rust_eh_personality(
170     exceptionRecord: *mut EXCEPTION_RECORD,
171     establisherFrame: LPVOID,
172     contextRecord: *mut CONTEXT,
173     dispatcherContext: *mut DISPATCHER_CONTEXT
174 ) -> EXCEPTION_DISPOSITION
175 {
176     let er = &*exceptionRecord;
177     let dc = &*dispatcherContext;
178     rtdebug!("rust_eh_personality: code={:X}, flags={:X}, frame={:X}, ip={:X}",
179         er.ExceptionCode, er.ExceptionFlags,
180         establisherFrame as usize, dc.ControlPc as usize);
181
182     if er.ExceptionFlags & EXCEPTION_UNWIND == 0 { // we are in the dispatch phase
183         if er.ExceptionCode == RUST_PANIC {
184             if let Some(lpad) = find_landing_pad(dc) {
185                 rtdebug!("unwinding to landing pad {:X}", lpad);
186
187                 RtlUnwindEx(establisherFrame,
188                             lpad as LPVOID,
189                             exceptionRecord,
190                             er.ExceptionInformation[0] as LPVOID, // pointer to PanicData
191                             contextRecord,
192                             dc.HistoryTable);
193                 rtabort!("could not unwind");
194             }
195         }
196     }
197     ExceptionContinueSearch
198 }
199
200 // The `resume` instruction, found at the end of the landing pads, and whose job
201 // is to resume stack unwinding, is typically lowered by LLVM into a call to
202 // `_Unwind_Resume` routine.  To avoid confusion with the same symbol exported
203 // from libgcc, we redirect it to `rust_eh_unwind_resume`.
204 // Since resolution of this symbol is done by the linker, `rust_eh_unwind_resume`
205 // must be marked `pub` + `#[no_mangle]`.  (Can we make it a lang item?)
206
207 #[lang = "eh_unwind_resume"]
208 #[cfg(not(test))]
209 unsafe extern fn rust_eh_unwind_resume(panic_ctx: LPVOID) {
210     rtdebug!("rust_eh_unwind_resume: ctx={:X}", panic_ctx as usize);
211     let params = [panic_ctx as ULONG_PTR];
212     RaiseException(RUST_PANIC,
213                    EXCEPTION_NONCONTINUABLE,
214                    params.len() as DWORD,
215                    &params as *const ULONG_PTR);
216     rtabort!("could not resume unwind");
217 }
218
219 unsafe fn find_landing_pad(dc: &DISPATCHER_CONTEXT) -> Option<usize> {
220     let eh_ctx = eh::EHContext {
221         ip: dc.ControlPc as usize,
222         func_start: dc.ImageBase as usize + (*dc.FunctionEntry).BeginAddress as usize,
223         text_start: dc.ImageBase as usize,
224         data_start: 0
225     };
226     eh::find_landing_pad(dc.HandlerData, &eh_ctx)
227 }