]>
Commit | Line | Data |
---|---|---|
c1a9b12d SL |
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 | ||
a7813a04 | 17 | use alloc::boxed::Box; |
c1a9b12d | 18 | |
a7813a04 XL |
19 | use core::any::Any; |
20 | use core::intrinsics; | |
5bcae85e SL |
21 | use core::ptr; |
22 | use dwarf::eh::{EHContext, EHAction, find_eh_action}; | |
a7813a04 | 23 | use windows as c; |
c1a9b12d SL |
24 | |
25 | // Define our exception codes: | |
26 | // according to http://msdn.microsoft.com/en-us/library/het71c37(v=VS.80).aspx, | |
27 | // [31:30] = 3 (error), 2 (warning), 1 (info), 0 (success) | |
28 | // [29] = 1 (user-defined) | |
29 | // [28] = 0 (reserved) | |
30 | // we define bits: | |
31 | // [24:27] = type | |
32 | // [0:23] = magic | |
92a42be0 SL |
33 | const ETYPE: c::DWORD = 0b1110_u32 << 28; |
34 | const MAGIC: c::DWORD = 0x525354; // "RST" | |
c1a9b12d | 35 | |
3157f602 | 36 | const RUST_PANIC: c::DWORD = ETYPE | (1 << 24) | MAGIC; |
c1a9b12d SL |
37 | |
38 | #[repr(C)] | |
39 | struct PanicData { | |
3157f602 | 40 | data: Box<Any + Send>, |
c1a9b12d SL |
41 | } |
42 | ||
a7813a04 | 43 | pub unsafe fn panic(data: Box<Any + Send>) -> u32 { |
c1a9b12d | 44 | let panic_ctx = Box::new(PanicData { data: data }); |
92a42be0 SL |
45 | let params = [Box::into_raw(panic_ctx) as c::ULONG_PTR]; |
46 | c::RaiseException(RUST_PANIC, | |
47 | c::EXCEPTION_NONCONTINUABLE, | |
48 | params.len() as c::DWORD, | |
49 | ¶ms as *const c::ULONG_PTR); | |
a7813a04 | 50 | u32::max_value() |
c1a9b12d SL |
51 | } |
52 | ||
7453a54e | 53 | pub fn payload() -> *mut u8 { |
5bcae85e | 54 | ptr::null_mut() |
7453a54e SL |
55 | } |
56 | ||
a7813a04 | 57 | pub unsafe fn cleanup(ptr: *mut u8) -> Box<Any + Send> { |
c1a9b12d SL |
58 | let panic_ctx = Box::from_raw(ptr as *mut PanicData); |
59 | return panic_ctx.data; | |
60 | } | |
61 | ||
62 | // SEH doesn't support resuming unwinds after calling a landing pad like | |
63 | // libunwind does. For this reason, MSVC compiler outlines landing pads into | |
64 | // separate functions that can be called directly from the personality function | |
65 | // but are nevertheless able to find and modify stack frame of the "parent" | |
66 | // function. | |
67 | // | |
68 | // Since this cannot be done with libdwarf-style landing pads, | |
69 | // rust_eh_personality instead catches RUST_PANICs, runs the landing pad, then | |
70 | // reraises the exception. | |
71 | // | |
72 | // Note that it makes certain assumptions about the exception: | |
73 | // | |
74 | // 1. That RUST_PANIC is non-continuable, so no lower stack frame may choose to | |
75 | // resume execution. | |
76 | // 2. That the first parameter of the exception is a pointer to an extra data | |
77 | // area (PanicData). | |
78 | // Since these assumptions do not generally hold true for foreign exceptions | |
79 | // (system faults, C++ exceptions, etc), we make no attempt to invoke our | |
80 | // landing pads (and, thus, destructors!) for anything other than RUST_PANICs. | |
81 | // This is considered acceptable, because the behavior of throwing exceptions | |
82 | // through a C ABI boundary is undefined. | |
83 | ||
5bcae85e SL |
84 | // *** Delete after a new snapshot *** |
85 | #[cfg(stage0)] | |
c1a9b12d SL |
86 | #[lang = "eh_personality_catch"] |
87 | #[cfg(not(test))] | |
3157f602 XL |
88 | unsafe extern "C" fn rust_eh_personality_catch(exceptionRecord: *mut c::EXCEPTION_RECORD, |
89 | establisherFrame: c::LPVOID, | |
90 | contextRecord: *mut c::CONTEXT, | |
91 | dispatcherContext: *mut c::DISPATCHER_CONTEXT) | |
92 | -> c::EXCEPTION_DISPOSITION { | |
93 | rust_eh_personality(exceptionRecord, | |
94 | establisherFrame, | |
95 | contextRecord, | |
96 | dispatcherContext) | |
c1a9b12d SL |
97 | } |
98 | ||
99 | #[lang = "eh_personality"] | |
100 | #[cfg(not(test))] | |
3157f602 XL |
101 | unsafe extern "C" fn rust_eh_personality(exceptionRecord: *mut c::EXCEPTION_RECORD, |
102 | establisherFrame: c::LPVOID, | |
103 | contextRecord: *mut c::CONTEXT, | |
104 | dispatcherContext: *mut c::DISPATCHER_CONTEXT) | |
105 | -> c::EXCEPTION_DISPOSITION { | |
c1a9b12d SL |
106 | let er = &*exceptionRecord; |
107 | let dc = &*dispatcherContext; | |
c1a9b12d | 108 | |
3157f602 XL |
109 | if er.ExceptionFlags & c::EXCEPTION_UNWIND == 0 { |
110 | // we are in the dispatch phase | |
c1a9b12d SL |
111 | if er.ExceptionCode == RUST_PANIC { |
112 | if let Some(lpad) = find_landing_pad(dc) { | |
92a42be0 SL |
113 | c::RtlUnwindEx(establisherFrame, |
114 | lpad as c::LPVOID, | |
115 | exceptionRecord, | |
116 | er.ExceptionInformation[0] as c::LPVOID, // pointer to PanicData | |
117 | contextRecord, | |
118 | dc.HistoryTable); | |
c1a9b12d SL |
119 | } |
120 | } | |
121 | } | |
92a42be0 | 122 | c::ExceptionContinueSearch |
c1a9b12d SL |
123 | } |
124 | ||
92a42be0 | 125 | #[lang = "eh_unwind_resume"] |
e9174d1e | 126 | #[unwind] |
3157f602 | 127 | unsafe extern "C" fn rust_eh_unwind_resume(panic_ctx: c::LPVOID) -> ! { |
92a42be0 SL |
128 | let params = [panic_ctx as c::ULONG_PTR]; |
129 | c::RaiseException(RUST_PANIC, | |
130 | c::EXCEPTION_NONCONTINUABLE, | |
131 | params.len() as c::DWORD, | |
132 | ¶ms as *const c::ULONG_PTR); | |
a7813a04 | 133 | intrinsics::abort(); |
c1a9b12d SL |
134 | } |
135 | ||
92a42be0 | 136 | unsafe fn find_landing_pad(dc: &c::DISPATCHER_CONTEXT) -> Option<usize> { |
5bcae85e SL |
137 | let eh_ctx = EHContext { |
138 | // The return address points 1 byte past the call instruction, | |
139 | // which could be in the next IP range in LSDA range table. | |
140 | ip: dc.ControlPc as usize - 1, | |
c1a9b12d | 141 | func_start: dc.ImageBase as usize + (*dc.FunctionEntry).BeginAddress as usize, |
5bcae85e SL |
142 | get_text_start: &|| dc.ImageBase as usize, |
143 | get_data_start: &|| unimplemented!(), | |
c1a9b12d | 144 | }; |
5bcae85e SL |
145 | match find_eh_action(dc.HandlerData, &eh_ctx) { |
146 | EHAction::None => None, | |
147 | EHAction::Cleanup(lpad) | EHAction::Catch(lpad) => Some(lpad), | |
148 | EHAction::Terminate => intrinsics::abort(), | |
149 | } | |
c1a9b12d | 150 | } |