diff --git a/src/doc/trpl/lang-items.md b/src/doc/trpl/lang-items.md index 8e7504c2f18ea..39de8920f098c 100644 --- a/src/doc/trpl/lang-items.md +++ b/src/doc/trpl/lang-items.md @@ -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 diff --git a/src/doc/trpl/no-stdlib.md b/src/doc/trpl/no-stdlib.md index 0a985334b5e4b..e530a9f1051a5 100644 --- a/src/doc/trpl/no-stdlib.md +++ b/src/doc/trpl/no-stdlib.md @@ -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() {} ``` diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index f7cd94f30af12..da1b9f48eda72 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -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; diff --git a/src/librustc/middle/weak_lang_items.rs b/src/librustc/middle/weak_lang_items.rs index 72fda9a7ae06a..934f7c0688c17 100644 --- a/src/librustc/middle/weak_lang_items.rs +++ b/src/librustc/middle/weak_lang_items.rs @@ -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; } diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs index 39e42913ff674..ce05a8878ff4b 100644 --- a/src/librustc_back/target/mod.rs +++ b/src/librustc_back/target/mod.rs @@ -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, } } } diff --git a/src/librustc_back/target/x86_64_pc_windows_gnu.rs b/src/librustc_back/target/x86_64_pc_windows_gnu.rs index e4d7b4bc9b024..aef1d7471b85b 100644 --- a/src/librustc_back/target/x86_64_pc_windows_gnu.rs +++ b/src/librustc_back/target/x86_64_pc_windows_gnu.rs @@ -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(), diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 61e81d75607cf..a5f2306aac806 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -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>, diff --git a/src/librustc_trans/trans/cleanup.rs b/src/librustc_trans/trans/cleanup.rs index b4b0472512e61..5e472e45775d0 100644 --- a/src/librustc_trans/trans/cleanup.rs +++ b/src/librustc_trans/trans/cleanup.rs @@ -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(); diff --git a/src/librustc_trans/trans/common.rs b/src/librustc_trans/trans/common.rs index 9f65050097ded..d0b81b38ab7a7 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -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 diff --git a/src/librustc_trans/trans/context.rs b/src/librustc_trans/trans/context.rs index 235538f62c245..b7b7b28a42bfb 100644 --- a/src/librustc_trans/trans/context.rs +++ b/src/librustc_trans/trans/context.rs @@ -146,6 +146,7 @@ pub struct LocalCrateContext<'tcx> { eh_personality: RefCell>, rust_try_fn: RefCell>, + unwind_resume_hooked: Cell, intrinsics: RefCell>, @@ -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 { + &self.local.unwind_resume_hooked + } + fn intrinsics<'a>(&'a self) -> &'a RefCell> { &self.local.intrinsics } diff --git a/src/librustc_trans/trans/intrinsic.rs b/src/librustc_trans/trans/intrinsic.rs index 32adcaa3a4167..0400771dff15c 100644 --- a/src/librustc_trans/trans/intrinsic.rs +++ b/src/librustc_trans/trans/intrinsic.rs @@ -16,6 +16,7 @@ use llvm::{SequentiallyConsistent, Acquire, Release, AtomicXchg, ValueRef, TypeK use middle::subst; use middle::subst::FnSpace; use trans::adt; +use trans::attributes; use trans::base::*; use trans::build::*; use trans::callee; @@ -1159,26 +1160,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 +1177,63 @@ 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); + attributes::emit_uwtable(rust_try, true); 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); + fcx.cleanup(); + + 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 +1243,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 { diff --git a/src/libstd/rt/dwarf/eh.rs b/src/libstd/rt/dwarf/eh.rs new file mode 100644 index 0000000000000..990501b28dba1 --- /dev/null +++ b/src/libstd/rt/dwarf/eh.rs @@ -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 or the MIT license +// , 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 +//! (/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 { + if lsda.is_null() { + return None; + } + + let func_start = context.func_start; + let mut reader = DwarfReader::new(lsda); + + let start_encoding = reader.read::(); + // 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::(); + 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::(); + 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::()) as *const u8; + return reader.read::(); + } + + let mut result = match encoding & 0x0F { + DW_EH_PE_absptr => reader.read::(), + DW_EH_PE_uleb128 => reader.read_uleb128() as usize, + DW_EH_PE_udata2 => reader.read::() as usize, + DW_EH_PE_udata4 => reader.read::() as usize, + DW_EH_PE_udata8 => reader.read::() as usize, + DW_EH_PE_sleb128 => reader.read_sleb128() as usize, + DW_EH_PE_sdata2 => reader.read::() as usize, + DW_EH_PE_sdata4 => reader.read::() as usize, + DW_EH_PE_sdata8 => reader.read::() 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 +} diff --git a/src/libstd/rt/dwarf/mod.rs b/src/libstd/rt/dwarf/mod.rs new file mode 100644 index 0000000000000..822826bcc837f --- /dev/null +++ b/src/libstd/rt/dwarf/mod.rs @@ -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 or the MIT license +// , 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); + +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(&mut self) -> T { + let Unaligned(result) = *(self.ptr as *const Unaligned); + self.ptr = self.ptr.offset(mem::size_of::() 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::(); + 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::(); + result |= ((byte & 0x7F) as u64) << shift; + shift += 7; + if byte & 0x80 == 0 { + break; + } + } + // sign-extend + if shift < 8 * mem::size_of::() && (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::to_be(1u8)); + assert!(reader.read::() == u16::to_be(0x0203)); + assert!(reader.read::() == u32::to_be(0x04050607)); + + assert!(reader.read_uleb128() == 624485); + assert!(reader.read_sleb128() == -624485); + + assert!(reader.read::() == i8::to_be(-1)); + } +} diff --git a/src/libstd/rt/libunwind.rs b/src/libstd/rt/libunwind.rs index d99b31c9f2b43..fde612014e992 100644 --- a/src/libstd/rt/libunwind.rs +++ b/src/libstd/rt/libunwind.rs @@ -36,6 +36,7 @@ pub enum _Unwind_Action { #[cfg(target_arch = "arm")] #[repr(C)] +#[derive(Copy, Clone)] pub enum _Unwind_State { _US_VIRTUAL_UNWIND_FRAME = 0, _US_UNWIND_FRAME_STARTING = 1, @@ -46,6 +47,7 @@ pub enum _Unwind_State { } #[repr(C)] +#[derive(Copy, Clone)] pub enum _Unwind_Reason_Code { _URC_NO_REASON = 0, _URC_FOREIGN_EXCEPTION_CAUGHT = 1, diff --git a/src/libstd/rt/macros.rs b/src/libstd/rt/macros.rs index 1e3ab6d34dab9..414ccc911afeb 100644 --- a/src/libstd/rt/macros.rs +++ b/src/libstd/rt/macros.rs @@ -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),*) } }) } diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index 7e86bb775a1b0..56bf73db3992e 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -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; diff --git a/src/libstd/rt/unwind/gcc.rs b/src/libstd/rt/unwind/gcc.rs index 23e10ee6c39e3..55deb048b7ee5 100644 --- a/src/libstd/rt/unwind/gcc.rs +++ b/src/libstd/rt/unwind/gcc.rs @@ -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 @@ -183,8 +179,8 @@ pub mod eabi { } else { // cleanup phase unsafe { - __gcc_personality_sj0(_version, actions, _exception_class, _ue_header, - _context) + __gcc_personality_sj0(version, actions, exception_class, ue_header, + context) } } } @@ -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 - } - } -} - -// 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) - } } } - diff --git a/src/libstd/rt/unwind/mod.rs b/src/libstd/rt/unwind/mod.rs index 60eced014de1b..59b2e14643d31 100644 --- a/src/libstd/rt/unwind/mod.rs +++ b/src/libstd/rt/unwind/mod.rs @@ -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; + +// 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); diff --git a/src/libstd/rt/unwind/seh64_gnu.rs b/src/libstd/rt/unwind/seh64_gnu.rs new file mode 100644 index 0000000000000..6a061a55fc2a6 --- /dev/null +++ b/src/libstd/rt/unwind/seh64_gnu.rs @@ -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 or the MIT license +// , 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 +} + +pub unsafe fn panic(data: Box) -> ! { + 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 { + 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 { + 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) +} diff --git a/src/test/auxiliary/lang-item-public.rs b/src/test/auxiliary/lang-item-public.rs index 4b60a370187af..fa5f7f48ab443 100644 --- a/src/test/auxiliary/lang-item-public.rs +++ b/src/test/auxiliary/lang-item-public.rs @@ -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) -> ! { diff --git a/src/test/compile-fail/no_owned_box_lang_item.rs b/src/test/compile-fail/no_owned_box_lang_item.rs index 49b5b5519d84f..b25367013755a 100644 --- a/src/test/compile-fail/no_owned_box_lang_item.rs +++ b/src/test/compile-fail/no_owned_box_lang_item.rs @@ -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 {} } diff --git a/src/test/run-make/c-link-to-rust-staticlib/Makefile b/src/test/run-make/c-link-to-rust-staticlib/Makefile index 56c09b895f141..2b927e3e4a730 100644 --- a/src/test/run-make/c-link-to-rust-staticlib/Makefile +++ b/src/test/run-make/c-link-to-rust-staticlib/Makefile @@ -1,8 +1,6 @@ -include ../tools.mk -ifndef IS_WINDOWS EXTRAFLAGS := $(EXTRACFLAGS) -endif # FIXME: ignore freebsd ifneq ($(shell uname),FreeBSD) diff --git a/src/test/run-make/no-duplicate-libs/bar.rs b/src/test/run-make/no-duplicate-libs/bar.rs index 29f52f97a8887..2c92778f59ac6 100644 --- a/src/test/run-make/no-duplicate-libs/bar.rs +++ b/src/test/run-make/no-duplicate-libs/bar.rs @@ -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 {} } diff --git a/src/test/run-make/no-duplicate-libs/foo.rs b/src/test/run-make/no-duplicate-libs/foo.rs index ae424c6569dbe..b3a36ec3efd81 100644 --- a/src/test/run-make/no-duplicate-libs/foo.rs +++ b/src/test/run-make/no-duplicate-libs/foo.rs @@ -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 {} } + diff --git a/src/test/run-pass/smallest-hello-world.rs b/src/test/run-pass/smallest-hello-world.rs index 5e84ce19de1ef..67affb5ae090e 100644 --- a/src/test/run-pass/smallest-hello-world.rs +++ b/src/test/run-pass/smallest-hello-world.rs @@ -22,6 +22,7 @@ extern "rust-intrinsic" { fn transmute(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]