1
Fork 0

Make SEH exceptions use a rust_panic type instead of unsigned __int64*

This commit is contained in:
Amanieu d'Antras 2019-10-27 22:33:25 +00:00
parent 83d6bf4929
commit ad61c88e72
5 changed files with 62 additions and 154 deletions

View file

@ -61,7 +61,6 @@ cfg_if::cfg_if! {
}
mod dwarf;
mod windows;
// Entry point for catching an exception, implemented using the `try` intrinsic
// in the compiler.

View file

@ -51,9 +51,7 @@ use alloc::boxed::Box;
use core::any::Any;
use core::mem;
use core::raw;
use crate::windows as c;
use libc::{c_int, c_uint};
use libc::{c_int, c_uint, c_void};
// 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
@ -76,18 +74,19 @@ use libc::{c_int, c_uint};
// sort of operation. For example, if you compile this C++ code on MSVC and emit
// the LLVM IR:
//
// #include <stdin.h>
// #include <stdint.h>
//
// struct rust_panic {
// uint64_t x[2];
// }
//
// void foo() {
// uint64_t a[2] = {0, 1};
// rust_panic a = {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!
// below were just copied from LLVM,
//
// In any case, these structures are all constructed in a similar manner, and
// it's just somewhat verbose for us.
@ -98,10 +97,9 @@ use libc::{c_int, c_uint};
#[macro_use]
mod imp {
pub type ptr_t = *mut u8;
pub const OFFSET: i32 = 4;
#[cfg(bootstrap)]
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) => (core::ptr::null_mut());
@ -113,10 +111,9 @@ mod imp {
#[macro_use]
mod imp {
pub type ptr_t = u32;
pub const OFFSET: i32 = 8;
#[cfg(bootstrap)]
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;
@ -141,7 +138,7 @@ pub struct _ThrowInfo {
#[repr(C)]
pub struct _CatchableTypeArray {
pub nCatchableTypes: c_int,
pub arrayOfCatchableTypes: [imp::ptr_t; 2],
pub arrayOfCatchableTypes: [imp::ptr_t; 1],
}
#[repr(C)]
@ -164,9 +161,19 @@ pub struct _PMD {
pub struct _TypeDescriptor {
pub pVFTable: *const u8,
pub spare: *mut u8,
#[cfg(bootstrap)]
pub name: [u8; 7],
#[cfg(not(bootstrap))]
pub name: [u8; 11],
}
// Note that we intentionally ignore name mangling rules here: we don't want C++
// to be able to catch Rust panics by simply declaring a `struct rust_panic`.
#[cfg(bootstrap)]
use imp::NAME1 as TYPE_NAME;
#[cfg(not(bootstrap))]
const TYPE_NAME: [u8; 11] = *b"rust_panic\0";
static mut THROW_INFO: _ThrowInfo = _ThrowInfo {
attributes: 0,
pnfnUnwind: ptr!(0),
@ -175,31 +182,22 @@ static mut THROW_INFO: _ThrowInfo = _ThrowInfo {
};
static mut CATCHABLE_TYPE_ARRAY: _CatchableTypeArray = _CatchableTypeArray {
nCatchableTypes: 2,
arrayOfCatchableTypes: [ptr!(0), ptr!(0)],
nCatchableTypes: 1,
arrayOfCatchableTypes: [ptr!(0)],
};
static mut CATCHABLE_TYPE1: _CatchableType = _CatchableType {
properties: 1,
static mut CATCHABLE_TYPE: _CatchableType = _CatchableType {
properties: 0,
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,
#[cfg(bootstrap)]
sizeOrOffset: mem::size_of::<*mut u64>() as c_int,
#[cfg(not(bootstrap))]
sizeOrOffset: mem::size_of::<[u64; 2]>() as c_int,
copy_function: ptr!(0),
};
@ -221,16 +219,10 @@ extern "C" {
//
// 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 {
static mut TYPE_DESCRIPTOR: _TypeDescriptor = _TypeDescriptor {
pVFTable: unsafe { &TYPE_INFO_VTABLE } as *const _ as *const _,
spare: core::ptr::null_mut(),
name: imp::NAME1,
};
static mut TYPE_DESCRIPTOR2: _TypeDescriptor = _TypeDescriptor {
pVFTable: unsafe { &TYPE_INFO_VTABLE } as *const _ as *const _,
spare: core::ptr::null_mut(),
name: imp::NAME2,
name: TYPE_NAME,
};
pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
@ -246,6 +238,11 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
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();
let throw_ptr = if cfg!(bootstrap) {
&mut ptrs_ptr as *mut _ as *mut _
} else {
ptrs_ptr as *mut _
};
// This... may seems surprising, and justifiably so. On 32-bit MSVC the
// pointers between these structure are just that, pointers. On 64-bit MSVC,
@ -270,17 +267,17 @@ pub unsafe fn panic(data: Box<dyn Any + Send>) -> u32 {
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);
ptr!(&CATCHABLE_TYPE as *const _) as u32);
atomic_store(&mut CATCHABLE_TYPE.pType as *mut _ as *mut u32,
ptr!(&TYPE_DESCRIPTOR as *const _) as u32);
c::_CxxThrowException(&mut ptrs_ptr as *mut _ as *mut _,
&mut THROW_INFO as *mut _ as *mut _);
u32::max_value()
extern "system" {
#[unwind(allowed)]
pub fn _CxxThrowException(pExceptionObject: *mut c_void, pThrowInfo: *mut u8) -> !;
}
_CxxThrowException(throw_ptr,
&mut THROW_INFO as *mut _ as *mut _);
}
pub fn payload() -> [u64; 2] {

View file

@ -1,86 +0,0 @@
#![allow(nonstandard_style)]
#![allow(dead_code)]
#![cfg(windows)]
use libc::{c_long, c_ulong, c_void};
pub type DWORD = c_ulong;
pub type LONG = c_long;
pub type ULONG_PTR = usize;
pub type LPVOID = *mut c_void;
pub const EXCEPTION_MAXIMUM_PARAMETERS: usize = 15;
pub const EXCEPTION_NONCONTINUABLE: DWORD = 0x1; // Noncontinuable exception
pub const EXCEPTION_UNWINDING: DWORD = 0x2; // Unwind is in progress
pub const EXCEPTION_EXIT_UNWIND: DWORD = 0x4; // Exit unwind is in progress
pub const EXCEPTION_TARGET_UNWIND: DWORD = 0x20; // Target unwind in progress
pub const EXCEPTION_COLLIDED_UNWIND: DWORD = 0x40; // Collided exception handler call
pub const EXCEPTION_UNWIND: DWORD = EXCEPTION_UNWINDING | EXCEPTION_EXIT_UNWIND |
EXCEPTION_TARGET_UNWIND |
EXCEPTION_COLLIDED_UNWIND;
#[repr(C)]
pub struct EXCEPTION_RECORD {
pub ExceptionCode: DWORD,
pub ExceptionFlags: DWORD,
pub ExceptionRecord: *mut EXCEPTION_RECORD,
pub ExceptionAddress: LPVOID,
pub NumberParameters: DWORD,
pub ExceptionInformation: [LPVOID; EXCEPTION_MAXIMUM_PARAMETERS],
}
#[repr(C)]
pub struct EXCEPTION_POINTERS {
pub ExceptionRecord: *mut EXCEPTION_RECORD,
pub ContextRecord: *mut CONTEXT,
}
pub enum UNWIND_HISTORY_TABLE {}
#[repr(C)]
pub struct RUNTIME_FUNCTION {
pub BeginAddress: DWORD,
pub EndAddress: DWORD,
pub UnwindData: DWORD,
}
pub enum CONTEXT {}
#[repr(C)]
pub struct DISPATCHER_CONTEXT {
pub ControlPc: LPVOID,
pub ImageBase: LPVOID,
pub FunctionEntry: *const RUNTIME_FUNCTION,
pub EstablisherFrame: LPVOID,
pub TargetIp: LPVOID,
pub ContextRecord: *const CONTEXT,
pub LanguageHandler: LPVOID,
pub HandlerData: *const u8,
pub HistoryTable: *const UNWIND_HISTORY_TABLE,
}
#[repr(C)]
pub enum EXCEPTION_DISPOSITION {
ExceptionContinueExecution,
ExceptionContinueSearch,
ExceptionNestedException,
ExceptionCollidedUnwind,
}
pub use self::EXCEPTION_DISPOSITION::*;
extern "system" {
#[unwind(allowed)]
pub fn RaiseException(dwExceptionCode: DWORD,
dwExceptionFlags: DWORD,
nNumberOfArguments: DWORD,
lpArguments: *const ULONG_PTR);
#[unwind(allowed)]
pub fn RtlUnwindEx(TargetFrame: LPVOID,
TargetIp: LPVOID,
ExceptionRecord: *const EXCEPTION_RECORD,
ReturnValue: LPVOID,
OriginalContext: *const CONTEXT,
HistoryTable: *const UNWIND_HISTORY_TABLE);
#[unwind(allowed)]
pub fn _CxxThrowException(pExceptionObject: *mut c_void, pThrowInfo: *mut u8);
}

View file

@ -849,7 +849,7 @@ fn codegen_msvc_try(
// We're generating an IR snippet that looks like:
//
// declare i32 @rust_try(%func, %data, %ptr) {
// %slot = alloca i64*
// %slot = alloca [2 x i64]
// invoke %func(%data) to label %normal unwind label %catchswitch
//
// normal:
@ -873,21 +873,25 @@ fn codegen_msvc_try(
//
// #include <stdint.h>
//
// struct rust_panic {
// uint64_t x[2];
// }
//
// 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];
// } catch(rust_panic a) {
// ret[0] = a.x[0];
// ret[1] = a.x[1];
// return 1;
// }
// }
//
// More information can be found in libstd's seh.rs implementation.
let i64p = bx.type_ptr_to(bx.type_i64());
let ptr_align = bx.tcx().data_layout.pointer_align.abi;
let slot = bx.alloca(i64p, ptr_align);
let i64_2 = bx.type_array(bx.type_i64(), 2);
let i64_align = bx.tcx().data_layout.i64_align.abi;
let slot = bx.alloca(i64_2, i64_align);
bx.invoke(func, &[data], normal.llbb(), catchswitch.llbb(), None);
normal.ret(bx.const_i32(0));
@ -900,17 +904,10 @@ fn codegen_msvc_try(
None => bug!("msvc_try_filter not defined"),
};
let funclet = catchpad.catch_pad(cs, &[tydesc, bx.const_i32(0), slot]);
let addr = catchpad.load(slot, ptr_align);
let i64_align = bx.tcx().data_layout.i64_align.abi;
let arg1 = catchpad.load(addr, i64_align);
let val1 = bx.const_i32(1);
let gep1 = catchpad.inbounds_gep(addr, &[val1]);
let arg2 = catchpad.load(gep1, i64_align);
let local_ptr = catchpad.bitcast(local_ptr, i64p);
let gep2 = catchpad.inbounds_gep(local_ptr, &[val1]);
catchpad.store(arg1, local_ptr, i64_align);
catchpad.store(arg2, gep2, i64_align);
let payload = catchpad.load(slot, i64_align);
let local_ptr = catchpad.bitcast(local_ptr, bx.type_ptr_to(i64_2));
catchpad.store(payload, local_ptr, i64_align);
catchpad.catch_ret(&funclet, caught.llbb());
caught.ret(bx.const_i32(1));

View file

@ -8,6 +8,7 @@ void println(const char* s) {
}
struct exception {};
struct rust_panic {};
struct drop_check {
bool* ok;
@ -45,7 +46,7 @@ extern "C" {
x.ok = NULL;
try {
cb();
} catch (exception e) {
} catch (rust_panic e) {
assert(false && "shouldn't be able to catch a rust panic");
} catch (...) {
println("caught foreign exception in catch (...)");