Skip to content

Commit

Permalink
Auto merge of #29177 - vadimcn:rtstuff, r=alexcrichton
Browse files Browse the repository at this point in the history
Note: for now, this change only affects `-windows-gnu` builds.

So why was this `libgcc` dylib dependency needed in the first place?
The stack unwinder needs to know about locations of unwind tables of all the modules loaded in the current process.  The easiest portable way of achieving this is to have each module register itself with the unwinder when loaded into the process.  All modules compiled by GCC do this by calling the __register_frame_info() in their startup code (that's `crtbegin.o` and `crtend.o`, which are automatically linked into any gcc output).
Another important piece is that there should be only one copy of the unwinder (and thus unwind tables registry) in the process.  This pretty much means that the unwinder must be in a shared library (unless everything is statically linked). 

Now, Rust compiler tries very hard to make sure that any given Rust crate appears in the final output just once.   So if we link the unwinder statically to one of Rust's crates, everything should be fine.

Unfortunately, GCC startup objects are built under assumption that `libgcc` is the one true place for the unwind info registry, so I couldn't find any better way than to replace them.  So out go `crtbegin`/`crtend`, in come `rsbegin`/`rsend`!  

A side benefit of this change is that rustc is now more in control of the command line that goes to the linker, so we could stop using `gcc` as the linker driver and just invoke `ld` directly.
  • Loading branch information
bors committed Nov 1, 2015
2 parents 6d43fef + 0332ee9 commit 7140918
Show file tree
Hide file tree
Showing 28 changed files with 383 additions and 135 deletions.
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
72 changes: 72 additions & 0 deletions mk/target.mk
Expand Up @@ -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
Expand Down Expand Up @@ -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))))))
2 changes: 2 additions & 0 deletions src/doc/trpl/custom-allocators.md
Expand Up @@ -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:
Expand Down
2 changes: 2 additions & 0 deletions src/doc/trpl/lang-items.md
Expand Up @@ -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
Expand Down
8 changes: 7 additions & 1 deletion src/doc/trpl/no-stdlib.md
Expand Up @@ -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!
```

Expand All @@ -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!
```

Expand Down Expand Up @@ -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() {}
```

Expand Down
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
28 changes: 24 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,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()
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 @@ -852,7 +852,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 @@ -868,6 +874,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
13 changes: 13 additions & 0 deletions src/librustc_trans/trans/base.rs
Expand Up @@ -943,6 +943,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();
Expand Down
4 changes: 1 addition & 3 deletions src/librustc_trans/trans/cleanup.rs
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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();
Expand Down

0 comments on commit 7140918

Please sign in to comment.