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
|
// 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.
//! Win64 SEH (see http://msdn.microsoft.com/en-us/library/1eyas8tf.aspx)
//!
//! On Windows (currently only on MSVC), the default exception handling
//! mechanism is Structured Exception Handling (SEH). This is quite different
//! than Dwarf-based exception handling (e.g. what other unix platforms use) in
//! terms of compiler internals, so LLVM is required to have a good deal of
//! extra support for SEH. Currently this support is somewhat lacking, so what's
//! here is the bare bones of SEH support.
//!
//! In a nutshell, what happens here is:
//!
//! 1. The `panic` function calls the standard Windows function `RaiseException`
//! with a Rust-specific code, triggering the unwinding process.
//! 2. All landing pads generated by the compiler (just "cleanup" landing pads)
//! use the personality function `__C_specific_handler`, a function in the
//! CRT, and the unwinding code in Windows will use this personality function
//! to execute all cleanup code on the stack.
//! 3. Eventually the "catch" code in `rust_try` (located in
//! src/rt/rust_try_msvc_64.ll) is executed, which will ensure that the
//! exception being caught is indeed a Rust exception, returning control back
//! into Rust.
//!
//! Some specific differences from the gcc-based exception handling are:
//!
//! * Rust has no custom personality function, it is instead *always*
//! __C_specific_handler, so the filtering is done in a C++-like manner
//! instead of in the personality function itself. Note that the specific
//! syntax for this (found in the rust_try_msvc_64.ll) is taken from an LLVM
//! test case for SEH.
//! * We've got some data to transmit across the unwinding boundary,
//! specifically a `Box<Any + Send + 'static>`. In Dwarf-based unwinding this
//! data is part of the payload of the exception, but I have not currently
//! figured out how to do this with LLVM's bindings. Judging by some comments
//! in the LLVM test cases this may not even be possible currently with LLVM,
//! so this is just abandoned entirely. Instead the data is stored in a
//! thread-local in `panic` and retrieved during `cleanup`.
//!
//! So given all that, the bindings here are pretty small,
#![allow(bad_style)]
use prelude::v1::*;
use any::Any;
use ptr;
use sys_common::thread_local::StaticKey;
use sys::c;
// 0x R U S T
const RUST_PANIC: c::DWORD = 0x52555354;
static PANIC_DATA: StaticKey = StaticKey::new(None);
pub unsafe fn panic(data: Box<Any + Send + 'static>) -> ! {
// See module docs above for an explanation of why `data` is stored in a
// thread local instead of being passed as an argument to the
// `RaiseException` function (which can in theory carry along arbitrary
// data).
let exception = Box::new(data);
rtassert!(PANIC_DATA.get().is_null());
PANIC_DATA.set(Box::into_raw(exception) as *mut u8);
c::RaiseException(RUST_PANIC, 0, 0, ptr::null());
rtabort!("could not unwind stack");
}
pub unsafe fn cleanup(ptr: *mut u8) -> Box<Any + Send + 'static> {
// The `ptr` here actually corresponds to the code of the exception, and our
// real data is stored in our thread local.
rtassert!(ptr as c::DWORD == RUST_PANIC);
let data = PANIC_DATA.get() as *mut Box<Any + Send + 'static>;
PANIC_DATA.set(ptr::null_mut());
rtassert!(!data.is_null());
*Box::from_raw(data)
}
// This is required by the compiler to exist (e.g. it's a lang item), but it's
// never actually called by the compiler because __C_specific_handler is the
// personality function that is always used. Hence this is just an aborting
// stub.
#[lang = "eh_personality"]
fn rust_eh_personality() {
unsafe { ::intrinsics::abort() }
}
// This is a function referenced from `rust_try_msvc_64.ll` which is used to
// filter the exceptions being caught by that function.
//
// In theory local variables can be accessed through the `rbp` parameter of this
// function, but a comment in an LLVM test case indicates that this is not
// implemented in LLVM, so this is just an idempotent function which doesn't
// ferry along any other information.
//
// This function just takes a look at the current EXCEPTION_RECORD being thrown
// to ensure that it's code is RUST_PANIC, which was set by the call to
// `RaiseException` above in the `panic` function.
#[lang = "msvc_try_filter"]
#[linkage = "external"]
#[allow(private_no_mangle_fns)]
extern fn __rust_try_filter(eh_ptrs: *mut c::EXCEPTION_POINTERS,
_rbp: *mut u8) -> i32 {
unsafe {
((*(*eh_ptrs).ExceptionRecord).ExceptionCode == RUST_PANIC) as i32
}
}
|