summary refs log tree commit diff
path: root/src/libstd/sys/common/unwind/seh64_gnu.rs
blob: 26c2cee9222db6c7dbfa04d61fe0a2ff6f7e45ce (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Unwinding implementation of top of native Win64 SEH,
//! however the unwind handler data (aka LSDA) uses GCC-compatible encoding.

#![allow(bad_style)]
#![allow(private_no_mangle_fns)]

use prelude::v1::*;

use any::Any;
use sys_common::dwarf::eh;
use core::mem;
use core::ptr;
use sys::c;

// Define our exception codes:
// according to http://msdn.microsoft.com/en-us/library/het71c37(v=VS.80).aspx,
//    [31:30] = 3 (error), 2 (warning), 1 (info), 0 (success)
//    [29]    = 1 (user-defined)
//    [28]    = 0 (reserved)
// we define bits:
//    [24:27] = type
//    [0:23]  = magic
const ETYPE: c::DWORD = 0b1110_u32 << 28;
const MAGIC: c::DWORD = 0x525354; // "RST"

const RUST_PANIC: c::DWORD  = ETYPE | (1 << 24) | MAGIC;

#[repr(C)]
struct PanicData {
    data: Box<Any + Send + 'static>
}

pub unsafe fn panic(data: Box<Any + Send + 'static>) -> ! {
    let panic_ctx = Box::new(PanicData { data: data });
    let params = [Box::into_raw(panic_ctx) as c::ULONG_PTR];
    c::RaiseException(RUST_PANIC,
                      c::EXCEPTION_NONCONTINUABLE,
                      params.len() as c::DWORD,
                      &params as *const c::ULONG_PTR);
    rtabort!("could not unwind stack");
}

pub unsafe fn cleanup(ptr: *mut u8) -> Box<Any + Send + 'static> {
    let panic_ctx = Box::from_raw(ptr as *mut PanicData);
    return panic_ctx.data;
}

// SEH doesn't support resuming unwinds after calling a landing pad like
// libunwind does. For this reason, MSVC compiler outlines landing pads into
// separate functions that can be called directly from the personality function
// but are nevertheless able to find and modify stack frame of the "parent"
// function.
//
// Since this cannot be done with libdwarf-style landing pads,
// rust_eh_personality instead catches RUST_PANICs, runs the landing pad, then
// reraises the exception.
//
// Note that it makes certain assumptions about the exception:
//
// 1. That RUST_PANIC is non-continuable, so no lower stack frame may choose to
//    resume execution.
// 2. That the first parameter of the exception is a pointer to an extra data
//    area (PanicData).
// Since these assumptions do not generally hold true for foreign exceptions
// (system faults, C++ exceptions, etc), we make no attempt to invoke our
// landing pads (and, thus, destructors!) for anything other than RUST_PANICs.
// This is considered acceptable, because the behavior of throwing exceptions
// through a C ABI boundary is undefined.

#[lang = "eh_personality_catch"]
#[cfg(not(test))]
unsafe extern fn rust_eh_personality_catch(
    exceptionRecord: *mut c::EXCEPTION_RECORD,
    establisherFrame: c::LPVOID,
    contextRecord: *mut c::CONTEXT,
    dispatcherContext: *mut c::DISPATCHER_CONTEXT
) -> c::EXCEPTION_DISPOSITION
{
    rust_eh_personality(exceptionRecord, establisherFrame,
                        contextRecord, dispatcherContext)
}

#[lang = "eh_personality"]
#[cfg(not(test))]
unsafe extern fn rust_eh_personality(
    exceptionRecord: *mut c::EXCEPTION_RECORD,
    establisherFrame: c::LPVOID,
    contextRecord: *mut c::CONTEXT,
    dispatcherContext: *mut c::DISPATCHER_CONTEXT
) -> c::EXCEPTION_DISPOSITION
{
    let er = &*exceptionRecord;
    let dc = &*dispatcherContext;

    if er.ExceptionFlags & c::EXCEPTION_UNWIND == 0 { // we are in the dispatch phase
        if er.ExceptionCode == RUST_PANIC {
            if let Some(lpad) = find_landing_pad(dc) {
                c::RtlUnwindEx(establisherFrame,
                               lpad as c::LPVOID,
                               exceptionRecord,
                               er.ExceptionInformation[0] as c::LPVOID, // pointer to PanicData
                               contextRecord,
                               dc.HistoryTable);
                rtabort!("could not unwind");
            }
        }
    }
    c::ExceptionContinueSearch
}

#[cfg(not(test))]
#[lang = "eh_unwind_resume"]
#[unwind]
unsafe extern fn rust_eh_unwind_resume(panic_ctx: c::LPVOID) -> ! {
    let params = [panic_ctx as c::ULONG_PTR];
    c::RaiseException(RUST_PANIC,
                      c::EXCEPTION_NONCONTINUABLE,
                      params.len() as c::DWORD,
                      &params as *const c::ULONG_PTR);
    rtabort!("could not resume unwind");
}

unsafe fn find_landing_pad(dc: &c::DISPATCHER_CONTEXT) -> Option<usize> {
    let eh_ctx = eh::EHContext {
        ip: dc.ControlPc as usize,
        func_start: dc.ImageBase as usize + (*dc.FunctionEntry).BeginAddress as usize,
        text_start: dc.ImageBase as usize,
        data_start: 0
    };
    eh::find_landing_pad(dc.HandlerData, &eh_ctx)
}