]> git.lizzy.rs Git - rust.git/blob - src/libpanic_unwind/seh64_gnu.rs
Auto merge of #35856 - phimuemue:master, r=brson
[rust.git] / src / libpanic_unwind / seh64_gnu.rs
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 alloc::boxed::Box;
18
19 use core::any::Any;
20 use core::intrinsics;
21 use core::ptr;
22 use dwarf::eh::{EHContext, EHAction, find_eh_action};
23 use windows as c;
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
33 const ETYPE: c::DWORD = 0b1110_u32 << 28;
34 const MAGIC: c::DWORD = 0x525354; // "RST"
35
36 const RUST_PANIC: c::DWORD = ETYPE | (1 << 24) | MAGIC;
37
38 #[repr(C)]
39 struct PanicData {
40     data: Box<Any + Send>,
41 }
42
43 pub unsafe fn panic(data: Box<Any + Send>) -> u32 {
44     let panic_ctx = Box::new(PanicData { data: data });
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                       &params as *const c::ULONG_PTR);
50     u32::max_value()
51 }
52
53 pub fn payload() -> *mut u8 {
54     ptr::null_mut()
55 }
56
57 pub unsafe fn cleanup(ptr: *mut u8) -> Box<Any + Send> {
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
84 #[lang = "eh_personality"]
85 #[cfg(not(test))]
86 unsafe extern "C" fn rust_eh_personality(exceptionRecord: *mut c::EXCEPTION_RECORD,
87                                          establisherFrame: c::LPVOID,
88                                          contextRecord: *mut c::CONTEXT,
89                                          dispatcherContext: *mut c::DISPATCHER_CONTEXT)
90                                          -> c::EXCEPTION_DISPOSITION {
91     let er = &*exceptionRecord;
92     let dc = &*dispatcherContext;
93
94     if er.ExceptionFlags & c::EXCEPTION_UNWIND == 0 {
95         // we are in the dispatch phase
96         if er.ExceptionCode == RUST_PANIC {
97             if let Some(lpad) = find_landing_pad(dc) {
98                 c::RtlUnwindEx(establisherFrame,
99                                lpad as c::LPVOID,
100                                exceptionRecord,
101                                er.ExceptionInformation[0] as c::LPVOID, // pointer to PanicData
102                                contextRecord,
103                                dc.HistoryTable);
104             }
105         }
106     }
107     c::ExceptionContinueSearch
108 }
109
110 #[lang = "eh_unwind_resume"]
111 #[unwind]
112 unsafe extern "C" fn rust_eh_unwind_resume(panic_ctx: c::LPVOID) -> ! {
113     let params = [panic_ctx as c::ULONG_PTR];
114     c::RaiseException(RUST_PANIC,
115                       c::EXCEPTION_NONCONTINUABLE,
116                       params.len() as c::DWORD,
117                       &params as *const c::ULONG_PTR);
118     intrinsics::abort();
119 }
120
121 unsafe fn find_landing_pad(dc: &c::DISPATCHER_CONTEXT) -> Option<usize> {
122     let eh_ctx = EHContext {
123         // The return address points 1 byte past the call instruction,
124         // which could be in the next IP range in LSDA range table.
125         ip: dc.ControlPc as usize - 1,
126         func_start: dc.ImageBase as usize + (*dc.FunctionEntry).BeginAddress as usize,
127         get_text_start: &|| dc.ImageBase as usize,
128         get_data_start: &|| unimplemented!(),
129     };
130     match find_eh_action(dc.HandlerData, &eh_ctx) {
131         EHAction::None => None,
132         EHAction::Cleanup(lpad) | EHAction::Catch(lpad) => Some(lpad),
133         EHAction::Terminate => intrinsics::abort(),
134     }
135 }