提交 6112b220 编写于 作者: V Vadim Chugunov

Implement Win64 eh_personality natively.

上级 28869d45
......@@ -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 @@ pub fn collect_language_items(krate: &ast::Crate,
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 @@ fn visit_foreign_item(&mut self, i: &ast::ForeignItem) {
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 @@ fn default() -> 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 @@ fn get_or_create_landing_pad(&'blk self) -> BasicBlockRef {
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 @@ pub fn eh_personality(&self) -> ValueRef {
}
}
}
/// 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 @@ fn new<'a>(shared: &SharedCrateContext<'a, '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 @@ pub fn rust_try_fn<'a>(&'a self) -> &'a RefCell<Option<ValueRef>> {
&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,108 +1176,61 @@ 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)
});
return rust_try
});
// Note that no invoke is used here because by definition this function
// can't panic (that's what it's catching).
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,
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 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);
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);
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);
}
Ret(catch, ptr, dloc);
return rust_try
});
// Note that no invoke is used here because by definition this function
// can't panic (that's what it's catching).
let ret = Call(bcx, llfn, &[func, data], None, dloc);
Store(bcx, ret, dest);
return bcx;
}
// 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 {
......
// 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
}
// 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 @@
::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 @@
} );
($str:expr, $($arg:expr),*) => ( {
if cfg!(rtdebug) {
rterrln!($str, $($arg)*)
rterrln!($str, $($arg),*)
}
})
}
......
......@@ -47,6 +47,8 @@
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 @@ fn __gcc_personality_v0(version: c_int,
#[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 @@ fn __gcc_personality_v0(version: c_int,
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 @@ fn __gcc_personality_sj0(version: c_int,
#[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 @@ fn __gcc_personality_v0(state: uw::_Unwind_State,
#[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 @@ fn __gcc_personality_v0(state: uw::_Unwind_State,
uw::_URC_HANDLER_FOUND // catch!
}
else { // cleanup phase
uw::_URC_INSTALL_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_personality_v0(state, ue_header, 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 @@
// 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;
// x86_64-pc-windows-gnu
#[cfg(all(windows, target_arch="x86_64", target_env="gnu"))]
#[path = "seh64_gnu.rs"] #[doc(hidden)]
pub mod imp;
#[cfg(not(target_env = "msvc"))] #[path = "gcc.rs"] #[doc(hidden)]
// 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);
......
// 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,
&params 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,
&params 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 @@
#[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 {} }
-include ../tools.mk
ifndef IS_WINDOWS
EXTRAFLAGS := $(EXTRACFLAGS)
endif
# FIXME: ignore freebsd
ifneq ($(shell uname),FreeBSD)
......
......@@ -19,4 +19,5 @@
#[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 @@
#[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 @@
#[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]
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册