// 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 or the MIT license // , 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`. 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 libc::{c_ulong, DWORD, c_void}; use sys_common::thread_local::StaticKey; // 0x R U S T const RUST_PANIC: DWORD = 0x52555354; static PANIC_DATA: StaticKey = StaticKey::new(None); // This function is provided by kernel32.dll extern "system" { fn RaiseException(dwExceptionCode: DWORD, dwExceptionFlags: DWORD, nNumberOfArguments: DWORD, lpArguments: *const c_ulong); } #[repr(C)] pub struct EXCEPTION_POINTERS { ExceptionRecord: *mut EXCEPTION_RECORD, ContextRecord: *mut CONTEXT, } enum CONTEXT {} #[repr(C)] struct EXCEPTION_RECORD { ExceptionCode: DWORD, ExceptionFlags: DWORD, ExceptionRecord: *mut _EXCEPTION_RECORD, ExceptionAddress: *mut c_void, NumberParameters: DWORD, ExceptionInformation: [*mut c_ulong; EXCEPTION_MAXIMUM_PARAMETERS], } enum _EXCEPTION_RECORD {} const EXCEPTION_MAXIMUM_PARAMETERS: usize = 15; pub unsafe fn panic(data: Box) -> ! { // 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); RaiseException(RUST_PANIC, 0, 0, 0 as *const _); rtabort!("could not unwind stack"); } pub unsafe fn cleanup(ptr: *mut u8) -> Box { // The `ptr` here actually corresponds to the code of the exception, and our // real data is stored in our thread local. rtassert!(ptr as DWORD == RUST_PANIC); let data = PANIC_DATA.get() as *mut Box; PANIC_DATA.set(0 as *mut u8); 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. #[no_mangle] #[lang = "msvc_try_filter"] pub extern fn __rust_try_filter(eh_ptrs: *mut EXCEPTION_POINTERS, _rbp: *mut u8) -> i32 { unsafe { ((*(*eh_ptrs).ExceptionRecord).ExceptionCode == RUST_PANIC) as i32 } }