2015-05-11 21:09:07 -07:00
|
|
|
// 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.
|
|
|
|
|
2015-06-19 14:57:06 -07:00
|
|
|
//! 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)]
|
|
|
|
|
2015-05-11 21:09:07 -07:00
|
|
|
use prelude::v1::*;
|
|
|
|
|
|
|
|
use any::Any;
|
2015-09-03 09:49:50 +03:00
|
|
|
use ptr;
|
2015-06-19 14:57:06 -07:00
|
|
|
use sys_common::thread_local::StaticKey;
|
2015-11-02 16:23:22 -08:00
|
|
|
use sys::c;
|
2015-06-19 14:57:06 -07:00
|
|
|
|
2015-11-02 16:23:22 -08:00
|
|
|
// 0x R U S T
|
|
|
|
const RUST_PANIC: c::DWORD = 0x52555354;
|
2015-06-19 14:57:06 -07:00
|
|
|
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);
|
|
|
|
|
2015-11-02 16:23:22 -08:00
|
|
|
c::RaiseException(RUST_PANIC, 0, 0, ptr::null());
|
2015-06-19 14:57:06 -07:00
|
|
|
rtabort!("could not unwind stack");
|
2015-05-11 21:09:07 -07:00
|
|
|
}
|
|
|
|
|
2015-07-20 13:27:38 -07:00
|
|
|
pub unsafe fn cleanup(ptr: *mut u8) -> Box<Any + Send + 'static> {
|
2015-06-19 14:57:06 -07:00
|
|
|
// The `ptr` here actually corresponds to the code of the exception, and our
|
|
|
|
// real data is stored in our thread local.
|
2015-11-02 16:23:22 -08:00
|
|
|
rtassert!(ptr as c::DWORD == RUST_PANIC);
|
2015-06-19 14:57:06 -07:00
|
|
|
|
|
|
|
let data = PANIC_DATA.get() as *mut Box<Any + Send + 'static>;
|
2015-09-03 09:49:50 +03:00
|
|
|
PANIC_DATA.set(ptr::null_mut());
|
2015-06-19 14:57:06 -07:00
|
|
|
rtassert!(!data.is_null());
|
|
|
|
|
|
|
|
*Box::from_raw(data)
|
2015-05-11 21:09:07 -07:00
|
|
|
}
|
|
|
|
|
2015-06-19 14:57:06 -07:00
|
|
|
// 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.
|
2015-05-11 21:09:07 -07:00
|
|
|
#[lang = "eh_personality"]
|
2015-06-19 14:57:06 -07:00
|
|
|
fn rust_eh_personality() {
|
|
|
|
unsafe { ::intrinsics::abort() }
|
|
|
|
}
|
2015-05-11 21:09:07 -07:00
|
|
|
|
2015-06-19 14:57:06 -07:00
|
|
|
// 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.
|
2015-07-20 13:27:38 -07:00
|
|
|
#[lang = "msvc_try_filter"]
|
2015-09-08 15:53:46 -07:00
|
|
|
#[linkage = "external"]
|
|
|
|
#[allow(private_no_mangle_fns)]
|
2015-11-02 16:23:22 -08:00
|
|
|
extern fn __rust_try_filter(eh_ptrs: *mut c::EXCEPTION_POINTERS,
|
2015-09-08 15:53:46 -07:00
|
|
|
_rbp: *mut u8) -> i32 {
|
2015-06-19 14:57:06 -07:00
|
|
|
unsafe {
|
|
|
|
((*(*eh_ptrs).ExceptionRecord).ExceptionCode == RUST_PANIC) as i32
|
|
|
|
}
|
|
|
|
}
|