Skip to content

Commit f5222fb

Browse files
committed
std: Implement aborting stubs for MSVC unwinding
At this time unwinding support is not implemented for MSVC as `libgcc_s_seh-1.dll` is not available by default (and this is used on MinGW), but this should be investigated soon. For now this change is just aimed at getting the compiler far enough to bootstrap everything instead of successfully running tests. This commit refactors the `std::rt::unwind` module a bit to prepare for SEH support eventually by moving all GCC-specific functionality to its own submodule and defining the interface needed.
1 parent 847c852 commit f5222fb

File tree

3 files changed

+389
-328
lines changed

3 files changed

+389
-328
lines changed

src/libstd/rt/unwind/gcc.rs

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

0 commit comments

Comments
 (0)