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.
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.
11 //! Unwinding implementation of top of native Win64 SEH,
12 //! however the unwind handler data (aka LSDA) uses GCC-compatible encoding.
15 #![allow(private_no_mangle_fns)]
20 use self::EXCEPTION_DISPOSITION::*;
21 use sys_common::dwarf::eh;
24 use libc::{c_void, c_ulonglong, DWORD, LPVOID};
25 type ULONG_PTR = c_ulonglong;
27 // Define our exception codes:
28 // according to http://msdn.microsoft.com/en-us/library/het71c37(v=VS.80).aspx,
29 // [31:30] = 3 (error), 2 (warning), 1 (info), 0 (success)
30 // [29] = 1 (user-defined)
31 // [28] = 0 (reserved)
35 const ETYPE: DWORD = 0b1110_u32 << 28;
36 const MAGIC: DWORD = 0x525354; // "RST"
38 const RUST_PANIC: DWORD = ETYPE | (1 << 24) | MAGIC;
40 const EXCEPTION_NONCONTINUABLE: DWORD = 0x1; // Noncontinuable exception
41 const EXCEPTION_UNWINDING: DWORD = 0x2; // Unwind is in progress
42 const EXCEPTION_EXIT_UNWIND: DWORD = 0x4; // Exit unwind is in progress
43 const EXCEPTION_STACK_INVALID: DWORD = 0x8; // Stack out of limits or unaligned
44 const EXCEPTION_NESTED_CALL: DWORD = 0x10; // Nested exception handler call
45 const EXCEPTION_TARGET_UNWIND: DWORD = 0x20; // Target unwind in progress
46 const EXCEPTION_COLLIDED_UNWIND: DWORD = 0x40; // Collided exception handler call
47 const EXCEPTION_UNWIND: DWORD = EXCEPTION_UNWINDING |
48 EXCEPTION_EXIT_UNWIND |
49 EXCEPTION_TARGET_UNWIND |
50 EXCEPTION_COLLIDED_UNWIND;
53 pub struct EXCEPTION_RECORD {
55 ExceptionFlags: DWORD,
56 ExceptionRecord: *const EXCEPTION_RECORD,
57 ExceptionAddress: LPVOID,
58 NumberParameters: DWORD,
59 ExceptionInformation: [ULONG_PTR; 15],
63 pub enum UNWIND_HISTORY_TABLE {}
66 pub struct RUNTIME_FUNCTION {
73 pub struct DISPATCHER_CONTEXT {
76 FunctionEntry: *const RUNTIME_FUNCTION,
77 EstablisherFrame: LPVOID,
79 ContextRecord: *const CONTEXT,
80 LanguageHandler: LPVOID,
81 HandlerData: *const u8,
82 HistoryTable: *const UNWIND_HISTORY_TABLE,
86 #[derive(Copy, Clone)]
87 pub enum EXCEPTION_DISPOSITION {
88 ExceptionContinueExecution,
89 ExceptionContinueSearch,
90 ExceptionNestedException,
91 ExceptionCollidedUnwind
96 fn RaiseException(dwExceptionCode: DWORD,
97 dwExceptionFlags: DWORD,
98 nNumberOfArguments: DWORD,
99 lpArguments: *const ULONG_PTR);
101 fn RtlUnwindEx(TargetFrame: LPVOID,
103 ExceptionRecord: *const EXCEPTION_RECORD,
105 OriginalContext: *const CONTEXT,
106 HistoryTable: *const UNWIND_HISTORY_TABLE);
111 data: Box<Any + Send + 'static>
114 pub unsafe fn panic(data: Box<Any + Send + 'static>) -> ! {
115 let panic_ctx = Box::new(PanicData { data: data });
116 let params = [Box::into_raw(panic_ctx) as ULONG_PTR];
117 RaiseException(RUST_PANIC,
118 EXCEPTION_NONCONTINUABLE,
119 params.len() as DWORD,
120 ¶ms as *const ULONG_PTR);
121 rtabort!("could not unwind stack");
124 pub unsafe fn cleanup(ptr: *mut u8) -> Box<Any + Send + 'static> {
125 let panic_ctx = Box::from_raw(ptr as *mut PanicData);
126 return panic_ctx.data;
129 // SEH doesn't support resuming unwinds after calling a landing pad like
130 // libunwind does. For this reason, MSVC compiler outlines landing pads into
131 // separate functions that can be called directly from the personality function
132 // but are nevertheless able to find and modify stack frame of the "parent"
135 // Since this cannot be done with libdwarf-style landing pads,
136 // rust_eh_personality instead catches RUST_PANICs, runs the landing pad, then
137 // reraises the exception.
139 // Note that it makes certain assumptions about the exception:
141 // 1. That RUST_PANIC is non-continuable, so no lower stack frame may choose to
143 // 2. That the first parameter of the exception is a pointer to an extra data
145 // Since these assumptions do not generally hold true for foreign exceptions
146 // (system faults, C++ exceptions, etc), we make no attempt to invoke our
147 // landing pads (and, thus, destructors!) for anything other than RUST_PANICs.
148 // This is considered acceptable, because the behavior of throwing exceptions
149 // through a C ABI boundary is undefined.
151 #[lang = "eh_personality_catch"]
153 unsafe extern fn rust_eh_personality_catch(
154 exceptionRecord: *mut EXCEPTION_RECORD,
155 establisherFrame: LPVOID,
156 contextRecord: *mut CONTEXT,
157 dispatcherContext: *mut DISPATCHER_CONTEXT
158 ) -> EXCEPTION_DISPOSITION
160 rust_eh_personality(exceptionRecord, establisherFrame,
161 contextRecord, dispatcherContext)
164 #[lang = "eh_personality"]
166 unsafe extern fn rust_eh_personality(
167 exceptionRecord: *mut EXCEPTION_RECORD,
168 establisherFrame: LPVOID,
169 contextRecord: *mut CONTEXT,
170 dispatcherContext: *mut DISPATCHER_CONTEXT
171 ) -> EXCEPTION_DISPOSITION
173 let er = &*exceptionRecord;
174 let dc = &*dispatcherContext;
176 if er.ExceptionFlags & EXCEPTION_UNWIND == 0 { // we are in the dispatch phase
177 if er.ExceptionCode == RUST_PANIC {
178 if let Some(lpad) = find_landing_pad(dc) {
179 RtlUnwindEx(establisherFrame,
182 er.ExceptionInformation[0] as LPVOID, // pointer to PanicData
185 rtabort!("could not unwind");
189 ExceptionContinueSearch
192 // The `resume` instruction, found at the end of the landing pads, and whose job
193 // is to resume stack unwinding, is typically lowered by LLVM into a call to
194 // `_Unwind_Resume` routine. To avoid confusion with the same symbol exported
195 // from libgcc, we redirect it to `rust_eh_unwind_resume`.
196 // Since resolution of this symbol is done by the linker, `rust_eh_unwind_resume`
197 // must be marked `pub` + `#[no_mangle]`. (Can we make it a lang item?)
199 #[lang = "eh_unwind_resume"]
201 unsafe extern fn rust_eh_unwind_resume(panic_ctx: LPVOID) {
202 let params = [panic_ctx as ULONG_PTR];
203 RaiseException(RUST_PANIC,
204 EXCEPTION_NONCONTINUABLE,
205 params.len() as DWORD,
206 ¶ms as *const ULONG_PTR);
207 rtabort!("could not resume unwind");
210 unsafe fn find_landing_pad(dc: &DISPATCHER_CONTEXT) -> Option<usize> {
211 let eh_ctx = eh::EHContext {
212 ip: dc.ControlPc as usize,
213 func_start: dc.ImageBase as usize + (*dc.FunctionEntry).BeginAddress as usize,
214 text_start: dc.ImageBase as usize,
217 eh::find_landing_pad(dc.HandlerData, &eh_ctx)