diff --git a/.gitmodules b/.gitmodules index fc2f8bbc8a350..1224146aa7351 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "src/llvm"] path = src/llvm - url = https://github.com/rust-lang/llvm.git + url = https://github.com/alexcrichton/llvm.git branch = master [submodule "src/jemalloc"] path = src/jemalloc diff --git a/src/libcore/panicking.rs b/src/libcore/panicking.rs index 4170d91e5fce2..ff444a6051a77 100644 --- a/src/libcore/panicking.rs +++ b/src/libcore/panicking.rs @@ -65,8 +65,8 @@ pub fn panic_fmt(fmt: fmt::Arguments, file_line_col: &(&'static str, u32, u32)) extern { #[lang = "panic_fmt"] #[unwind] - fn panic_impl(fmt: fmt::Arguments, file: &'static str, line: u32, col: u32) -> !; + fn rust_begin_unwind(fmt: fmt::Arguments, file: &'static str, line: u32, col: u32) -> !; } let (file, line, col) = *file_line_col; - unsafe { panic_impl(fmt, file, line, col) } + unsafe { rust_begin_unwind(fmt, file, line, col) } } diff --git a/src/libpanic_abort/lib.rs b/src/libpanic_abort/lib.rs index 5f768ef4399e8..61f85439935d2 100644 --- a/src/libpanic_abort/lib.rs +++ b/src/libpanic_abort/lib.rs @@ -98,17 +98,33 @@ pub unsafe extern fn __rust_start_panic(_data: usize, _vtable: usize) -> u32 { // runtime at all. pub mod personalities { #[no_mangle] - #[cfg(not(all(target_os = "windows", - target_env = "gnu", - target_arch = "x86_64")))] + #[cfg(not(all( + target_os = "windows", + any( + target_env = "msvc", + all(target_env = "gnu", target_arch = "x86_64") + ) + )))] pub extern fn rust_eh_personality() {} // On x86_64-pc-windows-gnu we use our own personality function that needs // to return `ExceptionContinueSearch` as we're passing on all our frames. #[no_mangle] - #[cfg(all(target_os = "windows", - target_env = "gnu", - target_arch = "x86_64"))] + #[cfg(all( + target_os = "windows", + any( + target_env = "msvc", + all(target_env = "gnu", target_arch = "x86_64") + ) + ))] + #[cfg_attr( + all(target_env = "msvc", target_arch = "x86"), + export_name = "rust_seh32_personality" + )] + #[cfg_attr( + all(target_env = "msvc", target_arch = "x86_64"), + export_name = "rust_seh64_personality" + )] pub extern fn rust_eh_personality(_record: usize, _frame: usize, _context: usize, diff --git a/src/libpanic_unwind/lib.rs b/src/libpanic_unwind/lib.rs index 92e40e8f26d40..fdb0bfc2afbba 100644 --- a/src/libpanic_unwind/lib.rs +++ b/src/libpanic_unwind/lib.rs @@ -57,7 +57,12 @@ use core::raw; pub use imp::eh_frame_registry::*; // *-pc-windows-msvc -#[cfg(target_env = "msvc")] +#[cfg(all(target_env = "msvc", stage0))] +#[path = "seh_stage0.rs"] +mod imp; + +// *-pc-windows-msvc +#[cfg(all(target_env = "msvc", not(stage0)))] #[path = "seh.rs"] mod imp; diff --git a/src/libpanic_unwind/seh.rs b/src/libpanic_unwind/seh.rs index 5896421493008..5ee40d6c46571 100644 --- a/src/libpanic_unwind/seh.rs +++ b/src/libpanic_unwind/seh.rs @@ -18,38 +18,40 @@ //! //! In a nutshell, what happens here is: //! -//! 1. The `panic` function calls the standard Windows function -//! `_CxxThrowException` to throw a C++-like exception, triggering the -//! unwinding process. +//! 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 use the personality function -//! `__CxxFrameHandler3`, a function in the CRT, and the unwinding code in -//! Windows will use this personality function to execute all cleanup code on -//! the stack. +//! `__C_specific_handler` on 64-bit and `__except_handler3` on 32-bit, +//! functions in the CRT, and the unwinding code in Windows will use this +//! personality function to execute all cleanup code on the stack. //! 3. All compiler-generated calls to `invoke` have a landing pad set as a //! `cleanuppad` LLVM instruction, which indicates the start of the cleanup //! routine. The personality (in step 2, defined in the CRT) is responsible //! for running the cleanup routines. //! 4. Eventually the "catch" code in the `try` intrinsic (generated by the -//! compiler) is executed and indicates that control should come back to +//! compiler) is executed, which will ensure that the exception being caught +//! is indeed a Rust exception, indicating that control should come back to //! Rust. This is done via a `catchswitch` plus a `catchpad` instruction in //! LLVM IR terms, finally returning normal control to the program with a -//! `catchret` instruction. +//! `catchret` instruction. The `try` intrinsic uses a filter function to +//! detect what kind of exception is being thrown, and this detection is +//! implemented as the msvc_try_filter language item below. //! //! Some specific differences from the gcc-based exception handling are: //! //! * Rust has no custom personality function, it is instead *always* -//! `__CxxFrameHandler3`. Additionally, no extra filtering is performed, so we -//! end up catching any C++ exceptions that happen to look like the kind we're -//! throwing. Note that throwing an exception into Rust is undefined behavior -//! anyway, so this should be fine. +//! __C_specific_handler or __except_handler3, so the filtering is done in a +//! C++-like manner instead of in the personality function itself. Note that +//! the precise codegen for this was lifted from an LLVM test case for SEH +//! (this is the `__rust_try_filter` function below). //! * We've got some data to transmit across the unwinding boundary, //! specifically a `Box`. Like with Dwarf exceptions //! these two pointers are stored as a payload in the exception itself. On -//! MSVC, however, there's no need for an extra heap allocation because the -//! call stack is preserved while filter functions are being executed. This -//! means that the pointers are passed directly to `_CxxThrowException` which -//! are then recovered in the filter function to be written to the stack frame -//! of the `try` intrinsic. +//! MSVC, however, there's no need for an extra allocation because the call +//! stack is preserved while filter functions are being executed. This means +//! that the pointers are passed directly to `RaiseException` which are then +//! recovered in the filter function to be written to the stack frame of the +//! `try` intrinsic. //! //! [win64]: http://msdn.microsoft.com/en-us/library/1eyas8tf.aspx //! [llvm]: http://llvm.org/docs/ExceptionHandling.html#background-on-windows-exceptions @@ -63,253 +65,116 @@ use core::mem; use core::raw; use windows as c; -use libc::{c_int, c_uint}; -// First up, a whole bunch of type definitions. There's a few platform-specific -// oddities here, and a lot that's just blatantly copied from LLVM. The purpose -// of all this is to implement the `panic` function below through a call to -// `_CxxThrowException`. -// -// This function takes two arguments. The first is a pointer to the data we're -// passing in, which in this case is our trait object. Pretty easy to find! The -// next, however, is more complicated. This is a pointer to a `_ThrowInfo` -// structure, and it generally is just intended to just describe the exception -// being thrown. -// -// Currently the definition of this type [1] is a little hairy, and the main -// oddity (and difference from the online article) is that on 32-bit the -// pointers are pointers but on 64-bit the pointers are expressed as 32-bit -// offsets from the `__ImageBase` symbol. The `ptr_t` and `ptr!` macro in the -// modules below are used to express this. -// -// The maze of type definitions also closely follows what LLVM emits for this -// sort of operation. For example, if you compile this C++ code on MSVC and emit -// the LLVM IR: -// -// #include -// -// void foo() { -// uint64_t a[2] = {0, 1}; -// throw a; -// } -// -// That's essentially what we're trying to emulate. Most of the constant values -// below were just copied from LLVM, I'm at least not 100% sure what's going on -// everywhere. For example the `.PA_K\0` and `.PEA_K\0` strings below (stuck in -// the names of a few of these) I'm not actually sure what they do, but it seems -// to mirror what LLVM does! -// -// In any case, these structures are all constructed in a similar manner, and -// it's just somewhat verbose for us. -// -// [1]: http://www.geoffchappell.com/studies/msvc/language/predefined/ - -#[cfg(target_arch = "x86")] -#[macro_use] -mod imp { - pub type ptr_t = *mut u8; - pub const OFFSET: i32 = 4; - - pub const NAME1: [u8; 7] = [b'.', b'P', b'A', b'_', b'K', 0, 0]; - pub const NAME2: [u8; 7] = [b'.', b'P', b'A', b'X', 0, 0, 0]; - - macro_rules! ptr { - (0) => (0 as *mut u8); - ($e:expr) => ($e as *mut u8); - } -} - -#[cfg(target_arch = "x86_64")] -#[macro_use] -mod imp { - pub type ptr_t = u32; - pub const OFFSET: i32 = 8; - - pub const NAME1: [u8; 7] = [b'.', b'P', b'E', b'A', b'_', b'K', 0]; - pub const NAME2: [u8; 7] = [b'.', b'P', b'E', b'A', b'X', 0, 0]; - - extern "C" { - pub static __ImageBase: u8; - } - - macro_rules! ptr { - (0) => (0); - ($e:expr) => { - (($e as usize) - (&imp::__ImageBase as *const _ as usize)) as u32 - } - } -} - -#[repr(C)] -pub struct _ThrowInfo { - pub attribues: c_uint, - pub pnfnUnwind: imp::ptr_t, - pub pForwardCompat: imp::ptr_t, - pub pCatchableTypeArray: imp::ptr_t, -} - -#[repr(C)] -pub struct _CatchableTypeArray { - pub nCatchableTypes: c_int, - pub arrayOfCatchableTypes: [imp::ptr_t; 2], -} - -#[repr(C)] -pub struct _CatchableType { - pub properties: c_uint, - pub pType: imp::ptr_t, - pub thisDisplacement: _PMD, - pub sizeOrOffset: c_int, - pub copy_function: imp::ptr_t, -} - -#[repr(C)] -pub struct _PMD { - pub mdisp: c_int, - pub pdisp: c_int, - pub vdisp: c_int, -} - -#[repr(C)] -pub struct _TypeDescriptor { - pub pVFTable: *const u8, - pub spare: *mut u8, - pub name: [u8; 7], -} - -static mut THROW_INFO: _ThrowInfo = _ThrowInfo { - attribues: 0, - pnfnUnwind: ptr!(0), - pForwardCompat: ptr!(0), - pCatchableTypeArray: ptr!(0), -}; - -static mut CATCHABLE_TYPE_ARRAY: _CatchableTypeArray = _CatchableTypeArray { - nCatchableTypes: 2, - arrayOfCatchableTypes: [ptr!(0), ptr!(0)], -}; - -static mut CATCHABLE_TYPE1: _CatchableType = _CatchableType { - properties: 1, - pType: ptr!(0), - thisDisplacement: _PMD { - mdisp: 0, - pdisp: -1, - vdisp: 0, - }, - sizeOrOffset: imp::OFFSET, - copy_function: ptr!(0), -}; - -static mut CATCHABLE_TYPE2: _CatchableType = _CatchableType { - properties: 1, - pType: ptr!(0), - thisDisplacement: _PMD { - mdisp: 0, - pdisp: -1, - vdisp: 0, - }, - sizeOrOffset: imp::OFFSET, - copy_function: ptr!(0), -}; - -extern "C" { - // The leading `\x01` byte here is actually a magical signal to LLVM to - // *not* apply any other mangling like prefixing with a `_` character. - // - // This symbol is the vtable used by C++'s `std::type_info`. Objects of type - // `std::type_info`, type descriptors, have a pointer to this table. Type - // descriptors are referenced by the C++ EH structures defined above and - // that we construct below. - #[link_name = "\x01??_7type_info@@6B@"] - static TYPE_INFO_VTABLE: *const u8; -} - -// We use #[lang = "msvc_try_filter"] here as this is the type descriptor which -// we'll use in LLVM's `catchpad` instruction which ends up also being passed as -// an argument to the C++ personality function. -// -// Again, I'm not entirely sure what this is describing, it just seems to work. -#[cfg_attr(not(test), lang = "msvc_try_filter")] -static mut TYPE_DESCRIPTOR1: _TypeDescriptor = _TypeDescriptor { - pVFTable: unsafe { &TYPE_INFO_VTABLE } as *const _ as *const _, - spare: 0 as *mut _, - name: imp::NAME1, -}; - -static mut TYPE_DESCRIPTOR2: _TypeDescriptor = _TypeDescriptor { - pVFTable: unsafe { &TYPE_INFO_VTABLE } as *const _ as *const _, - spare: 0 as *mut _, - name: imp::NAME2, -}; +// A code which indicates panics that originate from Rust. Note that some of the +// upper bits are used by the system so we just set them to 0 and ignore them. +// 0x 0 R S T +const RUST_PANIC: c::DWORD = 0x00525354; pub unsafe fn panic(data: Box) -> u32 { - use core::intrinsics::atomic_store; - - // _CxxThrowException executes entirely on this stack frame, so there's no - // need to otherwise transfer `data` to the heap. We just pass a stack - // pointer to this function. + // As mentioned above, the call stack here is preserved while the filter + // functions are running, so it's ok to pass stack-local arrays into + // `RaiseException`. // - // The first argument is the payload being thrown (our two pointers), and - // the second argument is the type information object describing the - // exception (constructed above). + // The two pointers of the `data` trait object are written to the stack, + // passed to `RaiseException`, and they're later extracted by the filter + // function below in the "custom exception information" section of the + // `EXCEPTION_RECORD` type. let ptrs = mem::transmute::<_, raw::TraitObject>(data); - let mut ptrs = [ptrs.data as u64, ptrs.vtable as u64]; - let mut ptrs_ptr = ptrs.as_mut_ptr(); - - // This... may seems surprising, and justifiably so. On 32-bit MSVC the - // pointers between these structure are just that, pointers. On 64-bit MSVC, - // however, the pointers between structures are rather expressed as 32-bit - // offsets from `__ImageBase`. - // - // Consequently, on 32-bit MSVC we can declare all these pointers in the - // `static`s above. On 64-bit MSVC, we would have to express subtraction of - // pointers in statics, which Rust does not currently allow, so we can't - // actually do that. - // - // The next best thing, then is to fill in these structures at runtime - // (panicking is already the "slow path" anyway). So here we reinterpret all - // of these pointer fields as 32-bit integers and then store the - // relevant value into it (atomically, as concurrent panics may be - // happening). Technically the runtime will probably do a nonatomic read of - // these fields, but in theory they never read the *wrong* value so it - // shouldn't be too bad... - // - // In any case, we basically need to do something like this until we can - // express more operations in statics (and we may never be able to). - atomic_store(&mut THROW_INFO.pCatchableTypeArray as *mut _ as *mut u32, - ptr!(&CATCHABLE_TYPE_ARRAY as *const _) as u32); - atomic_store(&mut CATCHABLE_TYPE_ARRAY.arrayOfCatchableTypes[0] as *mut _ as *mut u32, - ptr!(&CATCHABLE_TYPE1 as *const _) as u32); - atomic_store(&mut CATCHABLE_TYPE_ARRAY.arrayOfCatchableTypes[1] as *mut _ as *mut u32, - ptr!(&CATCHABLE_TYPE2 as *const _) as u32); - atomic_store(&mut CATCHABLE_TYPE1.pType as *mut _ as *mut u32, - ptr!(&TYPE_DESCRIPTOR1 as *const _) as u32); - atomic_store(&mut CATCHABLE_TYPE2.pType as *mut _ as *mut u32, - ptr!(&TYPE_DESCRIPTOR2 as *const _) as u32); - - c::_CxxThrowException(&mut ptrs_ptr as *mut _ as *mut _, - &mut THROW_INFO as *mut _ as *mut _); + let ptrs = [ptrs.data, ptrs.vtable]; + c::RaiseException(RUST_PANIC, 0, 2, ptrs.as_ptr() as *mut _); u32::max_value() } -pub fn payload() -> [u64; 2] { +pub fn payload() -> [usize; 2] { [0; 2] } -pub unsafe fn cleanup(payload: [u64; 2]) -> Box { +pub unsafe fn cleanup(payload: [usize; 2]) -> Box { mem::transmute(raw::TraitObject { data: payload[0] as *mut _, vtable: payload[1] as *mut _, }) } -// 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 -// or _except_handler3 is the personality function that is always used. -// Hence this is just an aborting stub. +// This is quite a special function, and it's not literally passed in as the +// filter function for the `catchpad` of the `try` intrinsic. The compiler +// actually generates its own filter function wrapper which will delegate to +// this for the actual execution logic for whether the exception should be +// caught. The reasons for this are: +// +// * Each architecture has a slightly different ABI for the filter function +// here. For example on x86 there are no arguments but on x86_64 there are +// two. +// * This function needs access to the stack frame of the `try` intrinsic +// which is using this filter as a catch pad. This is because the payload +// of this exception, `Box`, needs to be transmitted to that +// location. +// +// Both of these differences end up using a ton of weird llvm-specific +// intrinsics, so it's actually pretty difficult to express the entire +// filter function in Rust itself. As a compromise, the compiler takes care +// of all the weird LLVM-specific and platform-specific stuff, getting to +// the point where this function makes the actual decision about what to +// catch given two parameters. +// +// The first parameter is `*mut EXCEPTION_POINTERS` which is some contextual +// information about the exception being filtered, and the second pointer is +// `*mut *mut [usize; 2]` (the payload here). This value points directly +// into the stack frame of the `try` intrinsic itself, and we use it to copy +// information from the exception onto the stack. +#[lang = "msvc_try_filter"] +unsafe extern fn __rust_try_filter(eh_ptrs: *mut u8, + payload: *mut u8) -> i32 { + let eh_ptrs = eh_ptrs as *mut c::EXCEPTION_POINTERS; + let payload = payload as *mut *mut [usize; 2]; + let record = &*(*eh_ptrs).ExceptionRecord; + if record.ExceptionCode != RUST_PANIC { + return 0 + } + (**payload)[0] = record.ExceptionInformation[0] as usize; + (**payload)[1] = record.ExceptionInformation[1] as usize; + return 1 +} + #[lang = "eh_personality"] -#[cfg(not(test))] -fn rust_eh_personality() { - unsafe { ::core::intrinsics::abort() } +#[cfg(target_arch = "x86_64")] +#[no_mangle] +#[allow(unused)] +unsafe extern fn rust_seh64_personality( + ExceptionRecord: *mut c::EXCEPTION_RECORD, + EstablisherFrame: *mut u8, + ContextRecord: *mut u8, + DispatcherContext: *mut u8, +) -> c::EXCEPTION_DISPOSITION { + if (*ExceptionRecord).ExceptionCode != RUST_PANIC { + c::ExceptionContinueSearch + } else { + c::__C_specific_handler(ExceptionRecord, + EstablisherFrame, + ContextRecord, + DispatcherContext) + } +} + +#[lang = "eh_personality"] +#[cfg(target_arch = "x86")] +#[no_mangle] +#[allow(unused)] +unsafe extern fn rust_seh32_personality( + exception_record: *mut c::EXCEPTION_RECORD, + registration: *mut u8, + context: *mut u8, + dispatcher: *mut u8, +) -> i32 { + if (*exception_record).ExceptionCode != RUST_PANIC && + (*exception_record).ExceptionCode != c::STATUS_UNWIND + { + c::DISPOSITION_CONTINUE_SEARCH + } else { + c::_except_handler3(exception_record, + registration, + context, + dispatcher) + } } diff --git a/src/libpanic_unwind/seh_stage0.rs b/src/libpanic_unwind/seh_stage0.rs new file mode 100644 index 0000000000000..5896421493008 --- /dev/null +++ b/src/libpanic_unwind/seh_stage0.rs @@ -0,0 +1,315 @@ +// 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. + +//! Windows SEH +//! +//! 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. +//! +//! In a nutshell, what happens here is: +//! +//! 1. The `panic` function calls the standard Windows function +//! `_CxxThrowException` to throw a C++-like exception, triggering the +//! unwinding process. +//! 2. All landing pads generated by the compiler use the personality function +//! `__CxxFrameHandler3`, 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. All compiler-generated calls to `invoke` have a landing pad set as a +//! `cleanuppad` LLVM instruction, which indicates the start of the cleanup +//! routine. The personality (in step 2, defined in the CRT) is responsible +//! for running the cleanup routines. +//! 4. Eventually the "catch" code in the `try` intrinsic (generated by the +//! compiler) is executed and indicates that control should come back to +//! Rust. This is done via a `catchswitch` plus a `catchpad` instruction in +//! LLVM IR terms, finally returning normal control to the program with a +//! `catchret` instruction. +//! +//! Some specific differences from the gcc-based exception handling are: +//! +//! * Rust has no custom personality function, it is instead *always* +//! `__CxxFrameHandler3`. Additionally, no extra filtering is performed, so we +//! end up catching any C++ exceptions that happen to look like the kind we're +//! throwing. Note that throwing an exception into Rust is undefined behavior +//! anyway, so this should be fine. +//! * We've got some data to transmit across the unwinding boundary, +//! specifically a `Box`. Like with Dwarf exceptions +//! these two pointers are stored as a payload in the exception itself. On +//! MSVC, however, there's no need for an extra heap allocation because the +//! call stack is preserved while filter functions are being executed. This +//! means that the pointers are passed directly to `_CxxThrowException` which +//! are then recovered in the filter function to be written to the stack frame +//! of the `try` intrinsic. +//! +//! [win64]: http://msdn.microsoft.com/en-us/library/1eyas8tf.aspx +//! [llvm]: http://llvm.org/docs/ExceptionHandling.html#background-on-windows-exceptions + +#![allow(bad_style)] +#![allow(private_no_mangle_fns)] + +use alloc::boxed::Box; +use core::any::Any; +use core::mem; +use core::raw; + +use windows as c; +use libc::{c_int, c_uint}; + +// First up, a whole bunch of type definitions. There's a few platform-specific +// oddities here, and a lot that's just blatantly copied from LLVM. The purpose +// of all this is to implement the `panic` function below through a call to +// `_CxxThrowException`. +// +// This function takes two arguments. The first is a pointer to the data we're +// passing in, which in this case is our trait object. Pretty easy to find! The +// next, however, is more complicated. This is a pointer to a `_ThrowInfo` +// structure, and it generally is just intended to just describe the exception +// being thrown. +// +// Currently the definition of this type [1] is a little hairy, and the main +// oddity (and difference from the online article) is that on 32-bit the +// pointers are pointers but on 64-bit the pointers are expressed as 32-bit +// offsets from the `__ImageBase` symbol. The `ptr_t` and `ptr!` macro in the +// modules below are used to express this. +// +// The maze of type definitions also closely follows what LLVM emits for this +// sort of operation. For example, if you compile this C++ code on MSVC and emit +// the LLVM IR: +// +// #include +// +// void foo() { +// uint64_t a[2] = {0, 1}; +// throw a; +// } +// +// That's essentially what we're trying to emulate. Most of the constant values +// below were just copied from LLVM, I'm at least not 100% sure what's going on +// everywhere. For example the `.PA_K\0` and `.PEA_K\0` strings below (stuck in +// the names of a few of these) I'm not actually sure what they do, but it seems +// to mirror what LLVM does! +// +// In any case, these structures are all constructed in a similar manner, and +// it's just somewhat verbose for us. +// +// [1]: http://www.geoffchappell.com/studies/msvc/language/predefined/ + +#[cfg(target_arch = "x86")] +#[macro_use] +mod imp { + pub type ptr_t = *mut u8; + pub const OFFSET: i32 = 4; + + pub const NAME1: [u8; 7] = [b'.', b'P', b'A', b'_', b'K', 0, 0]; + pub const NAME2: [u8; 7] = [b'.', b'P', b'A', b'X', 0, 0, 0]; + + macro_rules! ptr { + (0) => (0 as *mut u8); + ($e:expr) => ($e as *mut u8); + } +} + +#[cfg(target_arch = "x86_64")] +#[macro_use] +mod imp { + pub type ptr_t = u32; + pub const OFFSET: i32 = 8; + + pub const NAME1: [u8; 7] = [b'.', b'P', b'E', b'A', b'_', b'K', 0]; + pub const NAME2: [u8; 7] = [b'.', b'P', b'E', b'A', b'X', 0, 0]; + + extern "C" { + pub static __ImageBase: u8; + } + + macro_rules! ptr { + (0) => (0); + ($e:expr) => { + (($e as usize) - (&imp::__ImageBase as *const _ as usize)) as u32 + } + } +} + +#[repr(C)] +pub struct _ThrowInfo { + pub attribues: c_uint, + pub pnfnUnwind: imp::ptr_t, + pub pForwardCompat: imp::ptr_t, + pub pCatchableTypeArray: imp::ptr_t, +} + +#[repr(C)] +pub struct _CatchableTypeArray { + pub nCatchableTypes: c_int, + pub arrayOfCatchableTypes: [imp::ptr_t; 2], +} + +#[repr(C)] +pub struct _CatchableType { + pub properties: c_uint, + pub pType: imp::ptr_t, + pub thisDisplacement: _PMD, + pub sizeOrOffset: c_int, + pub copy_function: imp::ptr_t, +} + +#[repr(C)] +pub struct _PMD { + pub mdisp: c_int, + pub pdisp: c_int, + pub vdisp: c_int, +} + +#[repr(C)] +pub struct _TypeDescriptor { + pub pVFTable: *const u8, + pub spare: *mut u8, + pub name: [u8; 7], +} + +static mut THROW_INFO: _ThrowInfo = _ThrowInfo { + attribues: 0, + pnfnUnwind: ptr!(0), + pForwardCompat: ptr!(0), + pCatchableTypeArray: ptr!(0), +}; + +static mut CATCHABLE_TYPE_ARRAY: _CatchableTypeArray = _CatchableTypeArray { + nCatchableTypes: 2, + arrayOfCatchableTypes: [ptr!(0), ptr!(0)], +}; + +static mut CATCHABLE_TYPE1: _CatchableType = _CatchableType { + properties: 1, + pType: ptr!(0), + thisDisplacement: _PMD { + mdisp: 0, + pdisp: -1, + vdisp: 0, + }, + sizeOrOffset: imp::OFFSET, + copy_function: ptr!(0), +}; + +static mut CATCHABLE_TYPE2: _CatchableType = _CatchableType { + properties: 1, + pType: ptr!(0), + thisDisplacement: _PMD { + mdisp: 0, + pdisp: -1, + vdisp: 0, + }, + sizeOrOffset: imp::OFFSET, + copy_function: ptr!(0), +}; + +extern "C" { + // The leading `\x01` byte here is actually a magical signal to LLVM to + // *not* apply any other mangling like prefixing with a `_` character. + // + // This symbol is the vtable used by C++'s `std::type_info`. Objects of type + // `std::type_info`, type descriptors, have a pointer to this table. Type + // descriptors are referenced by the C++ EH structures defined above and + // that we construct below. + #[link_name = "\x01??_7type_info@@6B@"] + static TYPE_INFO_VTABLE: *const u8; +} + +// We use #[lang = "msvc_try_filter"] here as this is the type descriptor which +// we'll use in LLVM's `catchpad` instruction which ends up also being passed as +// an argument to the C++ personality function. +// +// Again, I'm not entirely sure what this is describing, it just seems to work. +#[cfg_attr(not(test), lang = "msvc_try_filter")] +static mut TYPE_DESCRIPTOR1: _TypeDescriptor = _TypeDescriptor { + pVFTable: unsafe { &TYPE_INFO_VTABLE } as *const _ as *const _, + spare: 0 as *mut _, + name: imp::NAME1, +}; + +static mut TYPE_DESCRIPTOR2: _TypeDescriptor = _TypeDescriptor { + pVFTable: unsafe { &TYPE_INFO_VTABLE } as *const _ as *const _, + spare: 0 as *mut _, + name: imp::NAME2, +}; + +pub unsafe fn panic(data: Box) -> u32 { + use core::intrinsics::atomic_store; + + // _CxxThrowException executes entirely on this stack frame, so there's no + // need to otherwise transfer `data` to the heap. We just pass a stack + // pointer to this function. + // + // The first argument is the payload being thrown (our two pointers), and + // the second argument is the type information object describing the + // exception (constructed above). + let ptrs = mem::transmute::<_, raw::TraitObject>(data); + let mut ptrs = [ptrs.data as u64, ptrs.vtable as u64]; + let mut ptrs_ptr = ptrs.as_mut_ptr(); + + // This... may seems surprising, and justifiably so. On 32-bit MSVC the + // pointers between these structure are just that, pointers. On 64-bit MSVC, + // however, the pointers between structures are rather expressed as 32-bit + // offsets from `__ImageBase`. + // + // Consequently, on 32-bit MSVC we can declare all these pointers in the + // `static`s above. On 64-bit MSVC, we would have to express subtraction of + // pointers in statics, which Rust does not currently allow, so we can't + // actually do that. + // + // The next best thing, then is to fill in these structures at runtime + // (panicking is already the "slow path" anyway). So here we reinterpret all + // of these pointer fields as 32-bit integers and then store the + // relevant value into it (atomically, as concurrent panics may be + // happening). Technically the runtime will probably do a nonatomic read of + // these fields, but in theory they never read the *wrong* value so it + // shouldn't be too bad... + // + // In any case, we basically need to do something like this until we can + // express more operations in statics (and we may never be able to). + atomic_store(&mut THROW_INFO.pCatchableTypeArray as *mut _ as *mut u32, + ptr!(&CATCHABLE_TYPE_ARRAY as *const _) as u32); + atomic_store(&mut CATCHABLE_TYPE_ARRAY.arrayOfCatchableTypes[0] as *mut _ as *mut u32, + ptr!(&CATCHABLE_TYPE1 as *const _) as u32); + atomic_store(&mut CATCHABLE_TYPE_ARRAY.arrayOfCatchableTypes[1] as *mut _ as *mut u32, + ptr!(&CATCHABLE_TYPE2 as *const _) as u32); + atomic_store(&mut CATCHABLE_TYPE1.pType as *mut _ as *mut u32, + ptr!(&TYPE_DESCRIPTOR1 as *const _) as u32); + atomic_store(&mut CATCHABLE_TYPE2.pType as *mut _ as *mut u32, + ptr!(&TYPE_DESCRIPTOR2 as *const _) as u32); + + c::_CxxThrowException(&mut ptrs_ptr as *mut _ as *mut _, + &mut THROW_INFO as *mut _ as *mut _); + u32::max_value() +} + +pub fn payload() -> [u64; 2] { + [0; 2] +} + +pub unsafe fn cleanup(payload: [u64; 2]) -> Box { + mem::transmute(raw::TraitObject { + data: payload[0] as *mut _, + vtable: payload[1] as *mut _, + }) +} + +// 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 +// or _except_handler3 is the personality function that is always used. +// Hence this is just an aborting stub. +#[lang = "eh_personality"] +#[cfg(not(test))] +fn rust_eh_personality() { + unsafe { ::core::intrinsics::abort() } +} diff --git a/src/libpanic_unwind/windows.rs b/src/libpanic_unwind/windows.rs index a7e90071ceae8..eaf33288545d1 100644 --- a/src/libpanic_unwind/windows.rs +++ b/src/libpanic_unwind/windows.rs @@ -28,6 +28,8 @@ pub const EXCEPTION_COLLIDED_UNWIND: DWORD = 0x40; // Collided exception handler pub const EXCEPTION_UNWIND: DWORD = EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND | EXCEPTION_TARGET_UNWIND | EXCEPTION_COLLIDED_UNWIND; +pub const DISPOSITION_CONTINUE_SEARCH: i32 = 1; +pub const STATUS_UNWIND: DWORD = 0xc0000027; #[repr(C)] pub struct EXCEPTION_RECORD { @@ -93,4 +95,22 @@ extern "system" { HistoryTable: *const UNWIND_HISTORY_TABLE); #[unwind] pub fn _CxxThrowException(pExceptionObject: *mut c_void, pThrowInfo: *mut u8); + + #[cfg(target_arch = "x86_64")] + pub fn __C_specific_handler( + ExceptionRecord: *mut EXCEPTION_RECORD, + EstablisherFrame: *mut u8, + ContextRecord: *mut u8, + DispatcherContext: *mut u8, + ) -> EXCEPTION_DISPOSITION; +} + +extern { + #[cfg(target_arch = "x86")] + pub fn _except_handler3( + exception_record: *mut EXCEPTION_RECORD, + registration: *mut u8, + context: *mut u8, + dispatcher: *mut u8, + ) -> i32; } diff --git a/src/librustc_trans/back/symbol_export.rs b/src/librustc_trans/back/symbol_export.rs index 989ef8a953746..c7e69ac032a24 100644 --- a/src/librustc_trans/back/symbol_export.rs +++ b/src/librustc_trans/back/symbol_export.rs @@ -184,6 +184,8 @@ pub fn provide_extern(providers: &mut Providers) { // In general though we won't link right if these // symbols are stripped, and LTO currently strips them. if &*name == "rust_eh_personality" || + &*name == "rust_seh32_personality" || + &*name == "rust_seh64_personality" || &*name == "rust_eh_register_frames" || &*name == "rust_eh_unregister_frames" { SymbolExportLevel::C diff --git a/src/librustc_trans/context.rs b/src/librustc_trans/context.rs index a285e5f263ab7..e40349373e579 100644 --- a/src/librustc_trans/context.rs +++ b/src/librustc_trans/context.rs @@ -373,12 +373,16 @@ impl<'b, 'tcx> CodegenCx<'b, 'tcx> { } let tcx = self.tcx; let llfn = match tcx.lang_items().eh_personality() { - Some(def_id) if !base::wants_msvc_seh(self.sess()) => { + Some(def_id) => { callee::resolve_and_get_fn(self, def_id, tcx.intern_substs(&[])) } _ => { let name = if base::wants_msvc_seh(self.sess()) { - "__CxxFrameHandler3" + if self.tcx.sess.target.target.arch == "x86" { + "rust_seh32_personality" + } else { + "rust_seh64_personality" + } } else { "rust_eh_personality" }; diff --git a/src/librustc_trans/intrinsic.rs b/src/librustc_trans/intrinsic.rs index b1f1fb52c907d..7287c0a7a3df6 100644 --- a/src/librustc_trans/intrinsic.rs +++ b/src/librustc_trans/intrinsic.rs @@ -10,6 +10,7 @@ #![allow(non_upper_case_globals)] +use callee; use intrinsics::{self, Intrinsic}; use llvm; use llvm::{ValueRef}; @@ -797,7 +798,9 @@ fn trans_msvc_try<'a, 'tcx>(bx: &Builder<'a, 'tcx>, // We're generating an IR snippet that looks like: // // declare i32 @rust_try(%func, %data, %ptr) { - // %slot = alloca i64* + // %slot = alloca i8* + // call @llvm.localescape(%slot) + // store %ptr, %slot // invoke %func(%data) to label %normal unwind label %catchswitch // // normal: @@ -807,58 +810,38 @@ fn trans_msvc_try<'a, 'tcx>(bx: &Builder<'a, 'tcx>, // %cs = catchswitch within none [%catchpad] unwind to caller // // catchpad: - // %tok = catchpad within %cs [%type_descriptor, 0, %slot] - // %ptr[0] = %slot[0] - // %ptr[1] = %slot[1] + // %tok = catchpad within %cs [%rust_try_filter] // catchret from %tok to label %caught // // caught: // ret i32 1 // } // - // This structure follows the basic usage of throw/try/catch in LLVM. - // For example, compile this C++ snippet to see what LLVM generates: - // - // #include - // - // int bar(void (*foo)(void), uint64_t *ret) { - // try { - // foo(); - // return 0; - // } catch(uint64_t a[2]) { - // ret[0] = a[0]; - // ret[1] = a[1]; - // return 1; - // } - // } + // This structure follows the basic usage of the instructions in LLVM + // (see their documentation/test cases for examples), but a + // perhaps-surprising part here is the usage of the `localescape` + // intrinsic. This is used to allow the filter function (also generated + // here) to access variables on the stack of this intrinsic. This + // ability enables us to transfer information about the exception being + // thrown to this point, where we're catching the exception. // // More information can be found in libstd's seh.rs implementation. - let i64p = Type::i64(cx).ptr_to(); + let ptr_align = bx.tcx().data_layout.pointer_align; - let slot = bx.alloca(i64p, "slot", ptr_align); - bx.invoke(func, &[data], normal.llbb(), catchswitch.llbb(), - None); + let slot = bx.alloca(Type::i8p(cx), "slot", ptr_align); + let localescape = cx.get_intrinsic("llvm.localescape"); + bx.call(localescape, &[slot], None); + bx.store(local_ptr, slot, ptr_align); + bx.invoke(func, &[data], normal.llbb(), catchswitch.llbb(), None); normal.ret(C_i32(cx, 0)); let cs = catchswitch.catch_switch(None, None, 1); catchswitch.add_handler(cs, catchpad.llbb()); - let tcx = cx.tcx; - let tydesc = match tcx.lang_items().msvc_try_filter() { - Some(did) => ::consts::get_static(cx, did), - None => bug!("msvc_try_filter not defined"), - }; - let tok = catchpad.catch_pad(cs, &[tydesc, C_i32(cx, 0), slot]); - let addr = catchpad.load(slot, ptr_align); - - let i64_align = bx.tcx().data_layout.i64_align; - let arg1 = catchpad.load(addr, i64_align); - let val1 = C_i32(cx, 1); - let arg2 = catchpad.load(catchpad.inbounds_gep(addr, &[val1]), i64_align); - let local_ptr = catchpad.bitcast(local_ptr, i64p); - catchpad.store(arg1, local_ptr, i64_align); - catchpad.store(arg2, catchpad.inbounds_gep(local_ptr, &[val1]), i64_align); + let filter = generate_filter_fn(cx, bx.llfn()); + let filter = catchpad.bitcast(filter, Type::i8p(cx)); + let tok = catchpad.catch_pad(cs, &[filter]); catchpad.catch_ret(tok, caught.llbb()); caught.ret(C_i32(cx, 1)); @@ -871,6 +854,86 @@ fn trans_msvc_try<'a, 'tcx>(bx: &Builder<'a, 'tcx>, bx.store(ret, dest, i32_align); } +// For MSVC-style exceptions (SEH), the compiler generates a filter function +// which is used to determine whether an exception is being caught (e.g. if it's +// a Rust exception or some other). +// +// This function is used to generate said filter function. The shim generated +// here is actually just a thin wrapper to call the real implementation in the +// standard library itself. For reasons as to why, see seh.rs in the standard +// library. +fn generate_filter_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, rust_try_fn: ValueRef) + -> ValueRef +{ + let rust_try_filter = match cx.tcx.lang_items().msvc_try_filter() { + Some(did) => { + callee::resolve_and_get_fn(cx, did, cx.tcx.intern_substs(&[])) + } + None => bug!("msvc_try_filter not defined"), + }; + + let output = cx.tcx.types.i32; + let i8p = cx.tcx.mk_mut_ptr(cx.tcx.types.i8); + + let frameaddress = cx.get_intrinsic("llvm.frameaddress"); + let recoverfp = cx.get_intrinsic("llvm.x86.seh.recoverfp"); + let localrecover = cx.get_intrinsic("llvm.localrecover"); + + // On all platforms, once we have the EXCEPTION_POINTERS handle as well as + // the base pointer, we follow the standard layout of: + // + // block: + // %parentfp = call i8* llvm.x86.seh.recoverfp(@rust_try_fn, %bp) + // %arg = call i8* llvm.localrecover(@rust_try_fn, %parentfp, 0) + // %ret = call i32 @the_real_filter_function(%ehptrs, %arg) + // ret i32 %ret + // + // The recoverfp intrinsic is used to recover the frame pointer of the + // `rust_try_fn` function, which is then in turn passed to the + // `localrecover` intrinsic (pairing with the `localescape` intrinsic + // mentioned above). Putting all this together means that we now have a + // handle to the arguments passed into the `try` function, allowing writing + // to the stack over there. + // + // For more info, see seh.rs in the standard library. + let do_trans = |bx: Builder, ehptrs, base_pointer| { + let rust_try_fn = bx.bitcast(rust_try_fn, Type::i8p(cx)); + let parentfp = bx.call(recoverfp, &[rust_try_fn, base_pointer], None); + let arg = bx.call(localrecover, + &[rust_try_fn, parentfp, C_i32(cx, 0)], None); + let ret = bx.call(rust_try_filter, &[ehptrs, arg], None); + bx.ret(ret); + }; + + if cx.tcx.sess.target.target.arch == "x86" { + // On x86 the filter function doesn't actually receive any arguments. + // Instead the %ebp register contains some contextual information. + // + // Unfortunately I don't know of any great documentation as to what's + // going on here, all I can say is that there's a few tests cases in + // LLVM's test suite which follow this pattern of instructions, so we + // just do the same. + gen_fn(cx, "__rustc_try_filter", vec![], output, &mut |bx| { + let ebp = bx.call(frameaddress, &[C_i32(cx, 1)], None); + let exn = bx.inbounds_gep(ebp, &[C_i32(cx, -20)]); + let ptr_align = bx.tcx().data_layout.pointer_align; + let exn = bx.load(bx.bitcast(exn, Type::i8p(cx).ptr_to()), ptr_align); + do_trans(bx, exn, ebp); + }) + } else if cx.tcx.sess.target.target.arch == "x86_64" { + // Conveniently on x86_64 the EXCEPTION_POINTERS handle and base pointer + // are passed in as arguments to the filter function, so we just pass + // those along. + gen_fn(cx, "__rustc_try_filter", vec![i8p, i8p], output, &mut |bx| { + let exn = llvm::get_param(bx.llfn(), 0); + let rbp = llvm::get_param(bx.llfn(), 1); + do_trans(bx, exn, rbp); + }) + } else { + bug!("unknown target to generate a filter function") + } +} + // Definition of the standard "try" function for Rust using the GNU-like model // of exceptions (e.g. the normal semantics of LLVM's landingpad and invoke // instructions). diff --git a/src/librustc_trans_utils/symbol_names.rs b/src/librustc_trans_utils/symbol_names.rs index fb299bf7eea0c..f9bc67bc108d9 100644 --- a/src/librustc_trans_utils/symbol_names.rs +++ b/src/librustc_trans_utils/symbol_names.rs @@ -275,10 +275,6 @@ fn compute_symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance tcx.is_foreign_item(def_id) }; - if let Some(name) = weak_lang_items::link_name(&attrs) { - return name.to_string(); - } - if is_foreign { if let Some(name) = attr::first_attr_value_str_by_name(&attrs, "link_name") { return name.to_string(); @@ -297,6 +293,10 @@ fn compute_symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance return tcx.item_name(def_id).to_string(); } + if let Some(name) = weak_lang_items::link_name(&attrs) { + return name.to_string(); + } + // We want to compute the "type" of this item. Unfortunately, some // kinds of items (e.g., closures) don't have an entry in the // item-type array. So walk back up the find the closest parent diff --git a/src/llvm b/src/llvm index 9f81beaf32608..6e690ae043b84 160000 --- a/src/llvm +++ b/src/llvm @@ -1 +1 @@ -Subproject commit 9f81beaf32608fbe1fe0f2a82f974e800e9d8c62 +Subproject commit 6e690ae043b84e704b6dda22763149f93493b6d0 diff --git a/src/test/run-make/longjmp-across-rust/Makefile b/src/test/run-make/longjmp-across-rust/Makefile new file mode 100644 index 0000000000000..9d71ed8fcf3ab --- /dev/null +++ b/src/test/run-make/longjmp-across-rust/Makefile @@ -0,0 +1,5 @@ +-include ../tools.mk + +all: $(call NATIVE_STATICLIB,foo) + $(RUSTC) main.rs + $(call RUN,main) diff --git a/src/test/run-make/longjmp-across-rust/foo.c b/src/test/run-make/longjmp-across-rust/foo.c new file mode 100644 index 0000000000000..eb9939576741b --- /dev/null +++ b/src/test/run-make/longjmp-across-rust/foo.c @@ -0,0 +1,28 @@ +// Copyright 2018 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. + +#include +#include + +static jmp_buf ENV; + +extern void test_middle(); + +void test_start(void(*f)()) { + if (setjmp(ENV) != 0) + return; + f(); + assert(0); +} + +void test_end() { + longjmp(ENV, 1); + assert(0); +} diff --git a/src/test/run-make/longjmp-across-rust/main.rs b/src/test/run-make/longjmp-across-rust/main.rs new file mode 100644 index 0000000000000..0bf5f41d984b1 --- /dev/null +++ b/src/test/run-make/longjmp-across-rust/main.rs @@ -0,0 +1,41 @@ +// Copyright 2018 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. + +#[link(name = "foo", kind = "static")] +extern { + fn test_start(f: extern fn()); + fn test_end(); +} + +fn main() { + unsafe { + test_start(test_middle); + } +} + +struct A; + +impl Drop for A { + fn drop(&mut self) { + panic!() + } +} + +extern fn test_middle() { + let _a = A; + foo(); +} + +fn foo() { + let _a = A; + unsafe { + test_end(); + } +}