]> git.proxmox.com Git - rustc.git/blame - src/libstd/sys/common/unwind/seh64_gnu.rs
Imported Upstream version 1.5.0+dfsg1
[rustc.git] / src / libstd / sys / common / unwind / seh64_gnu.rs
CommitLineData
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
17use prelude::v1::*;
18
19use any::Any;
20use self::EXCEPTION_DISPOSITION::*;
e9174d1e 21use sys_common::dwarf::eh;
c1a9b12d
SL
22use core::mem;
23use core::ptr;
c1a9b12d
SL
24use libc::{c_void, c_ulonglong, DWORD, LPVOID};
25type ULONG_PTR = c_ulonglong;
26
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)
32// we define bits:
33// [24:27] = type
34// [0:23] = magic
35const ETYPE: DWORD = 0b1110_u32 << 28;
36const MAGIC: DWORD = 0x525354; // "RST"
37
38const RUST_PANIC: DWORD = ETYPE | (1 << 24) | MAGIC;
39
40const EXCEPTION_NONCONTINUABLE: DWORD = 0x1; // Noncontinuable exception
41const EXCEPTION_UNWINDING: DWORD = 0x2; // Unwind is in progress
42const EXCEPTION_EXIT_UNWIND: DWORD = 0x4; // Exit unwind is in progress
43const EXCEPTION_STACK_INVALID: DWORD = 0x8; // Stack out of limits or unaligned
44const EXCEPTION_NESTED_CALL: DWORD = 0x10; // Nested exception handler call
45const EXCEPTION_TARGET_UNWIND: DWORD = 0x20; // Target unwind in progress
46const EXCEPTION_COLLIDED_UNWIND: DWORD = 0x40; // Collided exception handler call
47const EXCEPTION_UNWIND: DWORD = EXCEPTION_UNWINDING |
48 EXCEPTION_EXIT_UNWIND |
49 EXCEPTION_TARGET_UNWIND |
50 EXCEPTION_COLLIDED_UNWIND;
51
52#[repr(C)]
53pub struct EXCEPTION_RECORD {
54 ExceptionCode: DWORD,
55 ExceptionFlags: DWORD,
56 ExceptionRecord: *const EXCEPTION_RECORD,
57 ExceptionAddress: LPVOID,
58 NumberParameters: DWORD,
59 ExceptionInformation: [ULONG_PTR; 15],
60}
61
e9174d1e
SL
62pub enum CONTEXT {}
63pub enum UNWIND_HISTORY_TABLE {}
c1a9b12d
SL
64
65#[repr(C)]
66pub struct RUNTIME_FUNCTION {
67 BeginAddress: DWORD,
68 EndAddress: DWORD,
69 UnwindData: DWORD,
70}
71
72#[repr(C)]
73pub struct DISPATCHER_CONTEXT {
74 ControlPc: LPVOID,
75 ImageBase: LPVOID,
76 FunctionEntry: *const RUNTIME_FUNCTION,
77 EstablisherFrame: LPVOID,
78 TargetIp: LPVOID,
79 ContextRecord: *const CONTEXT,
80 LanguageHandler: LPVOID,
81 HandlerData: *const u8,
82 HistoryTable: *const UNWIND_HISTORY_TABLE,
83}
84
85#[repr(C)]
86#[derive(Copy, Clone)]
87pub enum EXCEPTION_DISPOSITION {
88 ExceptionContinueExecution,
89 ExceptionContinueSearch,
90 ExceptionNestedException,
91 ExceptionCollidedUnwind
92}
93
94// From kernel32.dll
95extern "system" {
e9174d1e 96 #[unwind]
c1a9b12d
SL
97 fn RaiseException(dwExceptionCode: DWORD,
98 dwExceptionFlags: DWORD,
99 nNumberOfArguments: DWORD,
100 lpArguments: *const ULONG_PTR);
101
102 fn RtlUnwindEx(TargetFrame: LPVOID,
103 TargetIp: LPVOID,
104 ExceptionRecord: *const EXCEPTION_RECORD,
105 ReturnValue: LPVOID,
106 OriginalContext: *const CONTEXT,
107 HistoryTable: *const UNWIND_HISTORY_TABLE);
108}
109
110#[repr(C)]
111struct PanicData {
112 data: Box<Any + Send + 'static>
113}
114
115pub unsafe fn panic(data: Box<Any + Send + 'static>) -> ! {
116 let panic_ctx = Box::new(PanicData { data: data });
117 let params = [Box::into_raw(panic_ctx) as ULONG_PTR];
c1a9b12d
SL
118 RaiseException(RUST_PANIC,
119 EXCEPTION_NONCONTINUABLE,
120 params.len() as DWORD,
121 &params as *const ULONG_PTR);
122 rtabort!("could not unwind stack");
123}
124
125pub unsafe fn cleanup(ptr: *mut u8) -> Box<Any + Send + 'static> {
c1a9b12d
SL
126 let panic_ctx = Box::from_raw(ptr as *mut PanicData);
127 return panic_ctx.data;
128}
129
130// SEH doesn't support resuming unwinds after calling a landing pad like
131// libunwind does. For this reason, MSVC compiler outlines landing pads into
132// separate functions that can be called directly from the personality function
133// but are nevertheless able to find and modify stack frame of the "parent"
134// function.
135//
136// Since this cannot be done with libdwarf-style landing pads,
137// rust_eh_personality instead catches RUST_PANICs, runs the landing pad, then
138// reraises the exception.
139//
140// Note that it makes certain assumptions about the exception:
141//
142// 1. That RUST_PANIC is non-continuable, so no lower stack frame may choose to
143// resume execution.
144// 2. That the first parameter of the exception is a pointer to an extra data
145// area (PanicData).
146// Since these assumptions do not generally hold true for foreign exceptions
147// (system faults, C++ exceptions, etc), we make no attempt to invoke our
148// landing pads (and, thus, destructors!) for anything other than RUST_PANICs.
149// This is considered acceptable, because the behavior of throwing exceptions
150// through a C ABI boundary is undefined.
151
152#[lang = "eh_personality_catch"]
153#[cfg(not(test))]
154unsafe extern fn rust_eh_personality_catch(
155 exceptionRecord: *mut EXCEPTION_RECORD,
156 establisherFrame: LPVOID,
157 contextRecord: *mut CONTEXT,
158 dispatcherContext: *mut DISPATCHER_CONTEXT
159) -> EXCEPTION_DISPOSITION
160{
161 rust_eh_personality(exceptionRecord, establisherFrame,
162 contextRecord, dispatcherContext)
163}
164
165#[lang = "eh_personality"]
166#[cfg(not(test))]
167unsafe extern fn rust_eh_personality(
168 exceptionRecord: *mut EXCEPTION_RECORD,
169 establisherFrame: LPVOID,
170 contextRecord: *mut CONTEXT,
171 dispatcherContext: *mut DISPATCHER_CONTEXT
172) -> EXCEPTION_DISPOSITION
173{
174 let er = &*exceptionRecord;
175 let dc = &*dispatcherContext;
c1a9b12d
SL
176
177 if er.ExceptionFlags & EXCEPTION_UNWIND == 0 { // we are in the dispatch phase
178 if er.ExceptionCode == RUST_PANIC {
179 if let Some(lpad) = find_landing_pad(dc) {
c1a9b12d
SL
180 RtlUnwindEx(establisherFrame,
181 lpad as LPVOID,
182 exceptionRecord,
183 er.ExceptionInformation[0] as LPVOID, // pointer to PanicData
184 contextRecord,
185 dc.HistoryTable);
186 rtabort!("could not unwind");
187 }
188 }
189 }
190 ExceptionContinueSearch
191}
192
193// The `resume` instruction, found at the end of the landing pads, and whose job
194// is to resume stack unwinding, is typically lowered by LLVM into a call to
195// `_Unwind_Resume` routine. To avoid confusion with the same symbol exported
196// from libgcc, we redirect it to `rust_eh_unwind_resume`.
197// Since resolution of this symbol is done by the linker, `rust_eh_unwind_resume`
198// must be marked `pub` + `#[no_mangle]`. (Can we make it a lang item?)
199
200#[lang = "eh_unwind_resume"]
201#[cfg(not(test))]
e9174d1e 202#[unwind]
c1a9b12d 203unsafe extern fn rust_eh_unwind_resume(panic_ctx: LPVOID) {
c1a9b12d
SL
204 let params = [panic_ctx as ULONG_PTR];
205 RaiseException(RUST_PANIC,
206 EXCEPTION_NONCONTINUABLE,
207 params.len() as DWORD,
208 &params as *const ULONG_PTR);
209 rtabort!("could not resume unwind");
210}
211
212unsafe fn find_landing_pad(dc: &DISPATCHER_CONTEXT) -> Option<usize> {
213 let eh_ctx = eh::EHContext {
214 ip: dc.ControlPc as usize,
215 func_start: dc.ImageBase as usize + (*dc.FunctionEntry).BeginAddress as usize,
216 text_start: dc.ImageBase as usize,
217 data_start: 0
218 };
219 eh::find_landing_pad(dc.HandlerData, &eh_ctx)
220}