Skip to content

Commit 0cbc93f

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 8d85566 commit 0cbc93f

File tree

3 files changed

+272
-328
lines changed

3 files changed

+272
-328
lines changed

src/libstd/rt/unwind/gcc.rs

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
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"), not(test)))]
87+
pub mod eabi {
88+
use rt::libunwind as uw;
89+
use libc::c_int;
90+
91+
extern "C" {
92+
fn __gcc_personality_v0(version: c_int,
93+
actions: uw::_Unwind_Action,
94+
exception_class: uw::_Unwind_Exception_Class,
95+
ue_header: *mut uw::_Unwind_Exception,
96+
context: *mut uw::_Unwind_Context)
97+
-> uw::_Unwind_Reason_Code;
98+
}
99+
100+
#[lang="eh_personality"]
101+
#[no_mangle] // referenced from rust_try.ll
102+
#[allow(private_no_mangle_fns)]
103+
extern fn rust_eh_personality(
104+
version: c_int,
105+
actions: uw::_Unwind_Action,
106+
exception_class: uw::_Unwind_Exception_Class,
107+
ue_header: *mut uw::_Unwind_Exception,
108+
context: *mut uw::_Unwind_Context
109+
) -> uw::_Unwind_Reason_Code
110+
{
111+
unsafe {
112+
__gcc_personality_v0(version, actions, exception_class, ue_header,
113+
context)
114+
}
115+
}
116+
117+
#[no_mangle] // referenced from rust_try.ll
118+
pub extern "C" fn rust_eh_personality_catch(
119+
_version: c_int,
120+
actions: uw::_Unwind_Action,
121+
_exception_class: uw::_Unwind_Exception_Class,
122+
_ue_header: *mut uw::_Unwind_Exception,
123+
_context: *mut uw::_Unwind_Context
124+
) -> uw::_Unwind_Reason_Code
125+
{
126+
127+
if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase
128+
uw::_URC_HANDLER_FOUND // catch!
129+
}
130+
else { // cleanup phase
131+
uw::_URC_INSTALL_CONTEXT
132+
}
133+
}
134+
}
135+
136+
// iOS on armv7 is using SjLj exceptions and therefore requires to use
137+
// a specialized personality routine: __gcc_personality_sj0
138+
139+
#[cfg(all(target_os = "ios", target_arch = "arm", not(test)))]
140+
pub mod eabi {
141+
use rt::libunwind as uw;
142+
use libc::c_int;
143+
144+
extern "C" {
145+
fn __gcc_personality_sj0(version: c_int,
146+
actions: uw::_Unwind_Action,
147+
exception_class: uw::_Unwind_Exception_Class,
148+
ue_header: *mut uw::_Unwind_Exception,
149+
context: *mut uw::_Unwind_Context)
150+
-> uw::_Unwind_Reason_Code;
151+
}
152+
153+
#[lang="eh_personality"]
154+
#[no_mangle] // referenced from rust_try.ll
155+
pub extern "C" fn rust_eh_personality(
156+
version: c_int,
157+
actions: uw::_Unwind_Action,
158+
exception_class: uw::_Unwind_Exception_Class,
159+
ue_header: *mut uw::_Unwind_Exception,
160+
context: *mut uw::_Unwind_Context
161+
) -> uw::_Unwind_Reason_Code
162+
{
163+
unsafe {
164+
__gcc_personality_sj0(version, actions, exception_class, ue_header,
165+
context)
166+
}
167+
}
168+
169+
#[no_mangle] // referenced from rust_try.ll
170+
pub extern "C" fn rust_eh_personality_catch(
171+
_version: c_int,
172+
actions: uw::_Unwind_Action,
173+
_exception_class: uw::_Unwind_Exception_Class,
174+
_ue_header: *mut uw::_Unwind_Exception,
175+
_context: *mut uw::_Unwind_Context
176+
) -> uw::_Unwind_Reason_Code
177+
{
178+
if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase
179+
uw::_URC_HANDLER_FOUND // catch!
180+
}
181+
else { // cleanup phase
182+
unsafe {
183+
__gcc_personality_sj0(_version, actions, _exception_class, _ue_header,
184+
_context)
185+
}
186+
}
187+
}
188+
}
189+
190+
191+
// ARM EHABI uses a slightly different personality routine signature,
192+
// but otherwise works the same.
193+
#[cfg(all(target_arch = "arm", not(target_os = "ios"), not(test)))]
194+
pub mod eabi {
195+
use rt::libunwind as uw;
196+
use libc::c_int;
197+
198+
extern "C" {
199+
fn __gcc_personality_v0(state: uw::_Unwind_State,
200+
ue_header: *mut uw::_Unwind_Exception,
201+
context: *mut uw::_Unwind_Context)
202+
-> uw::_Unwind_Reason_Code;
203+
}
204+
205+
#[lang="eh_personality"]
206+
#[no_mangle] // referenced from rust_try.ll
207+
#[allow(private_no_mangle_fns)]
208+
extern "C" fn rust_eh_personality(
209+
state: uw::_Unwind_State,
210+
ue_header: *mut uw::_Unwind_Exception,
211+
context: *mut uw::_Unwind_Context
212+
) -> uw::_Unwind_Reason_Code
213+
{
214+
unsafe {
215+
__gcc_personality_v0(state, ue_header, context)
216+
}
217+
}
218+
219+
#[no_mangle] // referenced from rust_try.ll
220+
pub extern "C" fn rust_eh_personality_catch(
221+
state: uw::_Unwind_State,
222+
_ue_header: *mut uw::_Unwind_Exception,
223+
_context: *mut uw::_Unwind_Context
224+
) -> uw::_Unwind_Reason_Code
225+
{
226+
if (state as c_int & uw::_US_ACTION_MASK as c_int)
227+
== uw::_US_VIRTUAL_UNWIND_FRAME as c_int { // search phase
228+
uw::_URC_HANDLER_FOUND // catch!
229+
}
230+
else { // cleanup phase
231+
uw::_URC_INSTALL_CONTEXT
232+
}
233+
}
234+
}

0 commit comments

Comments
 (0)