Skip to content

Commit 7140918

Browse files
committed
Auto merge of #29177 - vadimcn:rtstuff, r=alexcrichton
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.
2 parents 6d43fef + 0332ee9 commit 7140918

28 files changed

+383
-135
lines changed

mk/cfg/i686-pc-windows-gnu.mk

+1
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,4 @@ CFG_LDPATH_i686-pc-windows-gnu :=
2222
CFG_RUN_i686-pc-windows-gnu=$(2)
2323
CFG_RUN_TARG_i686-pc-windows-gnu=$(call CFG_RUN_i686-pc-windows-gnu,,$(2))
2424
CFG_GNU_TRIPLE_i686-pc-windows-gnu := i686-w64-mingw32
25+
CFG_LIBC_STARTUP_OBJECTS_i686-pc-windows-gnu := crt2.o dllcrt2.o

mk/cfg/x86_64-pc-windows-gnu.mk

+1
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,4 @@ CFG_LDPATH_x86_64-pc-windows-gnu :=
2222
CFG_RUN_x86_64-pc-windows-gnu=$(2)
2323
CFG_RUN_TARG_x86_64-pc-windows-gnu=$(call CFG_RUN_x86_64-pc-windows-gnu,,$(2))
2424
CFG_GNU_TRIPLE_x86_64-pc-windows-gnu := x86_64-w64-mingw32
25+
CFG_LIBC_STARTUP_OBJECTS_x86_64-pc-windows-gnu := crt2.o dllcrt2.o

mk/target.mk

+72
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,73 @@ $$(TBIN$(1)_T_$(2)_H_$(3))/$(4)$$(X_$(2)): \
132132

133133
endef
134134

135+
# Macro for building runtime startup objects
136+
# Of those we have two kinds:
137+
# - Rust runtime-specific: these are Rust's equivalents of GCC's crti.o/crtn.o,
138+
# - LibC-specific: these we don't build ourselves, but copy them from the system lib directory.
139+
#
140+
# $(1) - stage
141+
# $(2) - target triple
142+
# $(3) - host triple
143+
define TARGET_RT_STARTUP
144+
145+
# Expand build rules for rsbegin.o and rsend.o
146+
$$(foreach obj,rsbegin rsend, \
147+
$$(eval $$(call TARGET_RUSTRT_STARTUP_OBJ,$(1),$(2),$(3),$$(obj))) )
148+
149+
# Expand build rules for libc startup objects
150+
$$(foreach obj,$$(CFG_LIBC_STARTUP_OBJECTS_$(2)), \
151+
$$(eval $$(call TARGET_LIBC_STARTUP_OBJ,$(1),$(2),$(3),$$(obj))) )
152+
153+
endef
154+
155+
# Macro for building runtime startup/shutdown object files;
156+
# these are Rust's equivalent of crti.o, crtn.o
157+
#
158+
# $(1) - stage
159+
# $(2) - target triple
160+
# $(3) - host triple
161+
# $(4) - object basename
162+
define TARGET_RUSTRT_STARTUP_OBJ
163+
164+
$$(TLIB$(1)_T_$(2)_H_$(3))/$(4).o: \
165+
$(S)src/rtstartup/$(4).rs \
166+
$$(TLIB$(1)_T_$(2)_H_$(3))/stamp.core \
167+
$$(HSREQ$(1)_T_$(2)_H_$(3)) \
168+
| $$(TBIN$(1)_T_$(2)_H_$(3))/
169+
@$$(call E, rustc: $$@)
170+
$$(STAGE$(1)_T_$(2)_H_$(3)) --emit=obj -o $$@ $$<
171+
172+
# Add dependencies on Rust startup objects to all crates that depend on core.
173+
# This ensures that they are built after core (since they depend on it),
174+
# but before everything else (since they are needed for linking dylib crates).
175+
$$(foreach crate, $$(TARGET_CRATES), \
176+
$$(if $$(findstring core,$$(DEPS_$$(crate))), \
177+
$$(TLIB$(1)_T_$(2)_H_$(3))/stamp.$$(crate))) : $$(TLIB$(1)_T_$(2)_H_$(3))/$(4).o
178+
179+
endef
180+
181+
# Macro for copying libc startup objects into the target's lib directory.
182+
#
183+
# $(1) - stage
184+
# $(2) - target triple
185+
# $(3) - host triple
186+
# $(4) - object name
187+
define TARGET_LIBC_STARTUP_OBJ
188+
189+
# Ask gcc where the startup object is located
190+
$$(TLIB$(1)_T_$(2)_H_$(3))/$(4) : $$(shell $$(CC_$(2)) -print-file-name=$(4))
191+
@$$(call E, cp: $$@)
192+
@cp $$^ $$@
193+
194+
# Make sure this is done before libcore has finished building
195+
# (libcore itself does not depend on these objects, but other crates do,
196+
# so might as well do it here)
197+
$$(TLIB$(1)_T_$(2)_H_$(3))/stamp.core : $$(TLIB$(1)_T_$(2)_H_$(3))/$(4)
198+
199+
endef
200+
201+
135202
# Every recipe in RUST_TARGET_STAGE_N outputs to $$(TLIB$(1)_T_$(2)_H_$(3),
136203
# a directory that can be cleaned out during the middle of a run of
137204
# the get-snapshot.py script. Therefore, every recipe needs to have
@@ -174,3 +241,8 @@ $(foreach host,$(CFG_HOST), \
174241
$(foreach stage,$(STAGES), \
175242
$(foreach tool,$(TOOLS), \
176243
$(eval $(call TARGET_TOOL,$(stage),$(target),$(host),$(tool)))))))
244+
245+
$(foreach host,$(CFG_HOST), \
246+
$(foreach target,$(CFG_TARGET), \
247+
$(foreach stage,$(STAGES), \
248+
$(eval $(call TARGET_RT_STARTUP,$(stage),$(target),$(host))))))

src/doc/trpl/custom-allocators.md

+2
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,8 @@ pub extern fn __rust_usable_size(size: usize, _align: usize) -> usize {
140140
# #[lang = "panic_fmt"] fn panic_fmt() {}
141141
# #[lang = "eh_personality"] fn eh_personality() {}
142142
# #[lang = "eh_unwind_resume"] extern fn eh_unwind_resume() {}
143+
# #[no_mangle] pub extern fn rust_eh_register_frames () {}
144+
# #[no_mangle] pub extern fn rust_eh_unregister_frames () {}
143145
```
144146

145147
After we compile this crate, it can be used as follows:

src/doc/trpl/lang-items.md

+2
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ fn main(argc: isize, argv: *const *const u8) -> isize {
5454
#[lang = "eh_personality"] extern fn eh_personality() {}
5555
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }
5656
# #[lang = "eh_unwind_resume"] extern fn rust_eh_unwind_resume() {}
57+
# #[no_mangle] pub extern fn rust_eh_register_frames () {}
58+
# #[no_mangle] pub extern fn rust_eh_unregister_frames () {}
5759
```
5860

5961
Note the use of `abort`: the `exchange_malloc` lang item is assumed to

src/doc/trpl/no-stdlib.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ fn start(_argc: isize, _argv: *const *const u8) -> isize {
3434
#[lang = "eh_personality"] extern fn eh_personality() {}
3535
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }
3636
# #[lang = "eh_unwind_resume"] extern fn rust_eh_unwind_resume() {}
37+
# #[no_mangle] pub extern fn rust_eh_register_frames () {}
38+
# #[no_mangle] pub extern fn rust_eh_unregister_frames () {}
3739
# // fn main() {} tricked you, rustdoc!
3840
```
3941

@@ -60,6 +62,8 @@ pub extern fn main(argc: i32, argv: *const *const u8) -> i32 {
6062
#[lang = "eh_personality"] extern fn eh_personality() {}
6163
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} }
6264
# #[lang = "eh_unwind_resume"] extern fn rust_eh_unwind_resume() {}
65+
# #[no_mangle] pub extern fn rust_eh_register_frames () {}
66+
# #[no_mangle] pub extern fn rust_eh_unregister_frames () {}
6367
# // fn main() {} tricked you, rustdoc!
6468
```
6569

@@ -145,8 +149,10 @@ extern fn panic_fmt(args: &core::fmt::Arguments,
145149
}
146150

147151
#[lang = "eh_personality"] extern fn eh_personality() {}
148-
# #[lang = "eh_unwind_resume"] extern fn rust_eh_unwind_resume() {}
149152
# #[start] fn start(argc: isize, argv: *const *const u8) -> isize { 0 }
153+
# #[lang = "eh_unwind_resume"] extern fn rust_eh_unwind_resume() {}
154+
# #[no_mangle] pub extern fn rust_eh_register_frames () {}
155+
# #[no_mangle] pub extern fn rust_eh_unregister_frames () {}
150156
# fn main() {}
151157
```
152158

src/librustc_back/target/i686_pc_windows_gnu.rs

+4-9
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,12 @@
1111
use target::Target;
1212

1313
pub fn target() -> Target {
14-
let mut options = super::windows_base::opts();
15-
options.cpu = "pentium4".to_string();
14+
let mut base = super::windows_base::opts();
15+
base.cpu = "pentium4".to_string();
1616

1717
// Mark all dynamic libraries and executables as compatible with the larger 4GiB address
1818
// space available to x86 Windows binaries on x86_64.
19-
options.pre_link_args.push("-Wl,--large-address-aware".to_string());
20-
21-
// Make sure that we link to the dynamic libgcc, otherwise cross-module
22-
// DWARF stack unwinding will not work.
23-
// This behavior may be overridden by -Clink-args="-static-libgcc"
24-
options.pre_link_args.push("-shared-libgcc".to_string());
19+
base.pre_link_args.push("-Wl,--large-address-aware".to_string());
2520

2621
Target {
2722
llvm_target: "i686-pc-windows-gnu".to_string(),
@@ -31,6 +26,6 @@ pub fn target() -> Target {
3126
target_os: "windows".to_string(),
3227
target_env: "gnu".to_string(),
3328
target_vendor: "pc".to_string(),
34-
options: options,
29+
options: base,
3530
}
3631
}

src/librustc_back/target/mod.rs

+16-5
Original file line numberDiff line numberDiff line change
@@ -98,16 +98,25 @@ pub struct TargetOptions {
9898
pub linker: String,
9999
/// Archive utility to use when managing archives. Defaults to "ar".
100100
pub ar: String,
101+
101102
/// Linker arguments that are unconditionally passed *before* any
102103
/// user-defined libraries.
103104
pub pre_link_args: Vec<String>,
105+
/// Objects to link before all others, always found within the
106+
/// sysroot folder.
107+
pub pre_link_objects_exe: Vec<String>, // ... when linking an executable
108+
pub pre_link_objects_dll: Vec<String>, // ... when linking a dylib
109+
/// Linker arguments that are unconditionally passed after any
110+
/// user-defined but before post_link_objects. Standard platform
111+
/// libraries that should be always be linked to, usually go here.
112+
pub late_link_args: Vec<String>,
113+
/// Objects to link after all others, always found within the
114+
/// sysroot folder.
115+
pub post_link_objects: Vec<String>,
104116
/// Linker arguments that are unconditionally passed *after* any
105117
/// user-defined libraries.
106118
pub post_link_args: Vec<String>,
107-
/// Objects to link before and after all others, always found within the
108-
/// sysroot folder.
109-
pub pre_link_objects: Vec<String>,
110-
pub post_link_objects: Vec<String>,
119+
111120
/// Default CPU to pass to LLVM. Corresponds to `llc -mcpu=$cpu`. Defaults
112121
/// to "default".
113122
pub cpu: String,
@@ -219,8 +228,10 @@ impl Default for TargetOptions {
219228
no_compiler_rt: false,
220229
no_default_libraries: true,
221230
position_independent_executables: false,
222-
pre_link_objects: Vec::new(),
231+
pre_link_objects_exe: Vec::new(),
232+
pre_link_objects_dll: Vec::new(),
223233
post_link_objects: Vec::new(),
234+
late_link_args: Vec::new(),
224235
archive_format: String::new(),
225236
custom_unwind_resume: false,
226237
lib_allocation_crate: "alloc_system".to_string(),

src/librustc_back/target/windows_base.rs

+24-4
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,7 @@ pub fn opts() -> TargetOptions {
2323
exe_suffix: ".exe".to_string(),
2424
staticlib_prefix: "".to_string(),
2525
staticlib_suffix: ".lib".to_string(),
26-
// Unfortunately right now passing -nodefaultlibs to gcc on windows
27-
// doesn't work so hot (in terms of native dependencies). This flag
28-
// should hopefully be removed one day though!
29-
no_default_libraries: false,
26+
no_default_libraries: true,
3027
is_like_windows: true,
3128
archive_format: "gnu".to_string(),
3229
pre_link_args: vec!(
@@ -63,7 +60,30 @@ pub fn opts() -> TargetOptions {
6360

6461
// Always enable DEP (NX bit) when it is available
6562
"-Wl,--nxcompat".to_string(),
63+
64+
// Do not use the standard system startup files or libraries when linking
65+
"-nostdlib".to_string(),
66+
),
67+
pre_link_objects_exe: vec!(
68+
"crt2.o".to_string(), // mingw C runtime initialization for executables
69+
"rsbegin.o".to_string(), // Rust compiler runtime initialization, see rsbegin.rs
70+
),
71+
pre_link_objects_dll: vec!(
72+
"dllcrt2.o".to_string(), // mingw C runtime initialization for dlls
73+
"rsbegin.o".to_string(),
74+
),
75+
late_link_args: vec!(
76+
"-lmingwex".to_string(),
77+
"-lmingw32".to_string(),
78+
"-lgcc".to_string(), // alas, mingw* libraries above depend on libgcc
79+
"-lmsvcrt".to_string(),
80+
"-luser32".to_string(),
81+
"-lkernel32".to_string(),
82+
),
83+
post_link_objects: vec!(
84+
"rsend.o".to_string()
6685
),
86+
custom_unwind_resume: true,
6787
exe_allocation_crate: super::maybe_jemalloc(),
6888

6989
.. Default::default()

src/librustc_back/target/x86_64_pc_windows_gnu.rs

-3
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,7 @@ use target::Target;
1313
pub fn target() -> Target {
1414
let mut base = super::windows_base::opts();
1515
base.cpu = "x86-64".to_string();
16-
// On Win64 unwinding is handled by the OS, so we can link libgcc statically.
17-
base.pre_link_args.push("-static-libgcc".to_string());
1816
base.pre_link_args.push("-m64".to_string());
19-
base.custom_unwind_resume = true;
2017

2118
Target {
2219
llvm_target: "x86_64-pc-windows-gnu".to_string(),

src/librustc_back/target/x86_64_unknown_linux_musl.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ pub fn target() -> Target {
5858
//
5959
// Each target directory for musl has these object files included in it so
6060
// they'll be included from there.
61-
base.pre_link_objects.push("crt1.o".to_string());
62-
base.pre_link_objects.push("crti.o".to_string());
61+
base.pre_link_objects_exe.push("crt1.o".to_string());
62+
base.pre_link_objects_exe.push("crti.o".to_string());
6363
base.post_link_objects.push("crtn.o".to_string());
6464

6565
// MUSL support doesn't currently include dynamic linking, so there's no

src/librustc_trans/back/link.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -852,7 +852,13 @@ fn link_natively(sess: &Session, dylib: bool,
852852

853853
let root = sess.target_filesearch(PathKind::Native).get_lib_path();
854854
cmd.args(&sess.target.target.options.pre_link_args);
855-
for obj in &sess.target.target.options.pre_link_objects {
855+
856+
let pre_link_objects = if dylib {
857+
&sess.target.target.options.pre_link_objects_dll
858+
} else {
859+
&sess.target.target.options.pre_link_objects_exe
860+
};
861+
for obj in pre_link_objects {
856862
cmd.arg(root.join(obj));
857863
}
858864

@@ -868,6 +874,7 @@ fn link_natively(sess: &Session, dylib: bool,
868874
linker.link_staticlib("compiler-rt");
869875
}
870876
}
877+
cmd.args(&sess.target.target.options.late_link_args);
871878
for obj in &sess.target.target.options.post_link_objects {
872879
cmd.arg(root.join(obj));
873880
}

src/librustc_trans/trans/base.rs

+13
Original file line numberDiff line numberDiff line change
@@ -943,6 +943,19 @@ pub fn call_lifetime_end(cx: Block, ptr: ValueRef) {
943943
Call(cx, lifetime_end, &[C_u64(ccx, size), ptr], None, DebugLoc::None);
944944
}
945945

946+
// Generates code for resumption of unwind at the end of a landing pad.
947+
pub fn trans_unwind_resume(bcx: Block, lpval: ValueRef) {
948+
if !bcx.sess().target.target.options.custom_unwind_resume {
949+
Resume(bcx, lpval);
950+
} else {
951+
let exc_ptr = ExtractValue(bcx, lpval, 0);
952+
let llunwresume = bcx.fcx.eh_unwind_resume();
953+
Call(bcx, llunwresume, &[exc_ptr], None, DebugLoc::None);
954+
Unreachable(bcx);
955+
}
956+
}
957+
958+
946959
pub fn call_memcpy(cx: Block, dst: ValueRef, src: ValueRef, n_bytes: ValueRef, align: u32) {
947960
let _icx = push_ctxt("call_memcpy");
948961
let ccx = cx.ccx();

src/librustc_trans/trans/cleanup.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -732,7 +732,7 @@ impl<'blk, 'tcx> CleanupHelperMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx
732732
"create_landing_pad() should have set this");
733733
let lp = build::Load(prev_bcx, personality);
734734
base::call_lifetime_end(prev_bcx, personality);
735-
build::Resume(prev_bcx, lp);
735+
base::trans_unwind_resume(prev_bcx, lp);
736736
prev_llbb = prev_bcx.llbb;
737737
break;
738738
}
@@ -845,8 +845,6 @@ impl<'blk, 'tcx> CleanupHelperMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx
845845

846846
debug!("get_or_create_landing_pad");
847847

848-
self.inject_unwind_resume_hook();
849-
850848
// Check if a landing pad block exists; if not, create one.
851849
{
852850
let mut scopes = self.scopes.borrow_mut();

0 commit comments

Comments
 (0)