]> git.proxmox.com Git - rustc.git/blob - src/libstd/rt/unwind/gcc.rs
Imported Upstream version 1.2.0+dfsg1
[rustc.git] / src / libstd / rt / unwind / gcc.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 use prelude::v1::*;
12
13 use any::Any;
14 use libc::c_void;
15 use rt::libunwind as uw;
16
17 struct Exception {
18 uwe: uw::_Unwind_Exception,
19 cause: Option<Box<Any + Send + 'static>>,
20 }
21
22 pub unsafe fn panic(data: Box<Any + Send + 'static>) -> ! {
23 let exception: Box<_> = box Exception {
24 uwe: uw::_Unwind_Exception {
25 exception_class: rust_exception_class(),
26 exception_cleanup: exception_cleanup,
27 private: [0; uw::unwinder_private_data_size],
28 },
29 cause: Some(data),
30 };
31 let exception_param = Box::into_raw(exception) as *mut uw::_Unwind_Exception;
32 let error = uw::_Unwind_RaiseException(exception_param);
33 rtabort!("Could not unwind stack, error = {}", error as isize);
34
35 extern fn exception_cleanup(_unwind_code: uw::_Unwind_Reason_Code,
36 exception: *mut uw::_Unwind_Exception) {
37 rtdebug!("exception_cleanup()");
38 unsafe {
39 let _: Box<Exception> = Box::from_raw(exception as *mut Exception);
40 }
41 }
42 }
43
44 pub unsafe fn cleanup(ptr: *mut c_void) -> Box<Any + Send + 'static> {
45 let my_ep = ptr as *mut Exception;
46 rtdebug!("caught {}", (*my_ep).uwe.exception_class);
47 let cause = (*my_ep).cause.take();
48 uw::_Unwind_DeleteException(ptr as *mut _);
49 cause.unwrap()
50 }
51
52 // Rust's exception class identifier. This is used by personality routines to
53 // determine whether the exception was thrown by their own runtime.
54 fn rust_exception_class() -> uw::_Unwind_Exception_Class {
55 // M O Z \0 R U S T -- vendor, language
56 0x4d4f5a_00_52555354
57 }
58
59 // We could implement our personality routine in pure Rust, however exception
60 // info decoding is tedious. More importantly, personality routines have to
61 // handle various platform quirks, which are not fun to maintain. For this
62 // reason, we attempt to reuse personality routine of the C language:
63 // __gcc_personality_v0.
64 //
65 // Since C does not support exception catching, __gcc_personality_v0 simply
66 // always returns _URC_CONTINUE_UNWIND in search phase, and always returns
67 // _URC_INSTALL_CONTEXT (i.e. "invoke cleanup code") in cleanup phase.
68 //
69 // This is pretty close to Rust's exception handling approach, except that Rust
70 // does have a single "catch-all" handler at the bottom of each thread's stack.
71 // So we have two versions of the personality routine:
72 // - rust_eh_personality, used by all cleanup landing pads, which never catches,
73 // so the behavior of __gcc_personality_v0 is perfectly adequate there, and
74 // - rust_eh_personality_catch, used only by rust_try(), which always catches.
75 //
76 // Note, however, that for implementation simplicity, rust_eh_personality_catch
77 // lacks code to install a landing pad, so in order to obtain exception object
78 // pointer (which it needs to return upstream), rust_try() employs another trick:
79 // it calls into the nested rust_try_inner(), whose landing pad does not resume
80 // unwinds. Instead, it extracts the exception pointer and performs a "normal"
81 // return.
82 //
83 // See also: rt/rust_try.ll
84
85 #[cfg(all(not(target_arch = "arm"),
86 not(all(windows, target_arch = "x86_64")),
87 not(test)))]
88 pub mod eabi {
89 use rt::libunwind as uw;
90 use libc::c_int;
91
92 extern "C" {
93 fn __gcc_personality_v0(version: c_int,
94 actions: uw::_Unwind_Action,
95 exception_class: uw::_Unwind_Exception_Class,
96 ue_header: *mut uw::_Unwind_Exception,
97 context: *mut uw::_Unwind_Context)
98 -> uw::_Unwind_Reason_Code;
99 }
100
101 #[lang="eh_personality"]
102 #[no_mangle] // referenced from rust_try.ll
103 #[allow(private_no_mangle_fns)]
104 extern fn rust_eh_personality(
105 version: c_int,
106 actions: uw::_Unwind_Action,
107 exception_class: uw::_Unwind_Exception_Class,
108 ue_header: *mut uw::_Unwind_Exception,
109 context: *mut uw::_Unwind_Context
110 ) -> uw::_Unwind_Reason_Code
111 {
112 unsafe {
113 __gcc_personality_v0(version, actions, exception_class, ue_header,
114 context)
115 }
116 }
117
118 #[no_mangle] // referenced from rust_try.ll
119 pub extern "C" fn rust_eh_personality_catch(
120 _version: c_int,
121 actions: uw::_Unwind_Action,
122 _exception_class: uw::_Unwind_Exception_Class,
123 _ue_header: *mut uw::_Unwind_Exception,
124 _context: *mut uw::_Unwind_Context
125 ) -> uw::_Unwind_Reason_Code
126 {
127
128 if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase
129 uw::_URC_HANDLER_FOUND // catch!
130 }
131 else { // cleanup phase
132 uw::_URC_INSTALL_CONTEXT
133 }
134 }
135 }
136
137 // iOS on armv7 is using SjLj exceptions and therefore requires to use
138 // a specialized personality routine: __gcc_personality_sj0
139
140 #[cfg(all(target_os = "ios", target_arch = "arm", not(test)))]
141 pub mod eabi {
142 use rt::libunwind as uw;
143 use libc::c_int;
144
145 extern "C" {
146 fn __gcc_personality_sj0(version: c_int,
147 actions: uw::_Unwind_Action,
148 exception_class: uw::_Unwind_Exception_Class,
149 ue_header: *mut uw::_Unwind_Exception,
150 context: *mut uw::_Unwind_Context)
151 -> uw::_Unwind_Reason_Code;
152 }
153
154 #[lang="eh_personality"]
155 #[no_mangle] // referenced from rust_try.ll
156 pub extern "C" fn rust_eh_personality(
157 version: c_int,
158 actions: uw::_Unwind_Action,
159 exception_class: uw::_Unwind_Exception_Class,
160 ue_header: *mut uw::_Unwind_Exception,
161 context: *mut uw::_Unwind_Context
162 ) -> uw::_Unwind_Reason_Code
163 {
164 unsafe {
165 __gcc_personality_sj0(version, actions, exception_class, ue_header,
166 context)
167 }
168 }
169
170 #[no_mangle] // referenced from rust_try.ll
171 pub extern "C" fn rust_eh_personality_catch(
172 _version: c_int,
173 actions: uw::_Unwind_Action,
174 _exception_class: uw::_Unwind_Exception_Class,
175 _ue_header: *mut uw::_Unwind_Exception,
176 _context: *mut uw::_Unwind_Context
177 ) -> uw::_Unwind_Reason_Code
178 {
179 if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase
180 uw::_URC_HANDLER_FOUND // catch!
181 }
182 else { // cleanup phase
183 unsafe {
184 __gcc_personality_sj0(_version, actions, _exception_class, _ue_header,
185 _context)
186 }
187 }
188 }
189 }
190
191
192 // ARM EHABI uses a slightly different personality routine signature,
193 // but otherwise works the same.
194 #[cfg(all(target_arch = "arm", not(target_os = "ios"), not(test)))]
195 pub mod eabi {
196 use rt::libunwind as uw;
197 use libc::c_int;
198
199 extern "C" {
200 fn __gcc_personality_v0(state: uw::_Unwind_State,
201 ue_header: *mut uw::_Unwind_Exception,
202 context: *mut uw::_Unwind_Context)
203 -> uw::_Unwind_Reason_Code;
204 }
205
206 #[lang="eh_personality"]
207 #[no_mangle] // referenced from rust_try.ll
208 #[allow(private_no_mangle_fns)]
209 extern "C" fn rust_eh_personality(
210 state: uw::_Unwind_State,
211 ue_header: *mut uw::_Unwind_Exception,
212 context: *mut uw::_Unwind_Context
213 ) -> uw::_Unwind_Reason_Code
214 {
215 unsafe {
216 __gcc_personality_v0(state, ue_header, context)
217 }
218 }
219
220 #[no_mangle] // referenced from rust_try.ll
221 pub extern "C" fn rust_eh_personality_catch(
222 state: uw::_Unwind_State,
223 _ue_header: *mut uw::_Unwind_Exception,
224 _context: *mut uw::_Unwind_Context
225 ) -> uw::_Unwind_Reason_Code
226 {
227 if (state as c_int & uw::_US_ACTION_MASK as c_int)
228 == uw::_US_VIRTUAL_UNWIND_FRAME as c_int { // search phase
229 uw::_URC_HANDLER_FOUND // catch!
230 }
231 else { // cleanup phase
232 uw::_URC_INSTALL_CONTEXT
233 }
234 }
235 }
236
237 // Win64 SEH (see http://msdn.microsoft.com/en-us/library/1eyas8tf.aspx)
238 //
239 // This looks a bit convoluted because rather than implementing a native SEH
240 // handler, GCC reuses the same personality routine as for the other
241 // architectures by wrapping it with an "API translator" layer
242 // (_GCC_specific_handler).
243
244 #[cfg(all(windows, target_arch = "x86_64", not(test)))]
245 #[doc(hidden)]
246 #[allow(non_camel_case_types, non_snake_case)]
247 pub mod eabi {
248 pub use self::EXCEPTION_DISPOSITION::*;
249 use rt::libunwind as uw;
250 use libc::{c_void, c_int};
251
252 #[repr(C)]
253 pub struct EXCEPTION_RECORD;
254 #[repr(C)]
255 pub struct CONTEXT;
256 #[repr(C)]
257 pub struct DISPATCHER_CONTEXT;
258
259 #[repr(C)]
260 #[derive(Copy, Clone)]
261 pub enum EXCEPTION_DISPOSITION {
262 ExceptionContinueExecution,
263 ExceptionContinueSearch,
264 ExceptionNestedException,
265 ExceptionCollidedUnwind
266 }
267
268 type _Unwind_Personality_Fn =
269 extern "C" fn(
270 version: c_int,
271 actions: uw::_Unwind_Action,
272 exception_class: uw::_Unwind_Exception_Class,
273 ue_header: *mut uw::_Unwind_Exception,
274 context: *mut uw::_Unwind_Context
275 ) -> uw::_Unwind_Reason_Code;
276
277 extern "C" {
278 fn __gcc_personality_seh0(
279 exceptionRecord: *mut EXCEPTION_RECORD,
280 establisherFrame: *mut c_void,
281 contextRecord: *mut CONTEXT,
282 dispatcherContext: *mut DISPATCHER_CONTEXT
283 ) -> EXCEPTION_DISPOSITION;
284
285 fn _GCC_specific_handler(
286 exceptionRecord: *mut EXCEPTION_RECORD,
287 establisherFrame: *mut c_void,
288 contextRecord: *mut CONTEXT,
289 dispatcherContext: *mut DISPATCHER_CONTEXT,
290 personality: _Unwind_Personality_Fn
291 ) -> EXCEPTION_DISPOSITION;
292 }
293
294 #[lang="eh_personality"]
295 #[no_mangle] // referenced from rust_try.ll
296 #[allow(private_no_mangle_fns)]
297 extern "C" fn rust_eh_personality(
298 exceptionRecord: *mut EXCEPTION_RECORD,
299 establisherFrame: *mut c_void,
300 contextRecord: *mut CONTEXT,
301 dispatcherContext: *mut DISPATCHER_CONTEXT
302 ) -> EXCEPTION_DISPOSITION
303 {
304 unsafe {
305 __gcc_personality_seh0(exceptionRecord, establisherFrame,
306 contextRecord, dispatcherContext)
307 }
308 }
309
310 #[no_mangle] // referenced from rust_try.ll
311 pub extern "C" fn rust_eh_personality_catch(
312 exceptionRecord: *mut EXCEPTION_RECORD,
313 establisherFrame: *mut c_void,
314 contextRecord: *mut CONTEXT,
315 dispatcherContext: *mut DISPATCHER_CONTEXT
316 ) -> EXCEPTION_DISPOSITION
317 {
318 extern "C" fn inner(
319 _version: c_int,
320 actions: uw::_Unwind_Action,
321 _exception_class: uw::_Unwind_Exception_Class,
322 _ue_header: *mut uw::_Unwind_Exception,
323 _context: *mut uw::_Unwind_Context
324 ) -> uw::_Unwind_Reason_Code
325 {
326 if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase
327 uw::_URC_HANDLER_FOUND // catch!
328 }
329 else { // cleanup phase
330 uw::_URC_INSTALL_CONTEXT
331 }
332 }
333
334 unsafe {
335 _GCC_specific_handler(exceptionRecord, establisherFrame,
336 contextRecord, dispatcherContext,
337 inner)
338 }
339 }
340 }
341