#[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
#[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!
```
#[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!
```
#[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() {}
```
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;
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 };
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;
}
/// 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 {
pre_link_objects: Vec::new(),
post_link_objects: Vec::new(),
archive_format: String::new(),
+ custom_unwind_resume: false,
}
}
}
// 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(),
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>,
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();
}
}
}
+
+ /// 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
eh_personality: RefCell<Option<ValueRef>>,
rust_try_fn: RefCell<Option<ValueRef>>,
+ unwind_resume_hooked: Cell<bool>,
intrinsics: RefCell<FnvHashMap<&'static str, ValueRef>>,
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()),
&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
}
// 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,
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)
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 {
--- /dev/null
+// 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
+}
--- /dev/null
+// 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));
+ }
+}
::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),*))
} )
}
} );
($str:expr, $($arg:expr),*) => ( {
if cfg!(rtdebug) {
- rterrln!($str, $($arg)*)
+ rterrln!($str, $($arg),*)
}
})
}
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;
// 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")),
#[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
{
uw::_URC_HANDLER_FOUND // catch!
}
else { // cleanup phase
- uw::_URC_INSTALL_CONTEXT
+ unsafe {
+ __gcc_personality_v0(version, actions, exception_class, ue_header,
+ context)
+ }
}
}
}
#[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
#[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)
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)
- }
}
}
-
//! "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
//!
// 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);
--- /dev/null
+// 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)
+}
#[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) -> ! {
#[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)
#[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 {} }
#[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 {} }
+
#[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]