From 1da466253c1fe6491f330a4ae5aa56da950928bf Mon Sep 17 00:00:00 2001 From: Vadim Chugunov Date: Sun, 18 Oct 2015 14:17:34 -0700 Subject: [PATCH 01/10] Revisit implementation of custom unwind resume; Rather than injecting a local `_Unwind_Resume` into the current translation unit, just replace `resume` instruction with a direct call the the `eh_unwind_resume` lang item. This is likely to be more robust in the face of future LLVM changes, and also allows us to delegate work back to libgcc's `_Unwind_Resume`. --- src/librustc_trans/trans/base.rs | 13 +++++ src/librustc_trans/trans/cleanup.rs | 4 +- src/librustc_trans/trans/common.rs | 68 ++++++++++----------------- src/librustc_trans/trans/context.rs | 12 ++--- src/librustc_trans/trans/intrinsic.rs | 2 +- 5 files changed, 45 insertions(+), 54 deletions(-) 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); From def917afeb4ae4668010c2ff2b2757ed7ac4931a Mon Sep 17 00:00:00 2001 From: Vadim Chugunov Date: Sun, 18 Oct 2015 14:28:47 -0700 Subject: [PATCH 02/10] Implement `eh_unwind_resume` in libstd. --- src/libstd/sys/common/libunwind.rs | 3 +++ src/libstd/sys/common/unwind/gcc.rs | 7 +++++++ src/libstd/sys/common/unwind/seh64_gnu.rs | 11 ++--------- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/libstd/sys/common/libunwind.rs b/src/libstd/sys/common/libunwind.rs index da7ebbf4ed39f..04f677a90c43f 100644 --- a/src/libstd/sys/common/libunwind.rs +++ b/src/libstd/sys/common/libunwind.rs @@ -142,6 +142,9 @@ extern "C" { -> _Unwind_Reason_Code; pub fn _Unwind_DeleteException(exception: *mut _Unwind_Exception); + + #[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..5ee14d9f57af8 100644 --- a/src/libstd/sys/common/unwind/gcc.rs +++ b/src/libstd/sys/common/unwind/gcc.rs @@ -231,3 +231,10 @@ pub mod eabi { } } } + +#[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); +} 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, From c807ff3b449a8dafc5cef539e5e65b2fe6dab879 Mon Sep 17 00:00:00 2001 From: Vadim Chugunov Date: Sun, 18 Oct 2015 14:31:32 -0700 Subject: [PATCH 03/10] Create entry points for unwind frame registry in libstd. --- src/libstd/lib.rs | 9 +++++++++ src/libstd/sys/common/libunwind.rs | 4 ++++ src/libstd/sys/common/unwind/gcc.rs | 21 +++++++++++++++++++++ 3 files changed, 34 insertions(+) diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index a624b3521267a..f318cde71bd66 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -400,6 +400,15 @@ pub mod __rand { pub use rand::{thread_rng, ThreadRng, Rng}; } +// 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 explicitly. +#[doc(hidden)] +#[unstable(feature = "eh_frame_registry", issue = "0")] +#[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))] +pub mod __frame_registry { + pub use sys_common::unwind::imp::eh_frame_registry::*; +} + // Include a number of private modules that exist solely to provide // the rustdoc documentation for primitive types. Using `include!` // because rustdoc only looks for these modules at the crate level. diff --git a/src/libstd/sys/common/libunwind.rs b/src/libstd/sys/common/libunwind.rs index 04f677a90c43f..74d334bd06280 100644 --- a/src/libstd/sys/common/libunwind.rs +++ b/src/libstd/sys/common/libunwind.rs @@ -128,6 +128,10 @@ extern {} #[link(name = "c++abi")] extern {} +#[cfg(all(target_os = "windows", target_env="gnu"))] +#[link(name = "gcc_eh")] +extern {} + extern "C" { // iOS on armv7 uses SjLj exceptions and requires to link // against corresponding routine (..._SjLj_...) diff --git a/src/libstd/sys/common/unwind/gcc.rs b/src/libstd/sys/common/unwind/gcc.rs index 5ee14d9f57af8..6d82eb7dfb198 100644 --- a/src/libstd/sys/common/unwind/gcc.rs +++ b/src/libstd/sys/common/unwind/gcc.rs @@ -238,3 +238,24 @@ pub mod eabi { 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 { + #[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); + } +} From bd0cf1ba13566417c71ce8908af39f94cbbb8468 Mon Sep 17 00:00:00 2001 From: Vadim Chugunov Date: Sun, 18 Oct 2015 14:32:50 -0700 Subject: [PATCH 04/10] Don't use GCC's startup objects (crtbegin.o/crtend.o); build and use our own (for now on for -windows-gnu target only). Since it isn't possible to disable linkage of just GCC startup objects, we now need logic for finding libc installation directory and copying the required startup files (e.g. crt2.o) to rustlib directory. Bonus change: use the `-nodefaultlibs` flag on Windows, thus paving the way to direct linker invocation. --- configure | 15 +++++ mk/cfg/i686-pc-windows-gnu.mk | 1 + mk/cfg/x86_64-pc-windows-gnu.mk | 1 + mk/target.mk | 59 ++++++++++++++++++ .../target/i686_pc_windows_gnu.rs | 13 ++-- src/librustc_back/target/mod.rs | 21 +++++-- src/librustc_back/target/windows_base.rs | 30 +++++++-- .../target/x86_64_pc_windows_gnu.rs | 3 - .../target/x86_64_unknown_linux_musl.rs | 4 +- src/librustc_trans/back/link.rs | 9 ++- src/rtstartup/rsbegin.rs | 61 +++++++++++++++++++ src/rtstartup/rsend.rs | 24 ++++++++ 12 files changed, 217 insertions(+), 24 deletions(-) create mode 100644 src/rtstartup/rsbegin.rs create mode 100644 src/rtstartup/rsend.rs diff --git a/configure b/configure index 60d366100f8c2..1360a1ff0ee88 100755 --- a/configure +++ b/configure @@ -625,6 +625,7 @@ valopt_nosave local-rust-root "/usr/local" "set prefix for local rust binary" valopt_nosave host "${CFG_BUILD}" "GNUs ./configure syntax LLVM host triples" valopt_nosave target "${CFG_HOST}" "GNUs ./configure syntax LLVM target triples" valopt_nosave mandir "${CFG_PREFIX}/share/man" "install man pages in PATH" +valopt_nosave libc-dir "/usr/lib" "installation directory of the system libc" # Temporarily support old triples until buildbots get updated CFG_BUILD=$(to_llvm_triple $CFG_BUILD) @@ -1080,6 +1081,9 @@ program_transform_name=$($CFG_CC -v 2>&1 | sed -n "s/.*--program-transform-name= CFG_STDCPP_NAME=$(echo "stdc++" | sed "${program_transform_name}") putvar CFG_STDCPP_NAME +#CFG_LIB_SEARCH_PATH=$($CFG_CC -print-search-dirs | sed -n "/libraries: =/ { s/.*=//; P }") +#putvar CFG_LIB_SEARCH_PATH + # a little post-processing of various config values CFG_PREFIX=${CFG_PREFIX%/} CFG_MANDIR=${CFG_MANDIR%/} @@ -1280,6 +1284,16 @@ $ pacman -R cmake && pacman -S mingw-w64-x86_64-cmake putvar CFG_DISABLE_JEMALLOC ;; + *-windows-gnu) + if [ -z "$CFG_LIBC_DIR_PROVIDED" ]; then + # Use gcc location to find mingw libc directory + for dir in $(dirname $CFG_GCC)/../*-mingw32/lib; do + if [ -d "$dir" ]; then + CFG_LIBC_DIR=$dir + fi + done + fi + ;; *) ;; esac @@ -1738,6 +1752,7 @@ putvar CFG_AARCH64_LINUX_ANDROID_NDK putvar CFG_ARM_LINUX_ANDROIDEABI_NDK putvar CFG_I686_LINUX_ANDROID_NDK putvar CFG_MANDIR +putvar CFG_LIBC_DIR # Avoid spurious warnings from clang by feeding it original source on # ccache-miss rather than preprocessed input. 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..fcb3d4a1e53a2 100644 --- a/mk/target.mk +++ b/mk/target.mk @@ -132,6 +132,60 @@ $$(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))) ) + +$$(foreach obj,$$(CFG_LIBC_STARTUP_OBJECTS_$(2)), \ + $$(eval $$(TLIB$(1)_T_$(2)_H_$(3))/stamp.core : $$(TLIB$(1)_T_$(2)_H_$(3))/$$(obj)) \ + $$(eval $$(call COPY_LIBC_STARTUP,$$(TLIB$(1)_T_$(2)_H_$(3)),$$(obj))) ) +endef + +# TARGET_RT_STARTUP's helper for copying LibC startup objects +# $(1) - target lib directory +# $(2) - object name +define COPY_LIBC_STARTUP + +$(1)/$(2) : $$(CFG_LIBC_DIR)/$(2) + @$$(call E, cp: $$@) + @cp $$^ $$@ +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 name +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))), \ + $$(eval $$(TLIB$(1)_T_$(2)_H_$(3))/stamp.$$(crate) : $$(TLIB$(1)_T_$(2)_H_$(3))/$(4).o) )) +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 +228,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/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..fc1e192f1e15d 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,32 @@ 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(), + "rsbegin.o".to_string(), + ), + pre_link_objects_dll: vec!( + "dllcrt2.o".to_string(), + "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(), + "-ladvapi32".to_string(), + "-lshell32".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/rtstartup/rsbegin.rs b/src/rtstartup/rsbegin.rs new file mode 100644 index 0000000000000..17684b74b7019 --- /dev/null +++ b/src/rtstartup/rsbegin.rs @@ -0,0 +1,61 @@ +// 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. + +#![feature(no_std)] +#![feature(linkage)] + +#![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"] + 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]; + + 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() { + rust_eh_register_frames(&__EH_FRAME_BEGIN__ as *const u8, + &mut obj as *mut _ as *mut u8); + } + + unsafe fn uninit() { + rust_eh_unregister_frames(&__EH_FRAME_BEGIN__ as *const u8, + &mut obj as *mut _ as *mut u8); + } + + 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..df7759877e9f8 --- /dev/null +++ b/src/rtstartup/rsend.rs @@ -0,0 +1,24 @@ +// 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. + +#![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; +} From adce670e75fc6757182f3b969c804b37cb391e12 Mon Sep 17 00:00:00 2001 From: Vadim Chugunov Date: Sun, 18 Oct 2015 15:10:48 -0700 Subject: [PATCH 05/10] Fix up tests and docs. --- src/doc/trpl/custom-allocators.md | 2 ++ src/doc/trpl/lang-items.md | 2 ++ src/doc/trpl/no-stdlib.md | 8 +++++++- src/test/run-make/no-duplicate-libs/bar.rs | 2 ++ src/test/run-make/no-duplicate-libs/foo.rs | 3 ++- src/test/run-pass/smallest-hello-world.rs | 2 ++ 6 files changed, 17 insertions(+), 2 deletions(-) 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/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 { From 145b8438fe19b81d964b60132eeeb6c95e8646e9 Mon Sep 17 00:00:00 2001 From: Vadim Chugunov Date: Sun, 18 Oct 2015 16:13:48 -0700 Subject: [PATCH 06/10] Use `cfg_attr` for switching `link` attrs in libunwind. --- src/libstd/sys/common/libunwind.rs | 50 ++++++++++-------------------- 1 file changed, 17 insertions(+), 33 deletions(-) diff --git a/src/libstd/sys/common/libunwind.rs b/src/libstd/sys/common/libunwind.rs index 74d334bd06280..feb05c7b56008 100644 --- a/src/libstd/sys/common/libunwind.rs +++ b/src/libstd/sys/common/libunwind.rs @@ -99,39 +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(all(target_os = "windows", target_env="gnu"))] -#[link(name = "gcc_eh")] -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_...) From 9a71c5c33170707cbf01cf0b975b45b0cdfbb2f5 Mon Sep 17 00:00:00 2001 From: Vadim Chugunov Date: Tue, 20 Oct 2015 23:08:00 -0700 Subject: [PATCH 07/10] Use `gcc -print-file-name` for finding C runtime startup objects: reverted changes in configure, refactored target.mk --- configure | 15 --------------- mk/target.mk | 41 +++++++++++++++++++++++++++-------------- 2 files changed, 27 insertions(+), 29 deletions(-) diff --git a/configure b/configure index 1360a1ff0ee88..60d366100f8c2 100755 --- a/configure +++ b/configure @@ -625,7 +625,6 @@ valopt_nosave local-rust-root "/usr/local" "set prefix for local rust binary" valopt_nosave host "${CFG_BUILD}" "GNUs ./configure syntax LLVM host triples" valopt_nosave target "${CFG_HOST}" "GNUs ./configure syntax LLVM target triples" valopt_nosave mandir "${CFG_PREFIX}/share/man" "install man pages in PATH" -valopt_nosave libc-dir "/usr/lib" "installation directory of the system libc" # Temporarily support old triples until buildbots get updated CFG_BUILD=$(to_llvm_triple $CFG_BUILD) @@ -1081,9 +1080,6 @@ program_transform_name=$($CFG_CC -v 2>&1 | sed -n "s/.*--program-transform-name= CFG_STDCPP_NAME=$(echo "stdc++" | sed "${program_transform_name}") putvar CFG_STDCPP_NAME -#CFG_LIB_SEARCH_PATH=$($CFG_CC -print-search-dirs | sed -n "/libraries: =/ { s/.*=//; P }") -#putvar CFG_LIB_SEARCH_PATH - # a little post-processing of various config values CFG_PREFIX=${CFG_PREFIX%/} CFG_MANDIR=${CFG_MANDIR%/} @@ -1284,16 +1280,6 @@ $ pacman -R cmake && pacman -S mingw-w64-x86_64-cmake putvar CFG_DISABLE_JEMALLOC ;; - *-windows-gnu) - if [ -z "$CFG_LIBC_DIR_PROVIDED" ]; then - # Use gcc location to find mingw libc directory - for dir in $(dirname $CFG_GCC)/../*-mingw32/lib; do - if [ -d "$dir" ]; then - CFG_LIBC_DIR=$dir - fi - done - fi - ;; *) ;; esac @@ -1752,7 +1738,6 @@ putvar CFG_AARCH64_LINUX_ANDROID_NDK putvar CFG_ARM_LINUX_ANDROIDEABI_NDK putvar CFG_I686_LINUX_ANDROID_NDK putvar CFG_MANDIR -putvar CFG_LIBC_DIR # Avoid spurious warnings from clang by feeding it original source on # ccache-miss rather than preprocessed input. diff --git a/mk/target.mk b/mk/target.mk index fcb3d4a1e53a2..5caf8d8e81eda 100644 --- a/mk/target.mk +++ b/mk/target.mk @@ -146,19 +146,10 @@ define TARGET_RT_STARTUP $$(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 $$(TLIB$(1)_T_$(2)_H_$(3))/stamp.core : $$(TLIB$(1)_T_$(2)_H_$(3))/$$(obj)) \ - $$(eval $$(call COPY_LIBC_STARTUP,$$(TLIB$(1)_T_$(2)_H_$(3)),$$(obj))) ) -endef - -# TARGET_RT_STARTUP's helper for copying LibC startup objects -# $(1) - target lib directory -# $(2) - object name -define COPY_LIBC_STARTUP + $$(eval $$(call TARGET_LIBC_STARTUP_OBJ,$(1),$(2),$(3),$$(obj))) ) -$(1)/$(2) : $$(CFG_LIBC_DIR)/$(2) - @$$(call E, cp: $$@) - @cp $$^ $$@ endef # Macro for building runtime startup/shutdown object files; @@ -167,10 +158,10 @@ endef # $(1) - stage # $(2) - target triple # $(3) - host triple -# $(4) - object name +# $(4) - object basename define TARGET_RUSTRT_STARTUP_OBJ -$$(TLIB$(1)_T_$(2)_H_$(3))/$(4).o:\ +$$(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)) \ @@ -183,9 +174,31 @@ $$(TLIB$(1)_T_$(2)_H_$(3))/$(4).o:\ # but before everything else (since they are needed for linking dylib crates). $$(foreach crate, $$(TARGET_CRATES), \ $$(if $$(findstring core,$$(DEPS_$$(crate))), \ - $$(eval $$(TLIB$(1)_T_$(2)_H_$(3))/stamp.$$(crate) : $$(TLIB$(1)_T_$(2)_H_$(3))/$(4).o) )) + $$(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 From d710f8ba1f2ef8b35d3603875b454955d2d2585c Mon Sep 17 00:00:00 2001 From: Vadim Chugunov Date: Wed, 21 Oct 2015 09:59:24 -0700 Subject: [PATCH 08/10] Moar comments. --- src/librustc_back/target/windows_base.rs | 6 ++-- src/libstd/lib.rs | 9 ------ src/libstd/rt.rs | 5 +++ src/libstd/sys/common/unwind/gcc.rs | 5 +++ src/libstd/sys/common/unwind/mod.rs | 41 ++++++++++++++---------- src/rtstartup/rsbegin.rs | 21 +++++++++++- src/rtstartup/rsend.rs | 2 ++ 7 files changed, 59 insertions(+), 30 deletions(-) diff --git a/src/librustc_back/target/windows_base.rs b/src/librustc_back/target/windows_base.rs index fc1e192f1e15d..4fa57726ef5f0 100644 --- a/src/librustc_back/target/windows_base.rs +++ b/src/librustc_back/target/windows_base.rs @@ -65,11 +65,11 @@ pub fn opts() -> TargetOptions { "-nostdlib".to_string(), ), pre_link_objects_exe: vec!( - "crt2.o".to_string(), - "rsbegin.o".to_string(), + "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(), + "dllcrt2.o".to_string(), // mingw C runtime initialization for dlls "rsbegin.o".to_string(), ), late_link_args: vec!( diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index f318cde71bd66..a624b3521267a 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -400,15 +400,6 @@ pub mod __rand { pub use rand::{thread_rng, ThreadRng, Rng}; } -// 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 explicitly. -#[doc(hidden)] -#[unstable(feature = "eh_frame_registry", issue = "0")] -#[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))] -pub mod __frame_registry { - pub use sys_common::unwind::imp::eh_frame_registry::*; -} - // Include a number of private modules that exist solely to provide // the rustdoc documentation for primitive types. Using `include!` // because rustdoc only looks for these modules at the crate level. 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/unwind/gcc.rs b/src/libstd/sys/common/unwind/gcc.rs index 6d82eb7dfb198..0a598b559514c 100644 --- a/src/libstd/sys/common/unwind/gcc.rs +++ b/src/libstd/sys/common/unwind/gcc.rs @@ -232,6 +232,7 @@ 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] @@ -241,6 +242,10 @@ unsafe extern fn rust_eh_unwind_resume(panic_ctx: *mut u8) -> ! { #[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); 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/rtstartup/rsbegin.rs b/src/rtstartup/rsbegin.rs index 17684b74b7019..af9a01ea2e4f7 100644 --- a/src/rtstartup/rsbegin.rs +++ b/src/rtstartup/rsbegin.rs @@ -8,8 +8,21 @@ // 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)] -#![feature(linkage)] #![crate_type="rlib"] #![no_std] @@ -20,27 +33,33 @@ 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, diff --git a/src/rtstartup/rsend.rs b/src/rtstartup/rsend.rs index df7759877e9f8..ecb6a228e1776 100644 --- a/src/rtstartup/rsend.rs +++ b/src/rtstartup/rsend.rs @@ -8,6 +8,8 @@ // 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"] From afc3046515064e9f40d9f6ad491b4791c3d0c8af Mon Sep 17 00:00:00 2001 From: Vadim Chugunov Date: Fri, 23 Oct 2015 09:35:02 -0700 Subject: [PATCH 09/10] Drop `advapi32` and `shell32` from late_link_args. --- src/librustc_back/target/windows_base.rs | 2 -- src/libstd/sys/windows/c.rs | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/librustc_back/target/windows_base.rs b/src/librustc_back/target/windows_base.rs index 4fa57726ef5f0..634a63cf0bb34 100644 --- a/src/librustc_back/target/windows_base.rs +++ b/src/librustc_back/target/windows_base.rs @@ -77,8 +77,6 @@ pub fn opts() -> TargetOptions { "-lmingw32".to_string(), "-lgcc".to_string(), // alas, mingw* libraries above depend on libgcc "-lmsvcrt".to_string(), - "-ladvapi32".to_string(), - "-lshell32".to_string(), "-luser32".to_string(), "-lkernel32".to_string(), ), 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; From 0332ee9f6336c17e8e8e7524cd2db1c6a92c1a30 Mon Sep 17 00:00:00 2001 From: Vadim Chugunov Date: Sat, 31 Oct 2015 18:47:38 -0700 Subject: [PATCH 10/10] Fix stage0 ICE caused by the old _Unwind_Resume override trickery. --- src/libstd/sys/common/libunwind.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libstd/sys/common/libunwind.rs b/src/libstd/sys/common/libunwind.rs index feb05c7b56008..75bb11216e1a2 100644 --- a/src/libstd/sys/common/libunwind.rs +++ b/src/libstd/sys/common/libunwind.rs @@ -131,6 +131,8 @@ extern "C" { 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) -> !; }