]> git.proxmox.com Git - rustc.git/blame - src/libstd/rt/unwind/seh.rs
Imported Upstream version 1.3.0+dfsg1
[rustc.git] / src / libstd / rt / unwind / seh.rs
CommitLineData
1a4d82fc 1// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
223e47cc
LB
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
c1a9b12d
SL
11//! Win64 SEH (see http://msdn.microsoft.com/en-us/library/1eyas8tf.aspx)
12//!
13//! On Windows (currently only on MSVC), the default exception handling
14//! mechanism is Structured Exception Handling (SEH). This is quite different
15//! than Dwarf-based exception handling (e.g. what other unix platforms use) in
16//! terms of compiler internals, so LLVM is required to have a good deal of
17//! extra support for SEH. Currently this support is somewhat lacking, so what's
18//! here is the bare bones of SEH support.
19//!
20//! In a nutshell, what happens here is:
21//!
22//! 1. The `panic` function calls the standard Windows function `RaiseException`
23//! with a Rust-specific code, triggering the unwinding process.
24//! 2. All landing pads generated by the compiler (just "cleanup" landing pads)
25//! use the personality function `__C_specific_handler`, a function in the
26//! CRT, and the unwinding code in Windows will use this personality function
27//! to execute all cleanup code on the stack.
28//! 3. Eventually the "catch" code in `rust_try` (located in
29//! src/rt/rust_try_msvc_64.ll) is executed, which will ensure that the
30//! exception being caught is indeed a Rust exception, returning control back
31//! into Rust.
32//!
33//! Some specific differences from the gcc-based exception handling are:
34//!
35//! * Rust has no custom personality function, it is instead *always*
36//! __C_specific_handler, so the filtering is done in a C++-like manner
37//! instead of in the personality function itself. Note that the specific
38//! syntax for this (found in the rust_try_msvc_64.ll) is taken from an LLVM
39//! test case for SEH.
40//! * We've got some data to transmit across the unwinding boundary,
41//! specifically a `Box<Any + Send + 'static>`. In Dwarf-based unwinding this
42//! data is part of the payload of the exception, but I have not currently
43//! figured out how to do this with LLVM's bindings. Judging by some comments
44//! in the LLVM test cases this may not even be possible currently with LLVM,
45//! so this is just abandoned entirely. Instead the data is stored in a
46//! thread-local in `panic` and retrieved during `cleanup`.
47//!
48//! So given all that, the bindings here are pretty small,
49
50#![allow(bad_style)]
51
62682a34 52use prelude::v1::*;
85aaf69f 53
62682a34 54use any::Any;
c1a9b12d
SL
55use libc::{c_ulong, DWORD, c_void};
56use sys_common::thread_local::StaticKey;
57
58// 0x R U S T
59const RUST_PANIC: DWORD = 0x52555354;
60static PANIC_DATA: StaticKey = StaticKey::new(None);
61
62// This function is provided by kernel32.dll
63extern "system" {
64 fn RaiseException(dwExceptionCode: DWORD,
65 dwExceptionFlags: DWORD,
66 nNumberOfArguments: DWORD,
67 lpArguments: *const c_ulong);
68}
69
70#[repr(C)]
71pub struct EXCEPTION_POINTERS {
72 ExceptionRecord: *mut EXCEPTION_RECORD,
73 ContextRecord: *mut CONTEXT,
74}
75
76enum CONTEXT {}
77
78#[repr(C)]
79struct EXCEPTION_RECORD {
80 ExceptionCode: DWORD,
81 ExceptionFlags: DWORD,
82 ExceptionRecord: *mut _EXCEPTION_RECORD,
83 ExceptionAddress: *mut c_void,
84 NumberParameters: DWORD,
85 ExceptionInformation: [*mut c_ulong; EXCEPTION_MAXIMUM_PARAMETERS],
86}
223e47cc 87
c1a9b12d
SL
88enum _EXCEPTION_RECORD {}
89
90const EXCEPTION_MAXIMUM_PARAMETERS: usize = 15;
91
92pub unsafe fn panic(data: Box<Any + Send + 'static>) -> ! {
93 // See module docs above for an explanation of why `data` is stored in a
94 // thread local instead of being passed as an argument to the
95 // `RaiseException` function (which can in theory carry along arbitrary
96 // data).
97 let exception = Box::new(data);
98 rtassert!(PANIC_DATA.get().is_null());
99 PANIC_DATA.set(Box::into_raw(exception) as *mut u8);
100
101 RaiseException(RUST_PANIC, 0, 0, 0 as *const _);
102 rtabort!("could not unwind stack");
62682a34
SL
103}
104
c1a9b12d
SL
105pub unsafe fn cleanup(ptr: *mut u8) -> Box<Any + Send + 'static> {
106 // The `ptr` here actually corresponds to the code of the exception, and our
107 // real data is stored in our thread local.
108 rtassert!(ptr as DWORD == RUST_PANIC);
109
110 let data = PANIC_DATA.get() as *mut Box<Any + Send + 'static>;
111 PANIC_DATA.set(0 as *mut u8);
112 rtassert!(!data.is_null());
113
114 *Box::from_raw(data)
62682a34
SL
115}
116
c1a9b12d
SL
117// This is required by the compiler to exist (e.g. it's a lang item), but it's
118// never actually called by the compiler because __C_specific_handler is the
119// personality function that is always used. Hence this is just an aborting
120// stub.
62682a34 121#[lang = "eh_personality"]
c1a9b12d
SL
122fn rust_eh_personality() {
123 unsafe { ::intrinsics::abort() }
124}
62682a34 125
c1a9b12d
SL
126// This is a function referenced from `rust_try_msvc_64.ll` which is used to
127// filter the exceptions being caught by that function.
128//
129// In theory local variables can be accessed through the `rbp` parameter of this
130// function, but a comment in an LLVM test case indicates that this is not
131// implemented in LLVM, so this is just an idempotent function which doesn't
132// ferry along any other information.
133//
134// This function just takes a look at the current EXCEPTION_RECORD being thrown
135// to ensure that it's code is RUST_PANIC, which was set by the call to
136// `RaiseException` above in the `panic` function.
62682a34 137#[no_mangle]
c1a9b12d
SL
138#[lang = "msvc_try_filter"]
139pub extern fn __rust_try_filter(eh_ptrs: *mut EXCEPTION_POINTERS,
140 _rbp: *mut u8) -> i32 {
141 unsafe {
142 ((*(*eh_ptrs).ExceptionRecord).ExceptionCode == RUST_PANIC) as i32
143 }
144}