]>
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 | ||
17 | use prelude::v1::*; | |
18 | ||
19 | use any::Any; | |
e9174d1e | 20 | use sys_common::dwarf::eh; |
c1a9b12d SL |
21 | use core::mem; |
22 | use core::ptr; | |
92a42be0 | 23 | use sys::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 | |
92a42be0 | 36 | const RUST_PANIC: c::DWORD = ETYPE | (1 << 24) | MAGIC; |
c1a9b12d SL |
37 | |
38 | #[repr(C)] | |
39 | struct PanicData { | |
40 | data: Box<Any + Send + 'static> | |
41 | } | |
42 | ||
43 | pub unsafe fn panic(data: Box<Any + Send + 'static>) -> ! { | |
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); | |
c1a9b12d SL |
50 | rtabort!("could not unwind stack"); |
51 | } | |
52 | ||
53 | pub unsafe fn cleanup(ptr: *mut u8) -> Box<Any + Send + 'static> { | |
c1a9b12d SL |
54 | let panic_ctx = Box::from_raw(ptr as *mut PanicData); |
55 | return panic_ctx.data; | |
56 | } | |
57 | ||
58 | // SEH doesn't support resuming unwinds after calling a landing pad like | |
59 | // libunwind does. For this reason, MSVC compiler outlines landing pads into | |
60 | // separate functions that can be called directly from the personality function | |
61 | // but are nevertheless able to find and modify stack frame of the "parent" | |
62 | // function. | |
63 | // | |
64 | // Since this cannot be done with libdwarf-style landing pads, | |
65 | // rust_eh_personality instead catches RUST_PANICs, runs the landing pad, then | |
66 | // reraises the exception. | |
67 | // | |
68 | // Note that it makes certain assumptions about the exception: | |
69 | // | |
70 | // 1. That RUST_PANIC is non-continuable, so no lower stack frame may choose to | |
71 | // resume execution. | |
72 | // 2. That the first parameter of the exception is a pointer to an extra data | |
73 | // area (PanicData). | |
74 | // Since these assumptions do not generally hold true for foreign exceptions | |
75 | // (system faults, C++ exceptions, etc), we make no attempt to invoke our | |
76 | // landing pads (and, thus, destructors!) for anything other than RUST_PANICs. | |
77 | // This is considered acceptable, because the behavior of throwing exceptions | |
78 | // through a C ABI boundary is undefined. | |
79 | ||
80 | #[lang = "eh_personality_catch"] | |
81 | #[cfg(not(test))] | |
82 | unsafe extern fn rust_eh_personality_catch( | |
92a42be0 SL |
83 | exceptionRecord: *mut c::EXCEPTION_RECORD, |
84 | establisherFrame: c::LPVOID, | |
85 | contextRecord: *mut c::CONTEXT, | |
86 | dispatcherContext: *mut c::DISPATCHER_CONTEXT | |
87 | ) -> c::EXCEPTION_DISPOSITION | |
c1a9b12d SL |
88 | { |
89 | rust_eh_personality(exceptionRecord, establisherFrame, | |
90 | contextRecord, dispatcherContext) | |
91 | } | |
92 | ||
93 | #[lang = "eh_personality"] | |
94 | #[cfg(not(test))] | |
95 | unsafe extern fn rust_eh_personality( | |
92a42be0 SL |
96 | exceptionRecord: *mut c::EXCEPTION_RECORD, |
97 | establisherFrame: c::LPVOID, | |
98 | contextRecord: *mut c::CONTEXT, | |
99 | dispatcherContext: *mut c::DISPATCHER_CONTEXT | |
100 | ) -> c::EXCEPTION_DISPOSITION | |
c1a9b12d SL |
101 | { |
102 | let er = &*exceptionRecord; | |
103 | let dc = &*dispatcherContext; | |
c1a9b12d | 104 | |
92a42be0 | 105 | if er.ExceptionFlags & c::EXCEPTION_UNWIND == 0 { // we are in the dispatch phase |
c1a9b12d SL |
106 | if er.ExceptionCode == RUST_PANIC { |
107 | if let Some(lpad) = find_landing_pad(dc) { | |
92a42be0 SL |
108 | c::RtlUnwindEx(establisherFrame, |
109 | lpad as c::LPVOID, | |
110 | exceptionRecord, | |
111 | er.ExceptionInformation[0] as c::LPVOID, // pointer to PanicData | |
112 | contextRecord, | |
113 | dc.HistoryTable); | |
c1a9b12d SL |
114 | rtabort!("could not unwind"); |
115 | } | |
116 | } | |
117 | } | |
92a42be0 | 118 | c::ExceptionContinueSearch |
c1a9b12d SL |
119 | } |
120 | ||
c1a9b12d | 121 | #[cfg(not(test))] |
92a42be0 | 122 | #[lang = "eh_unwind_resume"] |
e9174d1e | 123 | #[unwind] |
92a42be0 SL |
124 | unsafe extern fn rust_eh_unwind_resume(panic_ctx: c::LPVOID) -> ! { |
125 | let params = [panic_ctx as c::ULONG_PTR]; | |
126 | c::RaiseException(RUST_PANIC, | |
127 | c::EXCEPTION_NONCONTINUABLE, | |
128 | params.len() as c::DWORD, | |
129 | ¶ms as *const c::ULONG_PTR); | |
c1a9b12d SL |
130 | rtabort!("could not resume unwind"); |
131 | } | |
132 | ||
92a42be0 | 133 | unsafe fn find_landing_pad(dc: &c::DISPATCHER_CONTEXT) -> Option<usize> { |
c1a9b12d SL |
134 | let eh_ctx = eh::EHContext { |
135 | ip: dc.ControlPc as usize, | |
136 | func_start: dc.ImageBase as usize + (*dc.FunctionEntry).BeginAddress as usize, | |
137 | text_start: dc.ImageBase as usize, | |
138 | data_start: 0 | |
139 | }; | |
140 | eh::find_landing_pad(dc.HandlerData, &eh_ctx) | |
141 | } |