]> git.lizzy.rs Git - rust.git/commitdiff
Implement Win64 eh_personality natively.
authorVadim Chugunov <vadimcn@gmail.com>
Tue, 14 Jul 2015 01:11:44 +0000 (18:11 -0700)
committerVadim Chugunov <vadimcn@gmail.com>
Thu, 30 Jul 2015 18:35:16 +0000 (11:35 -0700)
24 files changed:
src/doc/trpl/lang-items.md
src/doc/trpl/no-stdlib.md
src/librustc/middle/lang_items.rs
src/librustc/middle/weak_lang_items.rs
src/librustc_back/target/mod.rs
src/librustc_back/target/x86_64_pc_windows_gnu.rs
src/librustc_trans/trans/base.rs
src/librustc_trans/trans/cleanup.rs
src/librustc_trans/trans/common.rs
src/librustc_trans/trans/context.rs
src/librustc_trans/trans/intrinsic.rs
src/libstd/rt/dwarf/eh.rs [new file with mode: 0644]
src/libstd/rt/dwarf/mod.rs [new file with mode: 0644]
src/libstd/rt/macros.rs
src/libstd/rt/mod.rs
src/libstd/rt/unwind/gcc.rs
src/libstd/rt/unwind/mod.rs
src/libstd/rt/unwind/seh64_gnu.rs [new file with mode: 0644]
src/test/auxiliary/lang-item-public.rs
src/test/compile-fail/no_owned_box_lang_item.rs
src/test/run-make/c-link-to-rust-staticlib/Makefile
src/test/run-make/no-duplicate-libs/bar.rs
src/test/run-make/no-duplicate-libs/foo.rs
src/test/run-pass/smallest-hello-world.rs

index 8e7504c2f18ea5eb76bba3b5f02857ae98a5e17c..39de8920f098cb34cd7e77a3fc93162e3929b957 100644 (file)
@@ -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
index 0a985334b5e4b694d797eb673986bc4fdebbbbe2..e530a9f1051a5ea02ff6ca0fa7941eec57e61990 100644 (file)
@@ -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() {}
 ```
index f7cd94f30af12e2a30643fbc25df39201109893b..da1b9f48eda72ea48d39883fc6047111a5a18c15 100644 (file)
@@ -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;
index 72fda9a7ae06ac236f82eda442dc6f2c4bf42f95..934f7c0688c172fd95cd49aff0d317c887fecdd6 100644 (file)
@@ -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;
 }
index 39e42913ff6745e4389001908b80804bb95e2a16..ce05a8878ff4b9e0145374f7bce9fdcdeca26f3b 100644 (file)
@@ -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,
         }
     }
 }
index e4d7b4bc9b024fab54df64f1d57958039c556098..aef1d7471b85b1d415f2d723dcb66bf11b2d2bf0 100644 (file)
@@ -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(),
index 61e81d75607cf702caae9432d3e1b245a5271b45..a5f2306aac80613c945a6dd9160fffb3d980302a 100644 (file)
@@ -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>,
index b4b0472512e61a5eb165db96295f0c150630a0f5..5e472e45775d0fc469ea40f6ccbf7bc81bbf7f10 100644 (file)
@@ -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();
index 9f65050097ded009a66802a31eafb12f72495a08..d0b81b38ab7a728923e82f95ed9af8c0ceb0e47c 100644 (file)
@@ -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
index 235538f62c24554ce06a5e3b44ae85e0a898d8a3..b7b7b28a42bfb3d9c21a6d5213dc2886dc48a712 100644 (file)
@@ -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
     }
index 32adcaa3a416771f7b9f3647313e92f01f70fa6b..e0062493e0a5f92cf9a4272699fac98697d0f62c 100644 (file)
@@ -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 {
diff --git a/src/libstd/rt/dwarf/eh.rs b/src/libstd/rt/dwarf/eh.rs
new file mode 100644 (file)
index 0000000..990501b
--- /dev/null
@@ -0,0 +1,159 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Parsing of GCC-style Language-Specific Data Area (LSDA)
+//! For details see:
+//!   http://refspecs.linuxfoundation.org/LSB_3.0.0/LSB-PDA/LSB-PDA/ehframechpt.html
+//!   http://mentorembedded.github.io/cxx-abi/exceptions.pdf
+//!   http://www.airs.com/blog/archives/460
+//!   http://www.airs.com/blog/archives/464
+//!
+//! A reference implementation may be found in the GCC source tree
+//! (<root>/libgcc/unwind-c.c as of this writing)
+
+#![allow(non_upper_case_globals)]
+#![allow(unused)]
+
+use prelude::v1::*;
+use rt::dwarf::DwarfReader;
+use core::mem;
+
+pub const DW_EH_PE_omit     : u8 = 0xFF;
+pub const DW_EH_PE_absptr   : u8 = 0x00;
+
+pub const DW_EH_PE_uleb128  : u8 = 0x01;
+pub const DW_EH_PE_udata2   : u8 = 0x02;
+pub const DW_EH_PE_udata4   : u8 = 0x03;
+pub const DW_EH_PE_udata8   : u8 = 0x04;
+pub const DW_EH_PE_sleb128  : u8 = 0x09;
+pub const DW_EH_PE_sdata2   : u8 = 0x0A;
+pub const DW_EH_PE_sdata4   : u8 = 0x0B;
+pub const DW_EH_PE_sdata8   : u8 = 0x0C;
+
+pub const DW_EH_PE_pcrel    : u8 = 0x10;
+pub const DW_EH_PE_textrel  : u8 = 0x20;
+pub const DW_EH_PE_datarel  : u8 = 0x30;
+pub const DW_EH_PE_funcrel  : u8 = 0x40;
+pub const DW_EH_PE_aligned  : u8 = 0x50;
+
+pub const DW_EH_PE_indirect : u8 = 0x80;
+
+#[derive(Copy, Clone)]
+pub struct EHContext {
+    pub ip: usize,         // Current instruction pointer
+    pub func_start: usize, // Address of the current function
+    pub text_start: usize, // Address of the code section
+    pub data_start: usize, // Address of the data section
+}
+
+pub unsafe fn find_landing_pad(lsda: *const u8, context: &EHContext)
+                               -> Option<usize> {
+    if lsda.is_null() {
+        return None;
+    }
+
+    let func_start = context.func_start;
+    let mut reader = DwarfReader::new(lsda);
+
+    let start_encoding = reader.read::<u8>();
+    // base address for landing pad offsets
+    let lpad_base = if start_encoding != DW_EH_PE_omit {
+        read_encoded_pointer(&mut reader, context, start_encoding)
+    } else {
+        func_start
+    };
+
+    let ttype_encoding = reader.read::<u8>();
+    if ttype_encoding != DW_EH_PE_omit {
+        // Rust doesn't analyze exception types, so we don't care about the type table
+        reader.read_uleb128();
+    }
+
+    let call_site_encoding = reader.read::<u8>();
+    let call_site_table_length = reader.read_uleb128();
+    let action_table = reader.ptr.offset(call_site_table_length as isize);
+    // Return addresses point 1 byte past the call instruction, which could
+    // be in the next IP range.
+    let ip = context.ip-1;
+
+    while reader.ptr < action_table {
+        let cs_start = read_encoded_pointer(&mut reader, context, call_site_encoding);
+        let cs_len = read_encoded_pointer(&mut reader, context, call_site_encoding);
+        let cs_lpad = read_encoded_pointer(&mut reader, context, call_site_encoding);
+        let cs_action = reader.read_uleb128();
+        // Callsite table is sorted by cs_start, so if we've passed the ip, we
+        // may stop searching.
+        if ip < func_start + cs_start {
+            break
+        }
+        if ip < func_start + cs_start + cs_len {
+            if cs_lpad != 0 {
+                return Some(lpad_base + cs_lpad);
+            } else {
+                return None;
+            }
+        }
+    }
+    // IP range not found: gcc's C++ personality calls terminate() here,
+    // however the rest of the languages treat this the same as cs_lpad == 0.
+    // We follow this suit.
+    return None;
+}
+
+#[inline]
+fn round_up(unrounded: usize, align: usize) -> usize {
+    assert!(align.is_power_of_two());
+    (unrounded + align - 1) & !(align - 1)
+}
+
+unsafe fn read_encoded_pointer(reader: &mut DwarfReader,
+                               context: &EHContext,
+                               encoding: u8) -> usize {
+    assert!(encoding != DW_EH_PE_omit);
+
+    // DW_EH_PE_aligned implies it's an absolute pointer value
+    if encoding == DW_EH_PE_aligned {
+        reader.ptr = round_up(reader.ptr as usize,
+                              mem::size_of::<usize>()) as *const u8;
+        return reader.read::<usize>();
+    }
+
+    let mut result = match encoding & 0x0F {
+        DW_EH_PE_absptr => reader.read::<usize>(),
+        DW_EH_PE_uleb128 => reader.read_uleb128() as usize,
+        DW_EH_PE_udata2 => reader.read::<u16>() as usize,
+        DW_EH_PE_udata4 => reader.read::<u32>() as usize,
+        DW_EH_PE_udata8 => reader.read::<u64>() as usize,
+        DW_EH_PE_sleb128 => reader.read_sleb128() as usize,
+        DW_EH_PE_sdata2 => reader.read::<i16>() as usize,
+        DW_EH_PE_sdata4 => reader.read::<i32>() as usize,
+        DW_EH_PE_sdata8 => reader.read::<i64>() as usize,
+        _ => panic!()
+    };
+
+    result += match encoding & 0x70 {
+        DW_EH_PE_absptr => 0,
+        // relative to address of the encoded value, despite the name
+        DW_EH_PE_pcrel => reader.ptr as usize,
+        DW_EH_PE_textrel => { assert!(context.text_start != 0);
+                              context.text_start },
+        DW_EH_PE_datarel => { assert!(context.data_start != 0);
+                              context.data_start },
+        DW_EH_PE_funcrel => { assert!(context.func_start != 0);
+                              context.func_start },
+        _ => panic!()
+    };
+
+    if encoding & DW_EH_PE_indirect != 0 {
+        result = *(result as *const usize);
+    }
+
+    result
+}
diff --git a/src/libstd/rt/dwarf/mod.rs b/src/libstd/rt/dwarf/mod.rs
new file mode 100644 (file)
index 0000000..822826b
--- /dev/null
@@ -0,0 +1,107 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Utilities for parsing DWARF-encoded data streams.
+//! See http://www.dwarfstd.org,
+//! DWARF-4 standard, Section 7 - "Data Representation"
+
+// This module is used only by x86_64-pc-windows-gnu for now, but we
+// are compiling it everywhere to avoid regressions.
+#![allow(unused)]
+
+pub mod eh;
+
+use prelude::v1::*;
+use core::mem;
+
+pub struct DwarfReader {
+    pub ptr : *const u8
+}
+
+#[repr(C,packed)]
+struct Unaligned<T>(T);
+
+impl DwarfReader {
+
+    pub fn new(ptr : *const u8) -> DwarfReader {
+        DwarfReader {
+            ptr : ptr
+        }
+    }
+
+    // DWARF streams are packed, so e.g. a u32 would not necessarily be aligned
+    // on a 4-byte boundary. This may cause problems on platforms with strict
+    // alignment requirements. By wrapping data in a "packed" struct, we are
+    // telling the backend to generate "misalignment-safe" code.
+    pub unsafe fn read<T:Copy>(&mut self) -> T {
+        let Unaligned(result) = *(self.ptr as *const Unaligned<T>);
+        self.ptr = self.ptr.offset(mem::size_of::<T>() as isize);
+        result
+    }
+
+    // ULEB128 and SLEB128 encodings are defined in Section 7.6 - "Variable
+    // Length Data".
+    pub unsafe fn read_uleb128(&mut self) -> u64 {
+        let mut shift : usize = 0;
+        let mut result : u64 = 0;
+        let mut byte : u8;
+        loop {
+            byte = self.read::<u8>();
+            result |= ((byte & 0x7F) as u64) << shift;
+            shift += 7;
+            if byte & 0x80 == 0 {
+                break;
+            }
+        }
+        result
+    }
+
+    pub unsafe fn read_sleb128(&mut self) -> i64 {
+        let mut shift : usize = 0;
+        let mut result : u64 = 0;
+        let mut byte : u8;
+        loop {
+            byte = self.read::<u8>();
+            result |= ((byte & 0x7F) as u64) << shift;
+            shift += 7;
+            if byte & 0x80 == 0 {
+                break;
+            }
+        }
+        // sign-extend
+        if shift < 8 * mem::size_of::<u64>() && (byte & 0x40) != 0 {
+            result |= (!0 as u64) << shift;
+        }
+        result as i64
+    }
+}
+
+#[test]
+fn dwarf_reader() {
+    let encoded: &[u8] = &[1,
+                           2, 3,
+                           4, 5, 6, 7,
+                           0xE5, 0x8E, 0x26,
+                           0x9B, 0xF1, 0x59,
+                           0xFF, 0xFF];
+
+    let mut reader = DwarfReader::new(encoded.as_ptr());
+
+    unsafe {
+        assert!(reader.read::<u8>() == u8::to_be(1u8));
+        assert!(reader.read::<u16>() == u16::to_be(0x0203));
+        assert!(reader.read::<u32>() == u32::to_be(0x04050607));
+
+        assert!(reader.read_uleb128() == 624485);
+        assert!(reader.read_sleb128() == -624485);
+
+        assert!(reader.read::<i8>() == i8::to_be(-1));
+    }
+}
index 1e3ab6d34dab9daecb05e14357c99ce7413e7a6b..414ccc911afebc4693220ca8c7d03b4ea1bd27ba 100644 (file)
@@ -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),*)
         }
     })
 }
index 7e86bb775a1b0fe517107eec30c17852f687654f..56bf73db3992e0be77679c268047b74f5fe8d116 100644 (file)
@@ -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;
index 23e10ee6c39e3b318fafb0df514c025c222002d0..eddc0e118ead45968b3e1a1829bf27a20f062376 100644 (file)
@@ -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)
-        }
     }
 }
-
index 60eced014de1b27aa4311d34a39dc199847684ac..59b2e14643d31fb28e64299cd27870896dd8c783 100644 (file)
@@ -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
 //!
 // 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 (file)
index 0000000..6a061a5
--- /dev/null
@@ -0,0 +1,227 @@
+// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Unwinding implementation of top of native Win64 SEH,
+//! however the unwind handler data (aka LSDA) uses GCC-compatible encoding.
+
+#![allow(bad_style)]
+#![allow(private_no_mangle_fns)]
+
+use prelude::v1::*;
+
+use any::Any;
+use self::EXCEPTION_DISPOSITION::*;
+use rt::dwarf::eh;
+use core::mem;
+use core::ptr;
+use simd;
+use libc::{c_void, c_ulonglong, DWORD, LPVOID};
+type ULONG_PTR = c_ulonglong;
+
+// Define our exception codes:
+// according to http://msdn.microsoft.com/en-us/library/het71c37(v=VS.80).aspx,
+//    [31:30] = 3 (error), 2 (warning), 1 (info), 0 (success)
+//    [29]    = 1 (user-defined)
+//    [28]    = 0 (reserved)
+// we define bits:
+//    [24:27] = type
+//    [0:23]  = magic
+const ETYPE: DWORD = 0b1110_u32 << 28;
+const MAGIC: DWORD = 0x525354; // "RST"
+
+const RUST_PANIC: DWORD  = ETYPE | (1 << 24) | MAGIC;
+
+const EXCEPTION_NONCONTINUABLE: DWORD = 0x1;   // Noncontinuable exception
+const EXCEPTION_UNWINDING: DWORD = 0x2;        // Unwind is in progress
+const EXCEPTION_EXIT_UNWIND: DWORD = 0x4;      // Exit unwind is in progress
+const EXCEPTION_STACK_INVALID: DWORD = 0x8;    // Stack out of limits or unaligned
+const EXCEPTION_NESTED_CALL: DWORD = 0x10;     // Nested exception handler call
+const EXCEPTION_TARGET_UNWIND: DWORD = 0x20;   // Target unwind in progress
+const EXCEPTION_COLLIDED_UNWIND: DWORD = 0x40; // Collided exception handler call
+const EXCEPTION_UNWIND: DWORD = EXCEPTION_UNWINDING |
+                                EXCEPTION_EXIT_UNWIND |
+                                EXCEPTION_TARGET_UNWIND |
+                                EXCEPTION_COLLIDED_UNWIND;
+
+#[repr(C)]
+pub struct EXCEPTION_RECORD {
+    ExceptionCode: DWORD,
+    ExceptionFlags: DWORD,
+    ExceptionRecord: *const EXCEPTION_RECORD,
+    ExceptionAddress: LPVOID,
+    NumberParameters: DWORD,
+    ExceptionInformation: [ULONG_PTR; 15],
+}
+
+pub type CONTEXT = c_void;
+pub type UNWIND_HISTORY_TABLE = c_void;
+
+#[repr(C)]
+pub struct RUNTIME_FUNCTION {
+    BeginAddress: DWORD,
+    EndAddress: DWORD,
+    UnwindData: DWORD,
+}
+
+#[repr(C)]
+pub struct DISPATCHER_CONTEXT {
+    ControlPc: LPVOID,
+    ImageBase: LPVOID,
+    FunctionEntry: *const RUNTIME_FUNCTION,
+    EstablisherFrame: LPVOID,
+    TargetIp: LPVOID,
+    ContextRecord: *const CONTEXT,
+    LanguageHandler: LPVOID,
+    HandlerData: *const u8,
+    HistoryTable: *const UNWIND_HISTORY_TABLE,
+}
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub enum EXCEPTION_DISPOSITION {
+    ExceptionContinueExecution,
+    ExceptionContinueSearch,
+    ExceptionNestedException,
+    ExceptionCollidedUnwind
+}
+
+// From kernel32.dll
+extern "system" {
+    fn RaiseException(dwExceptionCode: DWORD,
+                      dwExceptionFlags: DWORD,
+                      nNumberOfArguments: DWORD,
+                      lpArguments: *const ULONG_PTR);
+
+    fn RtlUnwindEx(TargetFrame: LPVOID,
+                   TargetIp: LPVOID,
+                   ExceptionRecord: *const EXCEPTION_RECORD,
+                   ReturnValue: LPVOID,
+                   OriginalContext: *const CONTEXT,
+                   HistoryTable: *const UNWIND_HISTORY_TABLE);
+}
+
+#[repr(C)]
+struct PanicData {
+    data: Box<Any + Send + 'static>
+}
+
+pub unsafe fn panic(data: Box<Any + Send + 'static>) -> ! {
+    let panic_ctx = Box::new(PanicData { data: data });
+    let params = [Box::into_raw(panic_ctx) as ULONG_PTR];
+    rtdebug!("panic: ctx={:X}", params[0]);
+    RaiseException(RUST_PANIC,
+                   EXCEPTION_NONCONTINUABLE,
+                   params.len() as DWORD,
+                   &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)
+}
index 4b60a370187af15bbb3ea1b05edc1c3aa11fd83f..fa5f7f48ab44366fda43c156f7a9127f6136c69e 100644 (file)
@@ -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) -> ! {
index 49b5b5519d84f208064b0a76a132611b0ac0f6ba..b25367013755a1a765965bec93ea0a2fbcb3dfe8 100644 (file)
@@ -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 {} }
index 56c09b895f1417005d324c4e326f2f8cdcec3bdc..2b927e3e4a730835bebe04f11dd0f37f2c10b104 100644 (file)
@@ -1,8 +1,6 @@
 -include ../tools.mk
 
-ifndef IS_WINDOWS
 EXTRAFLAGS := $(EXTRACFLAGS)
-endif
 
 # FIXME: ignore freebsd
 ifneq ($(shell uname),FreeBSD)
index 29f52f97a88878f0b8c8ab821ff683ac32b3578b..2c92778f59ac69dc13c5b4a939dbe55e65aa276c 100644 (file)
@@ -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 {} }
index ae424c6569dbe3f976c61522148dbd0c92738957..b3a36ec3efd8116a80adc56f6ce5ac0af0f6a68a 100644 (file)
@@ -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 {} }
+
index 5e84ce19de1efa21b5f64b96c3cb732de5970101..67affb5ae090efa4acc70ef46536355535fcb63e 100644 (file)
@@ -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]