Implement Win64 eh_personality natively.
This commit is contained in:
parent
28869d45dd
commit
6112b22078
@ -54,6 +54,7 @@ fn main(argc: isize, argv: *const *const u8) -> isize {
|
||||
#[lang = "stack_exhausted"] extern fn stack_exhausted() {}
|
||||
#[lang = "eh_personality"] extern fn eh_personality() {}
|
||||
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }
|
||||
# #[lang = "eh_unwind_resume"] extern fn rust_eh_unwind_resume() {}
|
||||
```
|
||||
|
||||
Note the use of `abort`: the `exchange_malloc` lang item is assumed to
|
||||
|
@ -39,6 +39,7 @@ fn start(_argc: isize, _argv: *const *const u8) -> isize {
|
||||
#[lang = "stack_exhausted"] extern fn stack_exhausted() {}
|
||||
#[lang = "eh_personality"] extern fn eh_personality() {}
|
||||
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }
|
||||
# #[lang = "eh_unwind_resume"] extern fn rust_eh_unwind_resume() {}
|
||||
# // fn main() {} tricked you, rustdoc!
|
||||
```
|
||||
|
||||
@ -63,6 +64,7 @@ pub extern fn main(argc: i32, argv: *const *const u8) -> i32 {
|
||||
#[lang = "stack_exhausted"] extern fn stack_exhausted() {}
|
||||
#[lang = "eh_personality"] extern fn eh_personality() {}
|
||||
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }
|
||||
# #[lang = "eh_unwind_resume"] extern fn rust_eh_unwind_resume() {}
|
||||
# // fn main() {} tricked you, rustdoc!
|
||||
```
|
||||
|
||||
@ -150,6 +152,7 @@ extern fn panic_fmt(args: &core::fmt::Arguments,
|
||||
|
||||
#[lang = "stack_exhausted"] extern fn stack_exhausted() {}
|
||||
#[lang = "eh_personality"] extern fn eh_personality() {}
|
||||
# #[lang = "eh_unwind_resume"] extern fn rust_eh_unwind_resume() {}
|
||||
# #[start] fn start(argc: isize, argv: *const *const u8) -> isize { 0 }
|
||||
# fn main() {}
|
||||
```
|
||||
|
@ -327,6 +327,7 @@ lets_do_this! {
|
||||
|
||||
EhPersonalityLangItem, "eh_personality", eh_personality;
|
||||
EhPersonalityCatchLangItem, "eh_personality_catch", eh_personality_catch;
|
||||
EhUnwindResumeLangItem, "eh_unwind_resume", eh_unwind_resume;
|
||||
MSVCTryFilterLangItem, "msvc_try_filter", msvc_try_filter;
|
||||
|
||||
ExchangeHeapLangItem, "exchange_heap", exchange_heap;
|
||||
|
@ -45,6 +45,10 @@ pub fn check_crate(krate: &ast::Crate,
|
||||
if items.eh_personality().is_none() {
|
||||
items.missing.push(lang_items::EhPersonalityLangItem);
|
||||
}
|
||||
if sess.target.target.options.custom_unwind_resume &
|
||||
items.eh_unwind_resume().is_none() {
|
||||
items.missing.push(lang_items::EhUnwindResumeLangItem);
|
||||
}
|
||||
|
||||
{
|
||||
let mut cx = Context { sess: sess, items: items };
|
||||
@ -122,4 +126,5 @@ weak_lang_items! {
|
||||
panic_fmt, PanicFmtLangItem, rust_begin_unwind;
|
||||
stack_exhausted, StackExhaustedLangItem, rust_stack_exhausted;
|
||||
eh_personality, EhPersonalityLangItem, rust_eh_personality;
|
||||
eh_unwind_resume, EhUnwindResumeLangItem, rust_eh_unwind_resume;
|
||||
}
|
||||
|
@ -171,6 +171,11 @@ pub struct TargetOptions {
|
||||
/// currently only "gnu" is used to fall into LLVM. Unknown strings cause
|
||||
/// the system linker to be used.
|
||||
pub archive_format: String,
|
||||
/// Whether the target uses a custom unwind resumption routine.
|
||||
/// By default LLVM lowers `resume` instructions into calls to `_Unwind_Resume`
|
||||
/// defined in libgcc. If this option is enabled, the target must provide
|
||||
/// `eh_unwind_resume` lang item.
|
||||
pub custom_unwind_resume: bool,
|
||||
}
|
||||
|
||||
impl Default for TargetOptions {
|
||||
@ -209,6 +214,7 @@ impl Default for TargetOptions {
|
||||
pre_link_objects: Vec::new(),
|
||||
post_link_objects: Vec::new(),
|
||||
archive_format: String::new(),
|
||||
custom_unwind_resume: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@ pub fn target() -> Target {
|
||||
// On Win64 unwinding is handled by the OS, so we can link libgcc statically.
|
||||
base.pre_link_args.push("-static-libgcc".to_string());
|
||||
base.pre_link_args.push("-m64".to_string());
|
||||
base.custom_unwind_resume = true;
|
||||
|
||||
Target {
|
||||
llvm_target: "x86_64-pc-windows-gnu".to_string(),
|
||||
|
@ -2171,6 +2171,12 @@ fn finish_register_fn(ccx: &CrateContext, sym: String, node_id: ast::NodeId,
|
||||
llvm::SetDLLStorageClass(llfn, llvm::DLLExportStorageClass);
|
||||
}
|
||||
}
|
||||
if ccx.tcx().lang_items.eh_unwind_resume() == Some(def) {
|
||||
llvm::SetLinkage(llfn, llvm::ExternalLinkage);
|
||||
if ccx.use_dll_storage_attrs() {
|
||||
llvm::SetDLLStorageClass(llfn, llvm::DLLExportStorageClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn register_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||
|
@ -846,6 +846,8 @@ impl<'blk, 'tcx> CleanupHelperMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx
|
||||
|
||||
debug!("get_or_create_landing_pad");
|
||||
|
||||
self.inject_unwind_resume_hook();
|
||||
|
||||
// Check if a landing pad block exists; if not, create one.
|
||||
{
|
||||
let mut scopes = self.scopes.borrow_mut();
|
||||
|
@ -561,6 +561,55 @@ impl<'a, 'tcx> FunctionContext<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// By default, LLVM lowers `resume` instructions into calls to `_Unwind_Resume`
|
||||
/// defined in libgcc, however, unlike personality routines, there is no easy way to
|
||||
/// override that symbol. This method injects a local-scoped `_Unwind_Resume` function
|
||||
/// which immediately defers to the user-defined `eh_unwind_resume` lang item.
|
||||
pub fn inject_unwind_resume_hook(&self) {
|
||||
let ccx = self.ccx;
|
||||
if !ccx.sess().target.target.options.custom_unwind_resume ||
|
||||
ccx.unwind_resume_hooked().get() {
|
||||
return;
|
||||
}
|
||||
|
||||
let new_resume = match ccx.tcx().lang_items.eh_unwind_resume() {
|
||||
Some(did) => callee::trans_fn_ref(ccx, did, ExprId(0), &self.param_substs).val,
|
||||
None => {
|
||||
let fty = Type::variadic_func(&[], &Type::void(self.ccx));
|
||||
declare::declare_cfn(self.ccx, "rust_eh_unwind_resume", fty,
|
||||
self.ccx.tcx().mk_nil())
|
||||
}
|
||||
};
|
||||
|
||||
unsafe {
|
||||
let resume_type = Type::func(&[Type::i8(ccx).ptr_to()], &Type::void(ccx));
|
||||
let old_resume = llvm::LLVMAddFunction(ccx.llmod(),
|
||||
"_Unwind_Resume\0".as_ptr() as *const _,
|
||||
resume_type.to_ref());
|
||||
llvm::SetLinkage(old_resume, llvm::InternalLinkage);
|
||||
let llbb = llvm::LLVMAppendBasicBlockInContext(ccx.llcx(),
|
||||
old_resume,
|
||||
"\0".as_ptr() as *const _);
|
||||
let builder = ccx.builder();
|
||||
builder.position_at_end(llbb);
|
||||
builder.call(new_resume, &[llvm::LLVMGetFirstParam(old_resume)], None);
|
||||
builder.unreachable(); // it should never return
|
||||
|
||||
// Until DwarfEHPrepare pass has run, _Unwind_Resume is not referenced by any live code
|
||||
// and is subject to dead code elimination. Here we add _Unwind_Resume to @llvm.globals
|
||||
// to prevent that.
|
||||
let i8p_ty = Type::i8p(ccx);
|
||||
let used_ty = Type::array(&i8p_ty, 1);
|
||||
let used = llvm::LLVMAddGlobal(ccx.llmod(), used_ty.to_ref(),
|
||||
"llvm.used\0".as_ptr() as *const _);
|
||||
let old_resume = llvm::LLVMConstBitCast(old_resume, i8p_ty.to_ref());
|
||||
llvm::LLVMSetInitializer(used, C_array(i8p_ty, &[old_resume]));
|
||||
llvm::SetLinkage(used, llvm::AppendingLinkage);
|
||||
llvm::LLVMSetSection(used, "llvm.metadata\0".as_ptr() as *const _)
|
||||
}
|
||||
ccx.unwind_resume_hooked().set(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Basic block context. We create a block context for each basic block
|
||||
|
@ -146,6 +146,7 @@ pub struct LocalCrateContext<'tcx> {
|
||||
|
||||
eh_personality: RefCell<Option<ValueRef>>,
|
||||
rust_try_fn: RefCell<Option<ValueRef>>,
|
||||
unwind_resume_hooked: Cell<bool>,
|
||||
|
||||
intrinsics: RefCell<FnvHashMap<&'static str, ValueRef>>,
|
||||
|
||||
@ -466,6 +467,7 @@ impl<'tcx> LocalCrateContext<'tcx> {
|
||||
dbg_cx: dbg_cx,
|
||||
eh_personality: RefCell::new(None),
|
||||
rust_try_fn: RefCell::new(None),
|
||||
unwind_resume_hooked: Cell::new(false),
|
||||
intrinsics: RefCell::new(FnvHashMap()),
|
||||
n_llvm_insns: Cell::new(0),
|
||||
trait_cache: RefCell::new(FnvHashMap()),
|
||||
@ -735,6 +737,10 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
|
||||
&self.local.rust_try_fn
|
||||
}
|
||||
|
||||
pub fn unwind_resume_hooked<'a>(&'a self) -> &'a Cell<bool> {
|
||||
&self.local.unwind_resume_hooked
|
||||
}
|
||||
|
||||
fn intrinsics<'a>(&'a self) -> &'a RefCell<FnvHashMap<&'static str, ValueRef>> {
|
||||
&self.local.intrinsics
|
||||
}
|
||||
|
@ -1159,26 +1159,14 @@ fn trans_msvc_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
||||
// of exceptions (e.g. the normal semantics of LLVM's landingpad and invoke
|
||||
// instructions).
|
||||
//
|
||||
// This translation is a little surprising for two reasons:
|
||||
// This translation is a little surprising because
|
||||
// we always call a shim function instead of inlining the call to `invoke`
|
||||
// manually here. This is done because in LLVM we're only allowed to have one
|
||||
// personality per function definition. The call to the `try` intrinsic is
|
||||
// being inlined into the function calling it, and that function may already
|
||||
// have other personality functions in play. By calling a shim we're
|
||||
// guaranteed that our shim will have the right personality function.
|
||||
//
|
||||
// 1. We always call a shim function instead of inlining the call to `invoke`
|
||||
// manually here. This is done because in LLVM we're only allowed to have one
|
||||
// personality per function definition. The call to the `try` intrinsic is
|
||||
// being inlined into the function calling it, and that function may already
|
||||
// have other personality functions in play. By calling a shim we're
|
||||
// guaranteed that our shim will have the right personality function.
|
||||
//
|
||||
// 2. Instead of making one shim (explained above), we make two shims! The
|
||||
// reason for this has to do with the technical details about the
|
||||
// implementation of unwinding in the runtime, but the tl;dr; is that the
|
||||
// outer shim's personality function says "catch rust exceptions" and the
|
||||
// inner shim's landing pad will not `resume` the exception being thrown.
|
||||
// This means that the outer shim's landing pad is never run and the inner
|
||||
// shim's return value is the return value of the whole call.
|
||||
//
|
||||
// The double-shim aspect is currently done for implementation ease on the
|
||||
// runtime side of things, and more info can be found in
|
||||
// src/libstd/rt/unwind/gcc.rs.
|
||||
fn trans_gnu_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
||||
func: ValueRef,
|
||||
data: ValueRef,
|
||||
@ -1188,38 +1176,50 @@ fn trans_gnu_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
||||
let ccx = bcx.ccx();
|
||||
let dloc = DebugLoc::None;
|
||||
|
||||
// Type indicator for the exception being thrown, not entirely sure
|
||||
// what's going on here but it's what all the examples in LLVM use.
|
||||
let lpad_ty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)],
|
||||
false);
|
||||
// Translates the shims described above:
|
||||
//
|
||||
// bcx:
|
||||
// invoke %func(%args...) normal %normal unwind %catch
|
||||
//
|
||||
// normal:
|
||||
// ret null
|
||||
//
|
||||
// catch:
|
||||
// (ptr, _) = landingpad
|
||||
// ret ptr
|
||||
|
||||
// Define the "inner try" shim
|
||||
let rust_try_inner = declare::define_internal_rust_fn(ccx,
|
||||
"__rust_try_inner",
|
||||
try_fn_ty);
|
||||
trans_rust_try(ccx, rust_try_inner, lpad_ty, bcx.fcx.eh_personality(),
|
||||
output, dloc, &mut |bcx, then, catch| {
|
||||
let func = llvm::get_param(rust_try_inner, 0);
|
||||
let data = llvm::get_param(rust_try_inner, 1);
|
||||
Invoke(bcx, func, &[data], then.llbb, catch.llbb, None, dloc);
|
||||
C_null(Type::i8p(ccx))
|
||||
});
|
||||
|
||||
// Define the "outer try" shim.
|
||||
let rust_try = declare::define_internal_rust_fn(ccx, "__rust_try",
|
||||
try_fn_ty);
|
||||
let rust_try = declare::define_internal_rust_fn(ccx, "__rust_try", try_fn_ty);
|
||||
let catch_pers = match bcx.tcx().lang_items.eh_personality_catch() {
|
||||
Some(did) => callee::trans_fn_ref(ccx, did, ExprId(0),
|
||||
bcx.fcx.param_substs).val,
|
||||
None => bcx.tcx().sess.bug("eh_personality_catch not defined"),
|
||||
};
|
||||
trans_rust_try(ccx, rust_try, lpad_ty, catch_pers, output, dloc,
|
||||
&mut |bcx, then, catch| {
|
||||
let func = llvm::get_param(rust_try, 0);
|
||||
let data = llvm::get_param(rust_try, 1);
|
||||
Invoke(bcx, rust_try_inner, &[func, data], then.llbb, catch.llbb,
|
||||
None, dloc)
|
||||
});
|
||||
|
||||
let (fcx, block_arena);
|
||||
block_arena = TypedArena::new();
|
||||
fcx = new_fn_ctxt(ccx, rust_try, ast::DUMMY_NODE_ID, false,
|
||||
output, ccx.tcx().mk_substs(Substs::trans_empty()),
|
||||
None, &block_arena);
|
||||
let bcx = init_function(&fcx, true, output);
|
||||
let then = bcx.fcx.new_temp_block("then");
|
||||
let catch = bcx.fcx.new_temp_block("catch");
|
||||
|
||||
let func = llvm::get_param(rust_try, 0);
|
||||
let data = llvm::get_param(rust_try, 1);
|
||||
Invoke(bcx, func, &[data], then.llbb, catch.llbb, None, dloc);
|
||||
Ret(then, C_null(Type::i8p(ccx)), dloc);
|
||||
|
||||
// Type indicator for the exception being thrown.
|
||||
// The first value in this tuple is a pointer to the exception object being thrown.
|
||||
// The second value is a "selector" indicating which of the landing pad clauses
|
||||
// the exception's type had been matched to. rust_try ignores the selector.
|
||||
let lpad_ty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)],
|
||||
false);
|
||||
let vals = LandingPad(catch, lpad_ty, catch_pers, 1);
|
||||
AddClause(catch, vals, C_null(Type::i8p(ccx)));
|
||||
let ptr = ExtractValue(catch, vals, 0);
|
||||
Ret(catch, ptr, dloc);
|
||||
|
||||
return rust_try
|
||||
});
|
||||
|
||||
@ -1228,68 +1228,9 @@ fn trans_gnu_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
||||
let ret = Call(bcx, llfn, &[func, data], None, dloc);
|
||||
Store(bcx, ret, dest);
|
||||
return bcx;
|
||||
|
||||
// Translates both the inner and outer shims described above. The only
|
||||
// difference between these two is the function invoked and the personality
|
||||
// involved, so a common routine is shared.
|
||||
//
|
||||
// bcx:
|
||||
// invoke %func(%args...) normal %normal unwind %unwind
|
||||
//
|
||||
// normal:
|
||||
// ret null
|
||||
//
|
||||
// unwind:
|
||||
// (ptr, _) = landingpad
|
||||
// br (ptr != null), done, reraise
|
||||
//
|
||||
// done:
|
||||
// ret ptr
|
||||
//
|
||||
// reraise:
|
||||
// resume
|
||||
//
|
||||
// Note that the branch checking for `null` here isn't actually necessary,
|
||||
// it's just an unfortunate hack to make sure that LLVM doesn't optimize too
|
||||
// much. If this were not present, then LLVM would correctly deduce that our
|
||||
// inner shim should be tagged with `nounwind` (as it catches all
|
||||
// exceptions) and then the outer shim's `invoke` will be translated to just
|
||||
// a simple call, destroying that entry for the personality function.
|
||||
//
|
||||
// To ensure that both shims always have an `invoke` this check against null
|
||||
// confuses LLVM enough to the point that it won't infer `nounwind` and
|
||||
// we'll proceed as normal.
|
||||
fn trans_rust_try<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>,
|
||||
llfn: ValueRef,
|
||||
lpad_ty: Type,
|
||||
personality: ValueRef,
|
||||
output: ty::FnOutput<'tcx>,
|
||||
dloc: DebugLoc,
|
||||
invoke: &mut FnMut(Block, Block, Block) -> ValueRef) {
|
||||
let (fcx, block_arena);
|
||||
block_arena = TypedArena::new();
|
||||
fcx = new_fn_ctxt(ccx, llfn, ast::DUMMY_NODE_ID, false,
|
||||
output, ccx.tcx().mk_substs(Substs::trans_empty()),
|
||||
None, &block_arena);
|
||||
let bcx = init_function(&fcx, true, output);
|
||||
let then = bcx.fcx.new_temp_block("then");
|
||||
let catch = bcx.fcx.new_temp_block("catch");
|
||||
let reraise = bcx.fcx.new_temp_block("reraise");
|
||||
let catch_return = bcx.fcx.new_temp_block("catch-return");
|
||||
|
||||
let invoke_ret = invoke(bcx, then, catch);
|
||||
Ret(then, invoke_ret, dloc);
|
||||
let vals = LandingPad(catch, lpad_ty, personality, 1);
|
||||
AddClause(catch, vals, C_null(Type::i8p(ccx)));
|
||||
let ptr = ExtractValue(catch, vals, 0);
|
||||
let valid = ICmp(catch, llvm::IntNE, ptr, C_null(Type::i8p(ccx)), dloc);
|
||||
CondBr(catch, valid, catch_return.llbb, reraise.llbb, dloc);
|
||||
Ret(catch_return, ptr, dloc);
|
||||
Resume(reraise, vals);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to generate the `Ty` associated with `rust_Try`
|
||||
// Helper to generate the `Ty` associated with `rust_try`
|
||||
fn get_rust_try_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>,
|
||||
f: &mut FnMut(Ty<'tcx>,
|
||||
ty::FnOutput<'tcx>) -> ValueRef)
|
||||
@ -1299,8 +1240,7 @@ fn get_rust_try_fn<'a, 'tcx>(fcx: &FunctionContext<'a, 'tcx>,
|
||||
return llfn
|
||||
}
|
||||
|
||||
// Define the types up front for the signatures of the rust_try and
|
||||
// rust_try_inner functions.
|
||||
// Define the type up front for the signature of the rust_try function.
|
||||
let tcx = ccx.tcx();
|
||||
let i8p = tcx.mk_mut_ptr(tcx.types.i8);
|
||||
let fn_ty = tcx.mk_bare_fn(ty::BareFnTy {
|
||||
|
159
src/libstd/rt/dwarf/eh.rs
Normal file
159
src/libstd/rt/dwarf/eh.rs
Normal file
@ -0,0 +1,159 @@
|
||||
// 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.
|
||||
|
||||
//! Parsing of GCC-style Language-Specific Data Area (LSDA)
|
||||
//! For details see:
|
||||
//! http://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA/ehframechpt.html
|
||||
//! http://mentorembedded.github.io/cxx-abi/exceptions.pdf
|
||||
//! http://www.airs.com/blog/archives/460
|
||||
//! http://www.airs.com/blog/archives/464
|
||||
//!
|
||||
//! A reference implementation may be found in the GCC source tree
|
||||
//! (<root>/libgcc/unwind-c.c as of this writing)
|
||||
|
||||
#![allow(non_upper_case_globals)]
|
||||
#![allow(unused)]
|
||||
|
||||
use prelude::v1::*;
|
||||
use rt::dwarf::DwarfReader;
|
||||
use core::mem;
|
||||
|
||||
pub const DW_EH_PE_omit : u8 = 0xFF;
|
||||
pub const DW_EH_PE_absptr : u8 = 0x00;
|
||||
|
||||
pub const DW_EH_PE_uleb128 : u8 = 0x01;
|
||||
pub const DW_EH_PE_udata2 : u8 = 0x02;
|
||||
pub const DW_EH_PE_udata4 : u8 = 0x03;
|
||||
pub const DW_EH_PE_udata8 : u8 = 0x04;
|
||||
pub const DW_EH_PE_sleb128 : u8 = 0x09;
|
||||
pub const DW_EH_PE_sdata2 : u8 = 0x0A;
|
||||
pub const DW_EH_PE_sdata4 : u8 = 0x0B;
|
||||
pub const DW_EH_PE_sdata8 : u8 = 0x0C;
|
||||
|
||||
pub const DW_EH_PE_pcrel : u8 = 0x10;
|
||||
pub const DW_EH_PE_textrel : u8 = 0x20;
|
||||
pub const DW_EH_PE_datarel : u8 = 0x30;
|
||||
pub const DW_EH_PE_funcrel : u8 = 0x40;
|
||||
pub const DW_EH_PE_aligned : u8 = 0x50;
|
||||
|
||||
pub const DW_EH_PE_indirect : u8 = 0x80;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct EHContext {
|
||||
pub ip: usize, // Current instruction pointer
|
||||
pub func_start: usize, // Address of the current function
|
||||
pub text_start: usize, // Address of the code section
|
||||
pub data_start: usize, // Address of the data section
|
||||
}
|
||||
|
||||
pub unsafe fn find_landing_pad(lsda: *const u8, context: &EHContext)
|
||||
-> Option<usize> {
|
||||
if lsda.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let func_start = context.func_start;
|
||||
let mut reader = DwarfReader::new(lsda);
|
||||
|
||||
let start_encoding = reader.read::<u8>();
|
||||
// base address for landing pad offsets
|
||||
let lpad_base = if start_encoding != DW_EH_PE_omit {
|
||||
read_encoded_pointer(&mut reader, context, start_encoding)
|
||||
} else {
|
||||
func_start
|
||||
};
|
||||
|
||||
let ttype_encoding = reader.read::<u8>();
|
||||
if ttype_encoding != DW_EH_PE_omit {
|
||||
// Rust doesn't analyze exception types, so we don't care about the type table
|
||||
reader.read_uleb128();
|
||||
}
|
||||
|
||||
let call_site_encoding = reader.read::<u8>();
|
||||
let call_site_table_length = reader.read_uleb128();
|
||||
let action_table = reader.ptr.offset(call_site_table_length as isize);
|
||||
// Return addresses point 1 byte past the call instruction, which could
|
||||
// be in the next IP range.
|
||||
let ip = context.ip-1;
|
||||
|
||||
while reader.ptr < action_table {
|
||||
let cs_start = read_encoded_pointer(&mut reader, context, call_site_encoding);
|
||||
let cs_len = read_encoded_pointer(&mut reader, context, call_site_encoding);
|
||||
let cs_lpad = read_encoded_pointer(&mut reader, context, call_site_encoding);
|
||||
let cs_action = reader.read_uleb128();
|
||||
// Callsite table is sorted by cs_start, so if we've passed the ip, we
|
||||
// may stop searching.
|
||||
if ip < func_start + cs_start {
|
||||
break
|
||||
}
|
||||
if ip < func_start + cs_start + cs_len {
|
||||
if cs_lpad != 0 {
|
||||
return Some(lpad_base + cs_lpad);
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
// IP range not found: gcc's C++ personality calls terminate() here,
|
||||
// however the rest of the languages treat this the same as cs_lpad == 0.
|
||||
// We follow this suit.
|
||||
return None;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn round_up(unrounded: usize, align: usize) -> usize {
|
||||
assert!(align.is_power_of_two());
|
||||
(unrounded + align - 1) & !(align - 1)
|
||||
}
|
||||
|
||||
unsafe fn read_encoded_pointer(reader: &mut DwarfReader,
|
||||
context: &EHContext,
|
||||
encoding: u8) -> usize {
|
||||
assert!(encoding != DW_EH_PE_omit);
|
||||
|
||||
// DW_EH_PE_aligned implies it's an absolute pointer value
|
||||
if encoding == DW_EH_PE_aligned {
|
||||
reader.ptr = round_up(reader.ptr as usize,
|
||||
mem::size_of::<usize>()) as *const u8;
|
||||
return reader.read::<usize>();
|
||||
}
|
||||
|
||||
let mut result = match encoding & 0x0F {
|
||||
DW_EH_PE_absptr => reader.read::<usize>(),
|
||||
DW_EH_PE_uleb128 => reader.read_uleb128() as usize,
|
||||
DW_EH_PE_udata2 => reader.read::<u16>() as usize,
|
||||
DW_EH_PE_udata4 => reader.read::<u32>() as usize,
|
||||
DW_EH_PE_udata8 => reader.read::<u64>() as usize,
|
||||
DW_EH_PE_sleb128 => reader.read_sleb128() as usize,
|
||||
DW_EH_PE_sdata2 => reader.read::<i16>() as usize,
|
||||
DW_EH_PE_sdata4 => reader.read::<i32>() as usize,
|
||||
DW_EH_PE_sdata8 => reader.read::<i64>() as usize,
|
||||
_ => panic!()
|
||||
};
|
||||
|
||||
result += match encoding & 0x70 {
|
||||
DW_EH_PE_absptr => 0,
|
||||
// relative to address of the encoded value, despite the name
|
||||
DW_EH_PE_pcrel => reader.ptr as usize,
|
||||
DW_EH_PE_textrel => { assert!(context.text_start != 0);
|
||||
context.text_start },
|
||||
DW_EH_PE_datarel => { assert!(context.data_start != 0);
|
||||
context.data_start },
|
||||
DW_EH_PE_funcrel => { assert!(context.func_start != 0);
|
||||
context.func_start },
|
||||
_ => panic!()
|
||||
};
|
||||
|
||||
if encoding & DW_EH_PE_indirect != 0 {
|
||||
result = *(result as *const usize);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
107
src/libstd/rt/dwarf/mod.rs
Normal file
107
src/libstd/rt/dwarf/mod.rs
Normal file
@ -0,0 +1,107 @@
|
||||
// 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.
|
||||
|
||||
//! Utilities for parsing DWARF-encoded data streams.
|
||||
//! See http://www.dwarfstd.org,
|
||||
//! DWARF-4 standard, Section 7 - "Data Representation"
|
||||
|
||||
// This module is used only by x86_64-pc-windows-gnu for now, but we
|
||||
// are compiling it everywhere to avoid regressions.
|
||||
#![allow(unused)]
|
||||
|
||||
pub mod eh;
|
||||
|
||||
use prelude::v1::*;
|
||||
use core::mem;
|
||||
|
||||
pub struct DwarfReader {
|
||||
pub ptr : *const u8
|
||||
}
|
||||
|
||||
#[repr(C,packed)]
|
||||
struct Unaligned<T>(T);
|
||||
|
||||
impl DwarfReader {
|
||||
|
||||
pub fn new(ptr : *const u8) -> DwarfReader {
|
||||
DwarfReader {
|
||||
ptr : ptr
|
||||
}
|
||||
}
|
||||
|
||||
// DWARF streams are packed, so e.g. a u32 would not necessarily be aligned
|
||||
// on a 4-byte boundary. This may cause problems on platforms with strict
|
||||
// alignment requirements. By wrapping data in a "packed" struct, we are
|
||||
// telling the backend to generate "misalignment-safe" code.
|
||||
pub unsafe fn read<T:Copy>(&mut self) -> T {
|
||||
let Unaligned(result) = *(self.ptr as *const Unaligned<T>);
|
||||
self.ptr = self.ptr.offset(mem::size_of::<T>() as isize);
|
||||
result
|
||||
}
|
||||
|
||||
// ULEB128 and SLEB128 encodings are defined in Section 7.6 - "Variable
|
||||
// Length Data".
|
||||
pub unsafe fn read_uleb128(&mut self) -> u64 {
|
||||
let mut shift : usize = 0;
|
||||
let mut result : u64 = 0;
|
||||
let mut byte : u8;
|
||||
loop {
|
||||
byte = self.read::<u8>();
|
||||
result |= ((byte & 0x7F) as u64) << shift;
|
||||
shift += 7;
|
||||
if byte & 0x80 == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
pub unsafe fn read_sleb128(&mut self) -> i64 {
|
||||
let mut shift : usize = 0;
|
||||
let mut result : u64 = 0;
|
||||
let mut byte : u8;
|
||||
loop {
|
||||
byte = self.read::<u8>();
|
||||
result |= ((byte & 0x7F) as u64) << shift;
|
||||
shift += 7;
|
||||
if byte & 0x80 == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// sign-extend
|
||||
if shift < 8 * mem::size_of::<u64>() && (byte & 0x40) != 0 {
|
||||
result |= (!0 as u64) << shift;
|
||||
}
|
||||
result as i64
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn dwarf_reader() {
|
||||
let encoded: &[u8] = &[1,
|
||||
2, 3,
|
||||
4, 5, 6, 7,
|
||||
0xE5, 0x8E, 0x26,
|
||||
0x9B, 0xF1, 0x59,
|
||||
0xFF, 0xFF];
|
||||
|
||||
let mut reader = DwarfReader::new(encoded.as_ptr());
|
||||
|
||||
unsafe {
|
||||
assert!(reader.read::<u8>() == u8::to_be(1u8));
|
||||
assert!(reader.read::<u16>() == u16::to_be(0x0203));
|
||||
assert!(reader.read::<u32>() == u32::to_be(0x04050607));
|
||||
|
||||
assert!(reader.read_uleb128() == 624485);
|
||||
assert!(reader.read_sleb128() == -624485);
|
||||
|
||||
assert!(reader.read::<i8>() == i8::to_be(-1));
|
||||
}
|
||||
}
|
@ -18,7 +18,7 @@ macro_rules! rterrln {
|
||||
::rt::util::dumb_print(format_args!(concat!($fmt, "\n")))
|
||||
} );
|
||||
($fmt:expr, $($arg:expr),*) => ( {
|
||||
::rt::util::dumb_print(format_args!(concat!($fmt, "\n"), $($arg)*))
|
||||
::rt::util::dumb_print(format_args!(concat!($fmt, "\n"), $($arg),*))
|
||||
} )
|
||||
}
|
||||
|
||||
@ -31,7 +31,7 @@ macro_rules! rtdebug {
|
||||
} );
|
||||
($str:expr, $($arg:expr),*) => ( {
|
||||
if cfg!(rtdebug) {
|
||||
rterrln!($str, $($arg)*)
|
||||
rterrln!($str, $($arg),*)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -47,6 +47,8 @@ pub mod args;
|
||||
mod at_exit_imp;
|
||||
mod libunwind;
|
||||
|
||||
mod dwarf;
|
||||
|
||||
/// The default error code of the rust runtime if the main thread panics instead
|
||||
/// of exiting cleanly.
|
||||
pub const DEFAULT_ERROR_CODE: isize = 101;
|
||||
|
@ -74,14 +74,7 @@ fn rust_exception_class() -> uw::_Unwind_Exception_Class {
|
||||
// so the behavior of __gcc_personality_v0 is perfectly adequate there, and
|
||||
// - rust_eh_personality_catch, used only by rust_try(), which always catches.
|
||||
//
|
||||
// Note, however, that for implementation simplicity, rust_eh_personality_catch
|
||||
// lacks code to install a landing pad, so in order to obtain exception object
|
||||
// pointer (which it needs to return upstream), rust_try() employs another trick:
|
||||
// it calls into the nested rust_try_inner(), whose landing pad does not resume
|
||||
// unwinds. Instead, it extracts the exception pointer and performs a "normal"
|
||||
// return.
|
||||
//
|
||||
// See also: rt/rust_try.ll
|
||||
// See also: rustc_trans::trans::intrinsic::trans_gnu_try
|
||||
|
||||
#[cfg(all(not(target_arch = "arm"),
|
||||
not(all(windows, target_arch = "x86_64")),
|
||||
@ -118,11 +111,11 @@ pub mod eabi {
|
||||
#[lang = "eh_personality_catch"]
|
||||
#[no_mangle]
|
||||
pub extern fn rust_eh_personality_catch(
|
||||
_version: c_int,
|
||||
version: c_int,
|
||||
actions: uw::_Unwind_Action,
|
||||
_exception_class: uw::_Unwind_Exception_Class,
|
||||
_ue_header: *mut uw::_Unwind_Exception,
|
||||
_context: *mut uw::_Unwind_Context
|
||||
exception_class: uw::_Unwind_Exception_Class,
|
||||
ue_header: *mut uw::_Unwind_Exception,
|
||||
context: *mut uw::_Unwind_Context
|
||||
) -> uw::_Unwind_Reason_Code
|
||||
{
|
||||
|
||||
@ -130,7 +123,10 @@ pub mod eabi {
|
||||
uw::_URC_HANDLER_FOUND // catch!
|
||||
}
|
||||
else { // cleanup phase
|
||||
uw::_URC_INSTALL_CONTEXT
|
||||
unsafe {
|
||||
__gcc_personality_v0(version, actions, exception_class, ue_header,
|
||||
context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -171,11 +167,11 @@ pub mod eabi {
|
||||
#[lang = "eh_personality_catch"]
|
||||
#[no_mangle]
|
||||
pub extern fn rust_eh_personality_catch(
|
||||
_version: c_int,
|
||||
version: c_int,
|
||||
actions: uw::_Unwind_Action,
|
||||
_exception_class: uw::_Unwind_Exception_Class,
|
||||
_ue_header: *mut uw::_Unwind_Exception,
|
||||
_context: *mut uw::_Unwind_Context
|
||||
exception_class: uw::_Unwind_Exception_Class,
|
||||
ue_header: *mut uw::_Unwind_Exception,
|
||||
context: *mut uw::_Unwind_Context
|
||||
) -> uw::_Unwind_Reason_Code
|
||||
{
|
||||
if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase
|
||||
@ -222,8 +218,8 @@ pub mod eabi {
|
||||
#[no_mangle]
|
||||
pub extern fn rust_eh_personality_catch(
|
||||
state: uw::_Unwind_State,
|
||||
_ue_header: *mut uw::_Unwind_Exception,
|
||||
_context: *mut uw::_Unwind_Context
|
||||
ue_header: *mut uw::_Unwind_Exception,
|
||||
context: *mut uw::_Unwind_Context
|
||||
) -> uw::_Unwind_Reason_Code
|
||||
{
|
||||
if (state as c_int & uw::_US_ACTION_MASK as c_int)
|
||||
@ -231,112 +227,9 @@ pub mod eabi {
|
||||
uw::_URC_HANDLER_FOUND // catch!
|
||||
}
|
||||
else { // cleanup phase
|
||||
uw::_URC_INSTALL_CONTEXT
|
||||
unsafe {
|
||||
__gcc_personality_v0(state, ue_header, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Win64 SEH (see http://msdn.microsoft.com/en-us/library/1eyas8tf.aspx)
|
||||
//
|
||||
// This looks a bit convoluted because rather than implementing a native SEH
|
||||
// handler, GCC reuses the same personality routine as for the other
|
||||
// architectures by wrapping it with an "API translator" layer
|
||||
// (_GCC_specific_handler).
|
||||
|
||||
#[cfg(all(windows, target_arch = "x86_64", not(test)))]
|
||||
#[doc(hidden)]
|
||||
#[allow(non_camel_case_types, non_snake_case)]
|
||||
pub mod eabi {
|
||||
pub use self::EXCEPTION_DISPOSITION::*;
|
||||
use rt::libunwind as uw;
|
||||
use libc::{c_void, c_int};
|
||||
|
||||
// Fake definitions; these are actually complicated structs,
|
||||
// but we don't use the contents here.
|
||||
pub type EXCEPTION_RECORD = c_void;
|
||||
pub type CONTEXT = c_void;
|
||||
pub type DISPATCHER_CONTEXT = c_void;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum EXCEPTION_DISPOSITION {
|
||||
ExceptionContinueExecution,
|
||||
ExceptionContinueSearch,
|
||||
ExceptionNestedException,
|
||||
ExceptionCollidedUnwind
|
||||
}
|
||||
|
||||
type _Unwind_Personality_Fn =
|
||||
extern fn(
|
||||
version: c_int,
|
||||
actions: uw::_Unwind_Action,
|
||||
exception_class: uw::_Unwind_Exception_Class,
|
||||
ue_header: *mut uw::_Unwind_Exception,
|
||||
context: *mut uw::_Unwind_Context
|
||||
) -> uw::_Unwind_Reason_Code;
|
||||
|
||||
extern {
|
||||
fn __gcc_personality_seh0(
|
||||
exceptionRecord: *mut EXCEPTION_RECORD,
|
||||
establisherFrame: *mut c_void,
|
||||
contextRecord: *mut CONTEXT,
|
||||
dispatcherContext: *mut DISPATCHER_CONTEXT
|
||||
) -> EXCEPTION_DISPOSITION;
|
||||
|
||||
fn _GCC_specific_handler(
|
||||
exceptionRecord: *mut EXCEPTION_RECORD,
|
||||
establisherFrame: *mut c_void,
|
||||
contextRecord: *mut CONTEXT,
|
||||
dispatcherContext: *mut DISPATCHER_CONTEXT,
|
||||
personality: _Unwind_Personality_Fn
|
||||
) -> EXCEPTION_DISPOSITION;
|
||||
}
|
||||
|
||||
#[lang = "eh_personality"]
|
||||
#[no_mangle]
|
||||
extern fn rust_eh_personality(
|
||||
exceptionRecord: *mut EXCEPTION_RECORD,
|
||||
establisherFrame: *mut c_void,
|
||||
contextRecord: *mut CONTEXT,
|
||||
dispatcherContext: *mut DISPATCHER_CONTEXT
|
||||
) -> EXCEPTION_DISPOSITION
|
||||
{
|
||||
unsafe {
|
||||
__gcc_personality_seh0(exceptionRecord, establisherFrame,
|
||||
contextRecord, dispatcherContext)
|
||||
}
|
||||
}
|
||||
|
||||
#[lang = "eh_personality_catch"]
|
||||
#[no_mangle]
|
||||
pub extern fn rust_eh_personality_catch(
|
||||
exceptionRecord: *mut EXCEPTION_RECORD,
|
||||
establisherFrame: *mut c_void,
|
||||
contextRecord: *mut CONTEXT,
|
||||
dispatcherContext: *mut DISPATCHER_CONTEXT
|
||||
) -> EXCEPTION_DISPOSITION
|
||||
{
|
||||
extern fn inner(
|
||||
_version: c_int,
|
||||
actions: uw::_Unwind_Action,
|
||||
_exception_class: uw::_Unwind_Exception_Class,
|
||||
_ue_header: *mut uw::_Unwind_Exception,
|
||||
_context: *mut uw::_Unwind_Context
|
||||
) -> uw::_Unwind_Reason_Code
|
||||
{
|
||||
if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase
|
||||
uw::_URC_HANDLER_FOUND // catch!
|
||||
}
|
||||
else { // cleanup phase
|
||||
uw::_URC_INSTALL_CONTEXT
|
||||
}
|
||||
}
|
||||
|
||||
unsafe {
|
||||
_GCC_specific_handler(exceptionRecord, establisherFrame,
|
||||
contextRecord, dispatcherContext,
|
||||
inner)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
||||
//! "Exception Handling in LLVM" (llvm.org/docs/ExceptionHandling.html) and
|
||||
//! documents linked from it.
|
||||
//! These are also good reads:
|
||||
//! http://theofilos.cs.columbia.edu/blog/2013/09/22/base_abi/
|
||||
//! http://mentorembedded.github.io/cxx-abi/abi-eh.html
|
||||
//! http://monoinfinito.wordpress.com/series/exception-handling-in-c/
|
||||
//! http://www.airs.com/blog/index.php?s=exception+frames
|
||||
//!
|
||||
@ -76,9 +76,20 @@ use sys_common::mutex::Mutex;
|
||||
// The actual unwinding implementation is cfg'd here, and we've got two current
|
||||
// implementations. One goes through SEH on Windows and the other goes through
|
||||
// libgcc via the libunwind-like API.
|
||||
#[cfg(target_env = "msvc")] #[path = "seh.rs"] #[doc(hidden)]
|
||||
|
||||
// *-pc-windows-msvc
|
||||
#[cfg(all(windows, target_env = "msvc"))]
|
||||
#[path = "seh.rs"] #[doc(hidden)]
|
||||
pub mod imp;
|
||||
#[cfg(not(target_env = "msvc"))] #[path = "gcc.rs"] #[doc(hidden)]
|
||||
|
||||
// x86_64-pc-windows-gnu
|
||||
#[cfg(all(windows, target_arch="x86_64", target_env="gnu"))]
|
||||
#[path = "seh64_gnu.rs"] #[doc(hidden)]
|
||||
pub mod imp;
|
||||
|
||||
// i686-pc-windows-gnu and all others
|
||||
#[cfg(any(unix, all(windows, target_arch="x86", target_env="gnu")))]
|
||||
#[path = "gcc.rs"] #[doc(hidden)]
|
||||
pub mod imp;
|
||||
|
||||
pub type Callback = fn(msg: &(Any + Send), file: &'static str, line: u32);
|
||||
|
227
src/libstd/rt/unwind/seh64_gnu.rs
Normal file
227
src/libstd/rt/unwind/seh64_gnu.rs
Normal file
@ -0,0 +1,227 @@
|
||||
// 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.
|
||||
|
||||
//! Unwinding implementation of top of native Win64 SEH,
|
||||
//! however the unwind handler data (aka LSDA) uses GCC-compatible encoding.
|
||||
|
||||
#![allow(bad_style)]
|
||||
#![allow(private_no_mangle_fns)]
|
||||
|
||||
use prelude::v1::*;
|
||||
|
||||
use any::Any;
|
||||
use self::EXCEPTION_DISPOSITION::*;
|
||||
use rt::dwarf::eh;
|
||||
use core::mem;
|
||||
use core::ptr;
|
||||
use simd;
|
||||
use libc::{c_void, c_ulonglong, DWORD, LPVOID};
|
||||
type ULONG_PTR = c_ulonglong;
|
||||
|
||||
// Define our exception codes:
|
||||
// according to http://msdn.microsoft.com/en-us/library/het71c37(v=VS.80).aspx,
|
||||
// [31:30] = 3 (error), 2 (warning), 1 (info), 0 (success)
|
||||
// [29] = 1 (user-defined)
|
||||
// [28] = 0 (reserved)
|
||||
// we define bits:
|
||||
// [24:27] = type
|
||||
// [0:23] = magic
|
||||
const ETYPE: DWORD = 0b1110_u32 << 28;
|
||||
const MAGIC: DWORD = 0x525354; // "RST"
|
||||
|
||||
const RUST_PANIC: DWORD = ETYPE | (1 << 24) | MAGIC;
|
||||
|
||||
const EXCEPTION_NONCONTINUABLE: DWORD = 0x1; // Noncontinuable exception
|
||||
const EXCEPTION_UNWINDING: DWORD = 0x2; // Unwind is in progress
|
||||
const EXCEPTION_EXIT_UNWIND: DWORD = 0x4; // Exit unwind is in progress
|
||||
const EXCEPTION_STACK_INVALID: DWORD = 0x8; // Stack out of limits or unaligned
|
||||
const EXCEPTION_NESTED_CALL: DWORD = 0x10; // Nested exception handler call
|
||||
const EXCEPTION_TARGET_UNWIND: DWORD = 0x20; // Target unwind in progress
|
||||
const EXCEPTION_COLLIDED_UNWIND: DWORD = 0x40; // Collided exception handler call
|
||||
const EXCEPTION_UNWIND: DWORD = EXCEPTION_UNWINDING |
|
||||
EXCEPTION_EXIT_UNWIND |
|
||||
EXCEPTION_TARGET_UNWIND |
|
||||
EXCEPTION_COLLIDED_UNWIND;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct EXCEPTION_RECORD {
|
||||
ExceptionCode: DWORD,
|
||||
ExceptionFlags: DWORD,
|
||||
ExceptionRecord: *const EXCEPTION_RECORD,
|
||||
ExceptionAddress: LPVOID,
|
||||
NumberParameters: DWORD,
|
||||
ExceptionInformation: [ULONG_PTR; 15],
|
||||
}
|
||||
|
||||
pub type CONTEXT = c_void;
|
||||
pub type UNWIND_HISTORY_TABLE = c_void;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct RUNTIME_FUNCTION {
|
||||
BeginAddress: DWORD,
|
||||
EndAddress: DWORD,
|
||||
UnwindData: DWORD,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct DISPATCHER_CONTEXT {
|
||||
ControlPc: LPVOID,
|
||||
ImageBase: LPVOID,
|
||||
FunctionEntry: *const RUNTIME_FUNCTION,
|
||||
EstablisherFrame: LPVOID,
|
||||
TargetIp: LPVOID,
|
||||
ContextRecord: *const CONTEXT,
|
||||
LanguageHandler: LPVOID,
|
||||
HandlerData: *const u8,
|
||||
HistoryTable: *const UNWIND_HISTORY_TABLE,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum EXCEPTION_DISPOSITION {
|
||||
ExceptionContinueExecution,
|
||||
ExceptionContinueSearch,
|
||||
ExceptionNestedException,
|
||||
ExceptionCollidedUnwind
|
||||
}
|
||||
|
||||
// From kernel32.dll
|
||||
extern "system" {
|
||||
fn RaiseException(dwExceptionCode: DWORD,
|
||||
dwExceptionFlags: DWORD,
|
||||
nNumberOfArguments: DWORD,
|
||||
lpArguments: *const ULONG_PTR);
|
||||
|
||||
fn RtlUnwindEx(TargetFrame: LPVOID,
|
||||
TargetIp: LPVOID,
|
||||
ExceptionRecord: *const EXCEPTION_RECORD,
|
||||
ReturnValue: LPVOID,
|
||||
OriginalContext: *const CONTEXT,
|
||||
HistoryTable: *const UNWIND_HISTORY_TABLE);
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct PanicData {
|
||||
data: Box<Any + Send + 'static>
|
||||
}
|
||||
|
||||
pub unsafe fn panic(data: Box<Any + Send + 'static>) -> ! {
|
||||
let panic_ctx = Box::new(PanicData { data: data });
|
||||
let params = [Box::into_raw(panic_ctx) as ULONG_PTR];
|
||||
rtdebug!("panic: ctx={:X}", params[0]);
|
||||
RaiseException(RUST_PANIC,
|
||||
EXCEPTION_NONCONTINUABLE,
|
||||
params.len() as DWORD,
|
||||
¶ms as *const ULONG_PTR);
|
||||
rtabort!("could not unwind stack");
|
||||
}
|
||||
|
||||
pub unsafe fn cleanup(ptr: *mut u8) -> Box<Any + Send + 'static> {
|
||||
rtdebug!("cleanup: ctx={:X}", ptr as usize);
|
||||
let panic_ctx = Box::from_raw(ptr as *mut PanicData);
|
||||
return panic_ctx.data;
|
||||
}
|
||||
|
||||
// SEH doesn't support resuming unwinds after calling a landing pad like
|
||||
// libunwind does. For this reason, MSVC compiler outlines landing pads into
|
||||
// separate functions that can be called directly from the personality function
|
||||
// but are nevertheless able to find and modify stack frame of the "parent"
|
||||
// function.
|
||||
//
|
||||
// Since this cannot be done with libdwarf-style landing pads,
|
||||
// rust_eh_personality instead catches RUST_PANICs, runs the landing pad, then
|
||||
// reraises the exception.
|
||||
//
|
||||
// Note that it makes certain assumptions about the exception:
|
||||
//
|
||||
// 1. That RUST_PANIC is non-continuable, so no lower stack frame may choose to
|
||||
// resume execution.
|
||||
// 2. That the first parameter of the exception is a pointer to an extra data
|
||||
// area (PanicData).
|
||||
// Since these assumptions do not generally hold true for foreign exceptions
|
||||
// (system faults, C++ exceptions, etc), we make no attempt to invoke our
|
||||
// landing pads (and, thus, destructors!) for anything other than RUST_PANICs.
|
||||
// This is considered acceptable, because the behavior of throwing exceptions
|
||||
// through a C ABI boundary is undefined.
|
||||
|
||||
#[lang = "eh_personality_catch"]
|
||||
#[cfg(not(test))]
|
||||
unsafe extern fn rust_eh_personality_catch(
|
||||
exceptionRecord: *mut EXCEPTION_RECORD,
|
||||
establisherFrame: LPVOID,
|
||||
contextRecord: *mut CONTEXT,
|
||||
dispatcherContext: *mut DISPATCHER_CONTEXT
|
||||
) -> EXCEPTION_DISPOSITION
|
||||
{
|
||||
rust_eh_personality(exceptionRecord, establisherFrame,
|
||||
contextRecord, dispatcherContext)
|
||||
}
|
||||
|
||||
#[lang = "eh_personality"]
|
||||
#[cfg(not(test))]
|
||||
unsafe extern fn rust_eh_personality(
|
||||
exceptionRecord: *mut EXCEPTION_RECORD,
|
||||
establisherFrame: LPVOID,
|
||||
contextRecord: *mut CONTEXT,
|
||||
dispatcherContext: *mut DISPATCHER_CONTEXT
|
||||
) -> EXCEPTION_DISPOSITION
|
||||
{
|
||||
let er = &*exceptionRecord;
|
||||
let dc = &*dispatcherContext;
|
||||
rtdebug!("rust_eh_personality: code={:X}, flags={:X}, frame={:X}, ip={:X}",
|
||||
er.ExceptionCode, er.ExceptionFlags,
|
||||
establisherFrame as usize, dc.ControlPc as usize);
|
||||
|
||||
if er.ExceptionFlags & EXCEPTION_UNWIND == 0 { // we are in the dispatch phase
|
||||
if er.ExceptionCode == RUST_PANIC {
|
||||
if let Some(lpad) = find_landing_pad(dc) {
|
||||
rtdebug!("unwinding to landing pad {:X}", lpad);
|
||||
|
||||
RtlUnwindEx(establisherFrame,
|
||||
lpad as LPVOID,
|
||||
exceptionRecord,
|
||||
er.ExceptionInformation[0] as LPVOID, // pointer to PanicData
|
||||
contextRecord,
|
||||
dc.HistoryTable);
|
||||
rtabort!("could not unwind");
|
||||
}
|
||||
}
|
||||
}
|
||||
ExceptionContinueSearch
|
||||
}
|
||||
|
||||
// The `resume` instruction, found at the end of the landing pads, and whose job
|
||||
// is to resume stack unwinding, is typically lowered by LLVM into a call to
|
||||
// `_Unwind_Resume` routine. To avoid confusion with the same symbol exported
|
||||
// from libgcc, we redirect it to `rust_eh_unwind_resume`.
|
||||
// Since resolution of this symbol is done by the linker, `rust_eh_unwind_resume`
|
||||
// must be marked `pub` + `#[no_mangle]`. (Can we make it a lang item?)
|
||||
|
||||
#[lang = "eh_unwind_resume"]
|
||||
#[cfg(not(test))]
|
||||
unsafe extern fn rust_eh_unwind_resume(panic_ctx: LPVOID) {
|
||||
rtdebug!("rust_eh_unwind_resume: ctx={:X}", panic_ctx as usize);
|
||||
let params = [panic_ctx as ULONG_PTR];
|
||||
RaiseException(RUST_PANIC,
|
||||
EXCEPTION_NONCONTINUABLE,
|
||||
params.len() as DWORD,
|
||||
¶ms as *const ULONG_PTR);
|
||||
rtabort!("could not resume unwind");
|
||||
}
|
||||
|
||||
unsafe fn find_landing_pad(dc: &DISPATCHER_CONTEXT) -> Option<usize> {
|
||||
let eh_ctx = eh::EHContext {
|
||||
ip: dc.ControlPc as usize,
|
||||
func_start: dc.ImageBase as usize + (*dc.FunctionEntry).BeginAddress as usize,
|
||||
text_start: dc.ImageBase as usize,
|
||||
data_start: 0
|
||||
};
|
||||
eh::find_landing_pad(dc.HandlerData, &eh_ctx)
|
||||
}
|
@ -21,6 +21,9 @@ extern fn stack_exhausted() {}
|
||||
#[lang = "eh_personality"]
|
||||
extern fn eh_personality() {}
|
||||
|
||||
#[lang = "eh_unwind_resume"]
|
||||
extern fn eh_unwind_resume() {}
|
||||
|
||||
#[lang = "panic_fmt"]
|
||||
extern fn rust_begin_unwind(msg: core::fmt::Arguments, file: &'static str,
|
||||
line: u32) -> ! {
|
||||
|
@ -23,4 +23,5 @@ fn main() {
|
||||
|
||||
#[lang = "stack_exhausted"] extern fn stack_exhausted() {}
|
||||
#[lang = "eh_personality"] extern fn eh_personality() {}
|
||||
#[lang = "eh_unwind_resume"] extern fn eh_unwind_resume() {}
|
||||
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }
|
||||
|
@ -1,8 +1,6 @@
|
||||
-include ../tools.mk
|
||||
|
||||
ifndef IS_WINDOWS
|
||||
EXTRAFLAGS := $(EXTRACFLAGS)
|
||||
endif
|
||||
|
||||
# FIXME: ignore freebsd
|
||||
ifneq ($(shell uname),FreeBSD)
|
||||
|
@ -19,4 +19,5 @@ pub extern fn bar() {}
|
||||
|
||||
#[lang = "stack_exhausted"] fn stack_exhausted() {}
|
||||
#[lang = "eh_personality"] fn eh_personality() {}
|
||||
#[lang = "eh_unwind_resume"] fn eh_unwind_resume() {}
|
||||
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }
|
||||
|
@ -19,4 +19,6 @@ pub extern fn foo() {}
|
||||
|
||||
#[lang = "stack_exhausted"] fn stack_exhausted() {}
|
||||
#[lang = "eh_personality"] fn eh_personality() {}
|
||||
#[lang = "eh_unwind_resume"] fn eh_unwind_resume() {}
|
||||
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }
|
||||
|
||||
|
@ -22,6 +22,7 @@ extern "rust-intrinsic" { fn transmute<T, U>(t: T) -> U; }
|
||||
|
||||
#[lang = "stack_exhausted"] extern fn stack_exhausted() {}
|
||||
#[lang = "eh_personality"] extern fn eh_personality() {}
|
||||
#[lang = "eh_unwind_resume"] extern fn eh_unwind_resume() {}
|
||||
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }
|
||||
|
||||
#[start]
|
||||
|
Loading…
x
Reference in New Issue
Block a user