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