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