diff --git a/mk/cfg/i686-pc-windows-gnu.mk b/mk/cfg/i686-pc-windows-gnu.mk index 174671a9a8812..ced8bf43163e7 100644 --- a/mk/cfg/i686-pc-windows-gnu.mk +++ b/mk/cfg/i686-pc-windows-gnu.mk @@ -22,3 +22,4 @@ CFG_LDPATH_i686-pc-windows-gnu := CFG_RUN_i686-pc-windows-gnu=$(2) CFG_RUN_TARG_i686-pc-windows-gnu=$(call CFG_RUN_i686-pc-windows-gnu,,$(2)) CFG_GNU_TRIPLE_i686-pc-windows-gnu := i686-w64-mingw32 +CFG_LIBC_STARTUP_OBJECTS_i686-pc-windows-gnu := crt2.o dllcrt2.o diff --git a/mk/cfg/x86_64-pc-windows-gnu.mk b/mk/cfg/x86_64-pc-windows-gnu.mk index 4118ea26c072b..0f49b6d585c6e 100644 --- a/mk/cfg/x86_64-pc-windows-gnu.mk +++ b/mk/cfg/x86_64-pc-windows-gnu.mk @@ -22,3 +22,4 @@ CFG_LDPATH_x86_64-pc-windows-gnu := CFG_RUN_x86_64-pc-windows-gnu=$(2) CFG_RUN_TARG_x86_64-pc-windows-gnu=$(call CFG_RUN_x86_64-pc-windows-gnu,,$(2)) CFG_GNU_TRIPLE_x86_64-pc-windows-gnu := x86_64-w64-mingw32 +CFG_LIBC_STARTUP_OBJECTS_x86_64-pc-windows-gnu := crt2.o dllcrt2.o \ No newline at end of file diff --git a/mk/target.mk b/mk/target.mk index d6fa55bf7f516..5caf8d8e81eda 100644 --- a/mk/target.mk +++ b/mk/target.mk @@ -132,6 +132,73 @@ $$(TBIN$(1)_T_$(2)_H_$(3))/$(4)$$(X_$(2)): \ endef +# Macro for building runtime startup objects +# Of those we have two kinds: +# - Rust runtime-specific: these are Rust's equivalents of GCC's crti.o/crtn.o, +# - LibC-specific: these we don't build ourselves, but copy them from the system lib directory. +# +# $(1) - stage +# $(2) - target triple +# $(3) - host triple +define TARGET_RT_STARTUP + +# Expand build rules for rsbegin.o and rsend.o +$$(foreach obj,rsbegin rsend, \ + $$(eval $$(call TARGET_RUSTRT_STARTUP_OBJ,$(1),$(2),$(3),$$(obj))) ) + +# Expand build rules for libc startup objects +$$(foreach obj,$$(CFG_LIBC_STARTUP_OBJECTS_$(2)), \ + $$(eval $$(call TARGET_LIBC_STARTUP_OBJ,$(1),$(2),$(3),$$(obj))) ) + +endef + +# Macro for building runtime startup/shutdown object files; +# these are Rust's equivalent of crti.o, crtn.o +# +# $(1) - stage +# $(2) - target triple +# $(3) - host triple +# $(4) - object basename +define TARGET_RUSTRT_STARTUP_OBJ + +$$(TLIB$(1)_T_$(2)_H_$(3))/$(4).o: \ + $(S)src/rtstartup/$(4).rs \ + $$(TLIB$(1)_T_$(2)_H_$(3))/stamp.core \ + $$(HSREQ$(1)_T_$(2)_H_$(3)) \ + | $$(TBIN$(1)_T_$(2)_H_$(3))/ + @$$(call E, rustc: $$@) + $$(STAGE$(1)_T_$(2)_H_$(3)) --emit=obj -o $$@ $$< + +# Add dependencies on Rust startup objects to all crates that depend on core. +# This ensures that they are built after core (since they depend on it), +# but before everything else (since they are needed for linking dylib crates). +$$(foreach crate, $$(TARGET_CRATES), \ + $$(if $$(findstring core,$$(DEPS_$$(crate))), \ + $$(TLIB$(1)_T_$(2)_H_$(3))/stamp.$$(crate))) : $$(TLIB$(1)_T_$(2)_H_$(3))/$(4).o + +endef + +# Macro for copying libc startup objects into the target's lib directory. +# +# $(1) - stage +# $(2) - target triple +# $(3) - host triple +# $(4) - object name +define TARGET_LIBC_STARTUP_OBJ + +# Ask gcc where the startup object is located +$$(TLIB$(1)_T_$(2)_H_$(3))/$(4) : $$(shell $$(CC_$(2)) -print-file-name=$(4)) + @$$(call E, cp: $$@) + @cp $$^ $$@ + +# Make sure this is done before libcore has finished building +# (libcore itself does not depend on these objects, but other crates do, +# so might as well do it here) +$$(TLIB$(1)_T_$(2)_H_$(3))/stamp.core : $$(TLIB$(1)_T_$(2)_H_$(3))/$(4) + +endef + + # Every recipe in RUST_TARGET_STAGE_N outputs to $$(TLIB$(1)_T_$(2)_H_$(3), # a directory that can be cleaned out during the middle of a run of # the get-snapshot.py script. Therefore, every recipe needs to have @@ -174,3 +241,8 @@ $(foreach host,$(CFG_HOST), \ $(foreach stage,$(STAGES), \ $(foreach tool,$(TOOLS), \ $(eval $(call TARGET_TOOL,$(stage),$(target),$(host),$(tool))))))) + +$(foreach host,$(CFG_HOST), \ + $(foreach target,$(CFG_TARGET), \ + $(foreach stage,$(STAGES), \ + $(eval $(call TARGET_RT_STARTUP,$(stage),$(target),$(host)))))) diff --git a/src/doc/trpl/custom-allocators.md b/src/doc/trpl/custom-allocators.md index 4fd05e87a7e69..d927eac0751d3 100644 --- a/src/doc/trpl/custom-allocators.md +++ b/src/doc/trpl/custom-allocators.md @@ -140,6 +140,8 @@ pub extern fn __rust_usable_size(size: usize, _align: usize) -> usize { # #[lang = "panic_fmt"] fn panic_fmt() {} # #[lang = "eh_personality"] fn eh_personality() {} # #[lang = "eh_unwind_resume"] extern fn eh_unwind_resume() {} +# #[no_mangle] pub extern fn rust_eh_register_frames () {} +# #[no_mangle] pub extern fn rust_eh_unregister_frames () {} ``` After we compile this crate, it can be used as follows: diff --git a/src/doc/trpl/lang-items.md b/src/doc/trpl/lang-items.md index cd1cec7a5d0ba..76b9f82827bbb 100644 --- a/src/doc/trpl/lang-items.md +++ b/src/doc/trpl/lang-items.md @@ -54,6 +54,8 @@ fn main(argc: isize, argv: *const *const u8) -> isize { #[lang = "eh_personality"] extern fn eh_personality() {} #[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} } # #[lang = "eh_unwind_resume"] extern fn rust_eh_unwind_resume() {} +# #[no_mangle] pub extern fn rust_eh_register_frames () {} +# #[no_mangle] pub extern fn rust_eh_unregister_frames () {} ``` 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 5fca05d534006..0040878fb2f4f 100644 --- a/src/doc/trpl/no-stdlib.md +++ b/src/doc/trpl/no-stdlib.md @@ -34,6 +34,8 @@ fn start(_argc: isize, _argv: *const *const u8) -> isize { #[lang = "eh_personality"] extern fn eh_personality() {} #[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} } # #[lang = "eh_unwind_resume"] extern fn rust_eh_unwind_resume() {} +# #[no_mangle] pub extern fn rust_eh_register_frames () {} +# #[no_mangle] pub extern fn rust_eh_unregister_frames () {} # // fn main() {} tricked you, rustdoc! ``` @@ -60,6 +62,8 @@ pub extern fn main(argc: i32, argv: *const *const u8) -> i32 { #[lang = "eh_personality"] extern fn eh_personality() {} #[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} } # #[lang = "eh_unwind_resume"] extern fn rust_eh_unwind_resume() {} +# #[no_mangle] pub extern fn rust_eh_register_frames () {} +# #[no_mangle] pub extern fn rust_eh_unregister_frames () {} # // fn main() {} tricked you, rustdoc! ``` @@ -145,8 +149,10 @@ extern fn panic_fmt(args: &core::fmt::Arguments, } #[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 } +# #[lang = "eh_unwind_resume"] extern fn rust_eh_unwind_resume() {} +# #[no_mangle] pub extern fn rust_eh_register_frames () {} +# #[no_mangle] pub extern fn rust_eh_unregister_frames () {} # fn main() {} ``` diff --git a/src/librustc_back/target/i686_pc_windows_gnu.rs b/src/librustc_back/target/i686_pc_windows_gnu.rs index c825f6043d27b..fa12bbd89323c 100644 --- a/src/librustc_back/target/i686_pc_windows_gnu.rs +++ b/src/librustc_back/target/i686_pc_windows_gnu.rs @@ -11,17 +11,12 @@ use target::Target; pub fn target() -> Target { - let mut options = super::windows_base::opts(); - options.cpu = "pentium4".to_string(); + let mut base = super::windows_base::opts(); + base.cpu = "pentium4".to_string(); // Mark all dynamic libraries and executables as compatible with the larger 4GiB address // space available to x86 Windows binaries on x86_64. - options.pre_link_args.push("-Wl,--large-address-aware".to_string()); - - // Make sure that we link to the dynamic libgcc, otherwise cross-module - // DWARF stack unwinding will not work. - // This behavior may be overridden by -Clink-args="-static-libgcc" - options.pre_link_args.push("-shared-libgcc".to_string()); + base.pre_link_args.push("-Wl,--large-address-aware".to_string()); Target { llvm_target: "i686-pc-windows-gnu".to_string(), @@ -31,6 +26,6 @@ pub fn target() -> Target { target_os: "windows".to_string(), target_env: "gnu".to_string(), target_vendor: "pc".to_string(), - options: options, + options: base, } } diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs index 197f8c760b040..7af31e33aba0f 100644 --- a/src/librustc_back/target/mod.rs +++ b/src/librustc_back/target/mod.rs @@ -98,16 +98,25 @@ pub struct TargetOptions { pub linker: String, /// Archive utility to use when managing archives. Defaults to "ar". pub ar: String, + /// Linker arguments that are unconditionally passed *before* any /// user-defined libraries. pub pre_link_args: Vec, + /// Objects to link before all others, always found within the + /// sysroot folder. + pub pre_link_objects_exe: Vec, // ... when linking an executable + pub pre_link_objects_dll: Vec, // ... when linking a dylib + /// Linker arguments that are unconditionally passed after any + /// user-defined but before post_link_objects. Standard platform + /// libraries that should be always be linked to, usually go here. + pub late_link_args: Vec, + /// Objects to link after all others, always found within the + /// sysroot folder. + pub post_link_objects: Vec, /// Linker arguments that are unconditionally passed *after* any /// user-defined libraries. pub post_link_args: Vec, - /// Objects to link before and after all others, always found within the - /// sysroot folder. - pub pre_link_objects: Vec, - pub post_link_objects: Vec, + /// Default CPU to pass to LLVM. Corresponds to `llc -mcpu=$cpu`. Defaults /// to "default". pub cpu: String, @@ -219,8 +228,10 @@ impl Default for TargetOptions { no_compiler_rt: false, no_default_libraries: true, position_independent_executables: false, - pre_link_objects: Vec::new(), + pre_link_objects_exe: Vec::new(), + pre_link_objects_dll: Vec::new(), post_link_objects: Vec::new(), + late_link_args: Vec::new(), archive_format: String::new(), custom_unwind_resume: false, lib_allocation_crate: "alloc_system".to_string(), diff --git a/src/librustc_back/target/windows_base.rs b/src/librustc_back/target/windows_base.rs index 2e5973679022d..634a63cf0bb34 100644 --- a/src/librustc_back/target/windows_base.rs +++ b/src/librustc_back/target/windows_base.rs @@ -23,10 +23,7 @@ pub fn opts() -> TargetOptions { exe_suffix: ".exe".to_string(), staticlib_prefix: "".to_string(), staticlib_suffix: ".lib".to_string(), - // Unfortunately right now passing -nodefaultlibs to gcc on windows - // doesn't work so hot (in terms of native dependencies). This flag - // should hopefully be removed one day though! - no_default_libraries: false, + no_default_libraries: true, is_like_windows: true, archive_format: "gnu".to_string(), pre_link_args: vec!( @@ -63,7 +60,30 @@ pub fn opts() -> TargetOptions { // Always enable DEP (NX bit) when it is available "-Wl,--nxcompat".to_string(), + + // Do not use the standard system startup files or libraries when linking + "-nostdlib".to_string(), + ), + pre_link_objects_exe: vec!( + "crt2.o".to_string(), // mingw C runtime initialization for executables + "rsbegin.o".to_string(), // Rust compiler runtime initialization, see rsbegin.rs + ), + pre_link_objects_dll: vec!( + "dllcrt2.o".to_string(), // mingw C runtime initialization for dlls + "rsbegin.o".to_string(), + ), + late_link_args: vec!( + "-lmingwex".to_string(), + "-lmingw32".to_string(), + "-lgcc".to_string(), // alas, mingw* libraries above depend on libgcc + "-lmsvcrt".to_string(), + "-luser32".to_string(), + "-lkernel32".to_string(), + ), + post_link_objects: vec!( + "rsend.o".to_string() ), + custom_unwind_resume: true, exe_allocation_crate: super::maybe_jemalloc(), .. Default::default() 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 2bd363e46bb1c..3e8438539156f 100644 --- a/src/librustc_back/target/x86_64_pc_windows_gnu.rs +++ b/src/librustc_back/target/x86_64_pc_windows_gnu.rs @@ -13,10 +13,7 @@ use target::Target; pub fn target() -> Target { let mut base = super::windows_base::opts(); base.cpu = "x86-64".to_string(); - // 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_back/target/x86_64_unknown_linux_musl.rs b/src/librustc_back/target/x86_64_unknown_linux_musl.rs index a5ac78cd5b608..dafbb924a9ca5 100644 --- a/src/librustc_back/target/x86_64_unknown_linux_musl.rs +++ b/src/librustc_back/target/x86_64_unknown_linux_musl.rs @@ -58,8 +58,8 @@ pub fn target() -> Target { // // Each target directory for musl has these object files included in it so // they'll be included from there. - base.pre_link_objects.push("crt1.o".to_string()); - base.pre_link_objects.push("crti.o".to_string()); + base.pre_link_objects_exe.push("crt1.o".to_string()); + base.pre_link_objects_exe.push("crti.o".to_string()); base.post_link_objects.push("crtn.o".to_string()); // MUSL support doesn't currently include dynamic linking, so there's no diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index bae14afd81d66..21b438ae81401 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -850,7 +850,13 @@ fn link_natively(sess: &Session, dylib: bool, let root = sess.target_filesearch(PathKind::Native).get_lib_path(); cmd.args(&sess.target.target.options.pre_link_args); - for obj in &sess.target.target.options.pre_link_objects { + + let pre_link_objects = if dylib { + &sess.target.target.options.pre_link_objects_dll + } else { + &sess.target.target.options.pre_link_objects_exe + }; + for obj in pre_link_objects { cmd.arg(root.join(obj)); } @@ -866,6 +872,7 @@ fn link_natively(sess: &Session, dylib: bool, linker.link_staticlib("compiler-rt"); } } + cmd.args(&sess.target.target.options.late_link_args); for obj in &sess.target.target.options.post_link_objects { cmd.arg(root.join(obj)); } diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 9fa1aaf76f819..b37a0f6318c20 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -944,6 +944,19 @@ pub fn call_lifetime_end(cx: Block, ptr: ValueRef) { Call(cx, lifetime_end, &[C_u64(ccx, size), ptr], None, DebugLoc::None); } +// Generates code for resumption of unwind at the end of a landing pad. +pub fn trans_unwind_resume(bcx: Block, lpval: ValueRef) { + if !bcx.sess().target.target.options.custom_unwind_resume { + Resume(bcx, lpval); + } else { + let exc_ptr = ExtractValue(bcx, lpval, 0); + let llunwresume = bcx.fcx.eh_unwind_resume(); + Call(bcx, llunwresume, &[exc_ptr], None, DebugLoc::None); + Unreachable(bcx); + } +} + + pub fn call_memcpy(cx: Block, dst: ValueRef, src: ValueRef, n_bytes: ValueRef, align: u32) { let _icx = push_ctxt("call_memcpy"); let ccx = cx.ccx(); diff --git a/src/librustc_trans/trans/cleanup.rs b/src/librustc_trans/trans/cleanup.rs index d226bc3f155df..ffdc2701f8158 100644 --- a/src/librustc_trans/trans/cleanup.rs +++ b/src/librustc_trans/trans/cleanup.rs @@ -732,7 +732,7 @@ impl<'blk, 'tcx> CleanupHelperMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx "create_landing_pad() should have set this"); let lp = build::Load(prev_bcx, personality); base::call_lifetime_end(prev_bcx, personality); - build::Resume(prev_bcx, lp); + base::trans_unwind_resume(prev_bcx, lp); prev_llbb = prev_bcx.llbb; break; } @@ -845,8 +845,6 @@ 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 d160465c619b9..b39b7818a6350 100644 --- a/src/librustc_trans/trans/common.rs +++ b/src/librustc_trans/trans/common.rs @@ -561,53 +561,33 @@ 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, + // Returns a ValueRef of the "eh_unwind_resume" lang item if one is defined, + // otherwise declares it as an external funtion. + pub fn eh_unwind_resume(&self) -> ValueRef { + use trans::attributes; + assert!(self.ccx.sess().target.target.options.custom_unwind_resume); + match self.ccx.tcx().lang_items.eh_unwind_resume() { + Some(def_id) => { + callee::trans_fn_ref(self.ccx, def_id, 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()) + let mut unwresume = self.ccx.eh_unwind_resume().borrow_mut(); + match *unwresume { + Some(llfn) => llfn, + None => { + let fty = Type::func(&[Type::i8p(self.ccx)], &Type::void(self.ccx)); + let llfn = declare::declare_fn(self.ccx, + "rust_eh_unwind_resume", + llvm::CCallConv, + fty, ty::FnDiverging); + attributes::unwind(llfn, true); + *unwresume = Some(llfn); + llfn + } + } } - }; - - 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); } } diff --git a/src/librustc_trans/trans/context.rs b/src/librustc_trans/trans/context.rs index 94ce92ab972f4..0f7ea334c889d 100644 --- a/src/librustc_trans/trans/context.rs +++ b/src/librustc_trans/trans/context.rs @@ -146,8 +146,8 @@ pub struct LocalCrateContext<'tcx> { dbg_cx: Option>, eh_personality: RefCell>, + eh_unwind_resume: RefCell>, rust_try_fn: RefCell>, - unwind_resume_hooked: Cell, intrinsics: RefCell>, @@ -466,8 +466,8 @@ impl<'tcx> LocalCrateContext<'tcx> { closure_vals: RefCell::new(FnvHashMap()), dbg_cx: dbg_cx, eh_personality: RefCell::new(None), + eh_unwind_resume: 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()), @@ -728,12 +728,12 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { &self.local.eh_personality } - pub fn rust_try_fn<'a>(&'a self) -> &'a RefCell> { - &self.local.rust_try_fn + pub fn eh_unwind_resume<'a>(&'a self) -> &'a RefCell> { + &self.local.eh_unwind_resume } - pub fn unwind_resume_hooked<'a>(&'a self) -> &'a Cell { - &self.local.unwind_resume_hooked + pub fn rust_try_fn<'a>(&'a self) -> &'a RefCell> { + &self.local.rust_try_fn } fn intrinsics<'a>(&'a self) -> &'a RefCell> { diff --git a/src/librustc_trans/trans/intrinsic.rs b/src/librustc_trans/trans/intrinsic.rs index 5e781bdf3306a..b8fa826b93948 100644 --- a/src/librustc_trans/trans/intrinsic.rs +++ b/src/librustc_trans/trans/intrinsic.rs @@ -1315,7 +1315,7 @@ fn trans_msvc_try<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // The "catch-resume" block is where we're running this landing pad but // we actually need to not catch the exception, so just resume the // exception to return. - Resume(catch_resume, vals); + trans_unwind_resume(catch_resume, vals); // On the successful branch we just return null. Ret(then, C_null(Type::i8p(ccx)), dloc); diff --git a/src/libstd/rt.rs b/src/libstd/rt.rs index 8be0c6f3b3d99..44961611b7bd7 100644 --- a/src/libstd/rt.rs +++ b/src/libstd/rt.rs @@ -32,6 +32,11 @@ use thread::{self, Thread}; // Reexport some of our utilities which are expected by other crates. pub use sys_common::unwind::{begin_unwind, begin_unwind_fmt}; +// Rust runtime's startup objects depend on these symbols, so they must be public. +// Since sys_common isn't public, we have to re-export them here. +#[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))] +pub use sys_common::unwind::imp::eh_frame_registry::*; + #[cfg(not(test))] #[lang = "start"] fn lang_start(main: *const u8, argc: isize, argv: *const *const u8) -> isize { diff --git a/src/libstd/sys/common/libunwind.rs b/src/libstd/sys/common/libunwind.rs index da7ebbf4ed39f..75bb11216e1a2 100644 --- a/src/libstd/sys/common/libunwind.rs +++ b/src/libstd/sys/common/libunwind.rs @@ -99,35 +99,23 @@ pub type _Unwind_Exception_Cleanup_Fn = extern "C" fn(unwind_code: _Unwind_Reason_Code, exception: *mut _Unwind_Exception); -#[cfg(any(all(target_os = "linux", not(target_env = "musl")), - target_os = "freebsd"))] -#[link(name = "gcc_s")] -extern {} - -#[cfg(all(target_os = "linux", target_env = "musl", not(test)))] -#[link(name = "unwind", kind = "static")] -extern {} - -#[cfg(any(target_os = "android", target_os = "openbsd"))] -#[link(name = "gcc")] -extern {} - -#[cfg(all(target_os = "netbsd", not(target_vendor = "rumprun")))] -#[link(name = "gcc")] -extern {} - -#[cfg(all(target_os = "netbsd", target_vendor = "rumprun"))] -#[link(name = "unwind")] -extern {} - -#[cfg(target_os = "dragonfly")] -#[link(name = "gcc_pic")] -extern {} - -#[cfg(target_os = "bitrig")] -#[link(name = "c++abi")] -extern {} - +#[cfg_attr(any(all(target_os = "linux", not(target_env = "musl")), + target_os = "freebsd"), + link(name = "gcc_s"))] +#[cfg_attr(all(target_os = "linux", target_env = "musl", not(test)), + link(name = "unwind", kind = "static"))] +#[cfg_attr(any(target_os = "android", target_os = "openbsd"), + link(name = "gcc"))] +#[cfg_attr(all(target_os = "netbsd", not(target_vendor = "rumprun")), + link(name = "gcc"))] +#[cfg_attr(all(target_os = "netbsd", target_vendor = "rumprun"), + link(name = "unwind"))] +#[cfg_attr(target_os = "dragonfly", + link(name = "gcc_pic"))] +#[cfg_attr(target_os = "bitrig", + link(name = "c++abi"))] +#[cfg_attr(all(target_os = "windows", target_env="gnu"), + link(name = "gcc_eh"))] extern "C" { // iOS on armv7 uses SjLj exceptions and requires to link // against corresponding routine (..._SjLj_...) @@ -142,6 +130,11 @@ extern "C" { -> _Unwind_Reason_Code; pub fn _Unwind_DeleteException(exception: *mut _Unwind_Exception); + + // remove cfg after new snapshot + #[cfg(not(all(stage0, target_os="windows", target_arch="x86_64")))] + #[unwind] + pub fn _Unwind_Resume(exception: *mut _Unwind_Exception) -> !; } // ... and now we just providing access to SjLj counterspart diff --git a/src/libstd/sys/common/unwind/gcc.rs b/src/libstd/sys/common/unwind/gcc.rs index 361cef08c11a5..0a598b559514c 100644 --- a/src/libstd/sys/common/unwind/gcc.rs +++ b/src/libstd/sys/common/unwind/gcc.rs @@ -231,3 +231,36 @@ pub mod eabi { } } } + +// See docs in the `unwind` module. +#[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu", not(test)))] +#[lang = "eh_unwind_resume"] +#[unwind] +unsafe extern fn rust_eh_unwind_resume(panic_ctx: *mut u8) -> ! { + uw::_Unwind_Resume(panic_ctx as *mut uw::_Unwind_Exception); +} + +#[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))] +pub mod eh_frame_registry { + // The implementation of stack unwinding is (for now) deferred to libgcc_eh, however Rust + // crates use these Rust-specific entry points to avoid potential clashes with GCC runtime. + // See also: rtbegin.rs, `unwind` module. + + #[link(name = "gcc_eh")] + extern { + fn __register_frame_info(eh_frame_begin: *const u8, object: *mut u8); + fn __deregister_frame_info(eh_frame_begin: *const u8, object: *mut u8); + } + #[cfg(not(test))] + #[no_mangle] + pub unsafe extern fn rust_eh_register_frames(eh_frame_begin: *const u8, + object: *mut u8) { + __register_frame_info(eh_frame_begin, object); + } + #[cfg(not(test))] + #[no_mangle] + pub unsafe extern fn rust_eh_unregister_frames(eh_frame_begin: *const u8, + object: *mut u8) { + __deregister_frame_info(eh_frame_begin, object); + } +} diff --git a/src/libstd/sys/common/unwind/mod.rs b/src/libstd/sys/common/unwind/mod.rs index 3978aeb39bc46..e42a4694070cb 100644 --- a/src/libstd/sys/common/unwind/mod.rs +++ b/src/libstd/sys/common/unwind/mod.rs @@ -34,28 +34,35 @@ //! object being thrown, and to decide whether it should be caught at that stack //! frame. Once the handler frame has been identified, cleanup phase begins. //! -//! In the cleanup phase, personality routines invoke cleanup code associated -//! with their stack frames (i.e. destructors). Once stack has been unwound down -//! to the handler frame level, unwinding stops and the last personality routine -//! transfers control to its catch block. +//! In the cleanup phase, the unwinder invokes each personality routine again. +//! This time it decides which (if any) cleanup code needs to be run for +//! the current stack frame. If so, the control is transferred to a special branch +//! in the function body, the "landing pad", which invokes destructors, frees memory, +//! etc. At the end of the landing pad, control is transferred back to the unwinder +//! and unwinding resumes. //! -//! ## Frame unwind info registration +//! Once stack has been unwound down to the handler frame level, unwinding stops +//! and the last personality routine transfers control to the catch block. //! -//! Each module has its own frame unwind info section (usually ".eh_frame"), and -//! unwinder needs to know about all of them in order for unwinding to be able to -//! cross module boundaries. +//! ## `eh_personality` and `eh_unwind_resume` //! -//! On some platforms, like Linux, this is achieved by dynamically enumerating -//! currently loaded modules via the dl_iterate_phdr() API and finding all -//! .eh_frame sections. +//! These language items are used by the compiler when generating unwind info. +//! The first one is the personality routine described above. The second one +//! allows compilation target to customize the process of resuming unwind at the +//! end of the landing pads. `eh_unwind_resume` is used only if `custom_unwind_resume` +//! flag in the target options is set. //! -//! Others, like Windows, require modules to actively register their unwind info -//! sections by calling __register_frame_info() API at startup. In the latter -//! case it is essential that there is only one copy of the unwinder runtime in -//! the process. This is usually achieved by linking to the dynamic version of -//! the unwind runtime. +//! ## Frame unwind info registration //! -//! Currently Rust uses unwind runtime provided by libgcc. +//! Each module's image contains a frame unwind info section (usually ".eh_frame"). +//! When a module is loaded/unloaded into the process, the unwinder must be informed +//! about the location of this section in memory. The methods of achieving that vary +//! by the platform. +//! On some (e.g. Linux), the unwinder can discover unwind info sections on its own +//! (by dynamically enumerating currently loaded modules via the dl_iterate_phdr() API +//! and finding their ".eh_frame" sections); +//! Others, like Windows, require modules to actively register their unwind info +//! sections via unwinder API (see `rust_eh_register_frames`/`rust_eh_unregister_frames`). #![allow(dead_code)] #![allow(unused_imports)] diff --git a/src/libstd/sys/common/unwind/seh64_gnu.rs b/src/libstd/sys/common/unwind/seh64_gnu.rs index 9478678fda995..92f059d68e1a3 100644 --- a/src/libstd/sys/common/unwind/seh64_gnu.rs +++ b/src/libstd/sys/common/unwind/seh64_gnu.rs @@ -190,17 +190,10 @@ unsafe extern fn rust_eh_personality( 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))] +#[lang = "eh_unwind_resume"] #[unwind] -unsafe extern fn rust_eh_unwind_resume(panic_ctx: LPVOID) { +unsafe extern fn rust_eh_unwind_resume(panic_ctx: LPVOID) -> ! { let params = [panic_ctx as ULONG_PTR]; RaiseException(RUST_PANIC, EXCEPTION_NONCONTINUABLE, diff --git a/src/libstd/sys/windows/c.rs b/src/libstd/sys/windows/c.rs index bac5b47eb1a62..69f15219f69a9 100644 --- a/src/libstd/sys/windows/c.rs +++ b/src/libstd/sys/windows/c.rs @@ -354,6 +354,7 @@ pub type PVECTORED_EXCEPTION_HANDLER = extern "system" #[link(name = "ws2_32")] #[link(name = "userenv")] +#[link(name = "shell32")] extern "system" { pub fn WSAStartup(wVersionRequested: libc::WORD, lpWSAData: LPWSADATA) -> libc::c_int; diff --git a/src/rtstartup/rsbegin.rs b/src/rtstartup/rsbegin.rs new file mode 100644 index 0000000000000..af9a01ea2e4f7 --- /dev/null +++ b/src/rtstartup/rsbegin.rs @@ -0,0 +1,80 @@ +// 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. + +// rsbegin.o and rsend.o are the so called "compiler runtime startup objects". +// They contain code needed to correctly initialize the compiler runtime. +// +// When an executable or dylib image is linked, all user code and libraries are +// "sandwiched" between these two object files, so code or data from rsbegin.o +// become first in the respective sections of the image, whereas code and data +// from rsend.o become the last ones. This effect can be used to place symbols +// at the beginning or at the end of a section, as well as to insert any required +// headers or footers. +// +// Note that the actual module entry point is located in the C runtime startup +// object (usually called `crtX.o), which then invokes initialization callbacks +// of other runtime components (registered via yet another special image section). + +#![feature(no_std)] + +#![crate_type="rlib"] +#![no_std] +#![allow(non_camel_case_types)] + +#[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))] +pub mod eh_frames +{ + #[no_mangle] + #[link_section = ".eh_frame"] + // Marks beginning of the stack frame unwind info section + pub static __EH_FRAME_BEGIN__: [u8; 0] = []; + + // Scratch space for unwinder's internal book-keeping. + // This is defined as `struct object` in $GCC/libgcc/unwind-dw2-fde.h. + static mut obj: [isize; 6] = [0; 6]; + + // Unwind info registration/deregistration routines. + // See the docs of `unwind` module in libstd. + extern { + fn rust_eh_register_frames(eh_frame_begin: *const u8, object: *mut u8); + fn rust_eh_unregister_frames(eh_frame_begin: *const u8, object: *mut u8); + } + + unsafe fn init() { + // register unwind info on module startup + rust_eh_register_frames(&__EH_FRAME_BEGIN__ as *const u8, + &mut obj as *mut _ as *mut u8); + } + + unsafe fn uninit() { + // unregister on shutdown + rust_eh_unregister_frames(&__EH_FRAME_BEGIN__ as *const u8, + &mut obj as *mut _ as *mut u8); + } + + // MSVC-specific init/uninit routine registration + pub mod ms_init + { + // .CRT$X?? sections are roughly analogous to ELF's .init_array and .fini_array, + // except that they exploit the fact that linker will sort them alphabitically, + // so e.g. sections with names between .CRT$XIA and .CRT$XIZ are guaranteed to be + // placed between those two, without requiring any ordering of objects on the linker + // command line. + // Note that ordering of same-named sections from different objects is not guaranteed. + // Since .CRT$XIA contains init array's header symbol, which must always come first, + // we place our initialization callback into .CRT$XIB. + + #[link_section = ".CRT$XIB"] // .CRT$XI? : C initialization callbacks + pub static P_INIT: unsafe fn() = super::init; + + #[link_section = ".CRT$XTY"] // .CRT$XT? : C termination callbacks + pub static P_UNINIT: unsafe fn() = super::uninit; + } +} diff --git a/src/rtstartup/rsend.rs b/src/rtstartup/rsend.rs new file mode 100644 index 0000000000000..ecb6a228e1776 --- /dev/null +++ b/src/rtstartup/rsend.rs @@ -0,0 +1,26 @@ +// 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. + +// See rsbegin.rs for details. + +#![feature(no_std)] + +#![crate_type="rlib"] +#![no_std] + +#[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))] +pub mod eh_frames +{ + // Terminate the frame unwind info section with a 0 as a sentinel; + // this would be the 'length' field in a real FDE. + #[no_mangle] + #[link_section = ".eh_frame"] + pub static __EH_FRAME_END__: u32 = 0; +} diff --git a/src/test/run-make/no-duplicate-libs/bar.rs b/src/test/run-make/no-duplicate-libs/bar.rs index c7f184cb57672..2df7604f96138 100644 --- a/src/test/run-make/no-duplicate-libs/bar.rs +++ b/src/test/run-make/no-duplicate-libs/bar.rs @@ -20,3 +20,5 @@ pub extern fn bar() {} #[lang = "eh_personality"] fn eh_personality() {} #[lang = "eh_unwind_resume"] fn eh_unwind_resume() {} #[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} } +#[no_mangle] pub extern fn rust_eh_register_frames () {} +#[no_mangle] pub extern fn rust_eh_unregister_frames () {} diff --git a/src/test/run-make/no-duplicate-libs/foo.rs b/src/test/run-make/no-duplicate-libs/foo.rs index d8bd2bec2de98..4e81f0ea06e6a 100644 --- a/src/test/run-make/no-duplicate-libs/foo.rs +++ b/src/test/run-make/no-duplicate-libs/foo.rs @@ -20,4 +20,5 @@ pub extern fn foo() {} #[lang = "eh_personality"] fn eh_personality() {} #[lang = "eh_unwind_resume"] fn eh_unwind_resume() {} #[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} } - +#[no_mangle] pub extern fn rust_eh_register_frames () {} +#[no_mangle] pub extern fn rust_eh_unregister_frames () {} diff --git a/src/test/run-pass/smallest-hello-world.rs b/src/test/run-pass/smallest-hello-world.rs index 5f35bd4f7d2e2..b11970560d59a 100644 --- a/src/test/run-pass/smallest-hello-world.rs +++ b/src/test/run-pass/smallest-hello-world.rs @@ -23,6 +23,8 @@ extern "rust-intrinsic" { fn transmute(t: T) -> U; } #[lang = "eh_personality"] extern fn eh_personality() {} #[lang = "eh_unwind_resume"] extern fn eh_unwind_resume() {} #[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} } +#[no_mangle] pub extern fn rust_eh_register_frames () {} +#[no_mangle] pub extern fn rust_eh_unregister_frames () {} #[start] fn main(_: isize, _: *const *const u8) -> isize {