Skip to content

Commit

Permalink
Don't use GCC's startup objects (crtbegin.o/crtend.o); build and use …
Browse files Browse the repository at this point in the history
…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.
  • Loading branch information
vadimcn committed Oct 19, 2015
1 parent c807ff3 commit bd0cf1b
Show file tree
Hide file tree
Showing 12 changed files with 217 additions and 24 deletions.
15 changes: 15 additions & 0 deletions configure
Expand Up @@ -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)
Expand Down Expand Up @@ -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%/}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions mk/cfg/i686-pc-windows-gnu.mk
Expand Up @@ -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
1 change: 1 addition & 0 deletions mk/cfg/x86_64-pc-windows-gnu.mk
Expand Up @@ -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
59 changes: 59 additions & 0 deletions mk/target.mk
Expand Up @@ -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
Expand Down Expand Up @@ -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))))))
13 changes: 4 additions & 9 deletions src/librustc_back/target/i686_pc_windows_gnu.rs
Expand Up @@ -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(),
Expand All @@ -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,
}
}
21 changes: 16 additions & 5 deletions src/librustc_back/target/mod.rs
Expand Up @@ -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<String>,
/// Objects to link before all others, always found within the
/// sysroot folder.
pub pre_link_objects_exe: Vec<String>, // ... when linking an executable
pub pre_link_objects_dll: Vec<String>, // ... 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<String>,
/// Objects to link after all others, always found within the
/// sysroot folder.
pub post_link_objects: Vec<String>,
/// Linker arguments that are unconditionally passed *after* any
/// user-defined libraries.
pub post_link_args: Vec<String>,
/// Objects to link before and after all others, always found within the
/// sysroot folder.
pub pre_link_objects: Vec<String>,
pub post_link_objects: Vec<String>,

/// Default CPU to pass to LLVM. Corresponds to `llc -mcpu=$cpu`. Defaults
/// to "default".
pub cpu: String,
Expand Down Expand Up @@ -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(),
Expand Down
30 changes: 26 additions & 4 deletions src/librustc_back/target/windows_base.rs
Expand Up @@ -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!(
Expand Down Expand Up @@ -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()
Expand Down
3 changes: 0 additions & 3 deletions src/librustc_back/target/x86_64_pc_windows_gnu.rs
Expand Up @@ -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(),
Expand Down
4 changes: 2 additions & 2 deletions src/librustc_back/target/x86_64_unknown_linux_musl.rs
Expand Up @@ -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
Expand Down
9 changes: 8 additions & 1 deletion src/librustc_trans/back/link.rs
Expand Up @@ -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));
}

Expand All @@ -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));
}
Expand Down
61 changes: 61 additions & 0 deletions 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![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;
}
}
24 changes: 24 additions & 0 deletions 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![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;
}

0 comments on commit bd0cf1b

Please sign in to comment.