From bd0cf1ba13566417c71ce8908af39f94cbbb8468 Mon Sep 17 00:00:00 2001 From: Vadim Chugunov Date: Sun, 18 Oct 2015 14:32:50 -0700 Subject: [PATCH] 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 60d366100f8..1360a1ff0ee 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 174671a9a88..ced8bf43163 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 4118ea26c07..0f49b6d585c 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 d6fa55bf7f5..fcb3d4a1e53 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 c825f6043d2..fa12bbd8932 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 197f8c760b0..7af31e33aba 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 @@ fn default() -> 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 2e597367902..fc1e192f1e1 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 2bd363e46bb..3e843853915 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 @@ 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 a5ac78cd5b6..dafbb924a9c 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 bae14afd81d..21b438ae814 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 00000000000..17684b74b70 --- /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 00000000000..df7759877e9 --- /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; +}