mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
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.
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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))))))
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {}
|
||||
```
|
||||
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
@@ -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(),
|
||||
|
||||
@@ -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,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()
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -732,7 +732,7 @@ fn trans_cleanups_to_exit_scope(&'blk self,
|
||||
"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 @@ fn get_or_create_landing_pad(&'blk self) -> BasicBlockRef {
|
||||
|
||||
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();
|
||||
|
||||
@@ -561,53 +561,33 @@ pub fn eh_personality(&self) -> ValueRef {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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,
|
||||
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())
|
||||
// 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 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -146,8 +146,8 @@ pub struct LocalCrateContext<'tcx> {
|
||||
dbg_cx: Option<debuginfo::CrateDebugContext<'tcx>>,
|
||||
|
||||
eh_personality: RefCell<Option<ValueRef>>,
|
||||
eh_unwind_resume: RefCell<Option<ValueRef>>,
|
||||
rust_try_fn: RefCell<Option<ValueRef>>,
|
||||
unwind_resume_hooked: Cell<bool>,
|
||||
|
||||
intrinsics: RefCell<FnvHashMap<&'static str, ValueRef>>,
|
||||
|
||||
@@ -469,8 +469,8 @@ fn new<'a>(shared: &SharedCrateContext<'a, '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),
|
||||
type_of_depth: Cell::new(0),
|
||||
@@ -732,12 +732,12 @@ pub fn eh_personality<'a>(&'a self) -> &'a RefCell<Option<ValueRef>> {
|
||||
&self.local.eh_personality
|
||||
}
|
||||
|
||||
pub fn rust_try_fn<'a>(&'a self) -> &'a RefCell<Option<ValueRef>> {
|
||||
&self.local.rust_try_fn
|
||||
pub fn eh_unwind_resume<'a>(&'a self) -> &'a RefCell<Option<ValueRef>> {
|
||||
&self.local.eh_unwind_resume
|
||||
}
|
||||
|
||||
pub fn unwind_resume_hooked<'a>(&'a self) -> &'a Cell<bool> {
|
||||
&self.local.unwind_resume_hooked
|
||||
pub fn rust_try_fn<'a>(&'a self) -> &'a RefCell<Option<ValueRef>> {
|
||||
&self.local.rust_try_fn
|
||||
}
|
||||
|
||||
fn intrinsics<'a>(&'a self) -> &'a RefCell<FnvHashMap<&'static str, ValueRef>> {
|
||||
|
||||
@@ -1154,7 +1154,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);
|
||||
|
||||
@@ -32,6 +32,11 @@
|
||||
// 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 {
|
||||
|
||||
@@ -99,35 +99,23 @@ pub enum _Unwind_Context {}
|
||||
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_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_...)
|
||||
@@ -142,6 +130,11 @@ fn _Unwind_SjLj_RaiseException(e: *mut _Unwind_Exception)
|
||||
-> _Unwind_Reason_Code;
|
||||
|
||||
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) -> !;
|
||||
}
|
||||
|
||||
// ... and now we just providing access to SjLj counterspart
|
||||
|
||||
@@ -231,3 +231,36 @@ fn __gcc_personality_v0(state: uw::_Unwind_State,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// See docs in the `unwind` module.
|
||||
#[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);
|
||||
}
|
||||
|
||||
#[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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
//!
|
||||
//! Once stack has been unwound down to the handler frame level, unwinding stops
|
||||
//! and the last personality routine transfers control to the catch block.
|
||||
//!
|
||||
//! ## `eh_personality` and `eh_unwind_resume`
|
||||
//!
|
||||
//! 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.
|
||||
//!
|
||||
//! ## Frame unwind info registration
|
||||
//!
|
||||
//! 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.
|
||||
//!
|
||||
//! 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.
|
||||
//!
|
||||
//! 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 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.
|
||||
//!
|
||||
//! Currently Rust uses unwind runtime provided by libgcc.
|
||||
//! sections via unwinder API (see `rust_eh_register_frames`/`rust_eh_unregister_frames`).
|
||||
|
||||
#![allow(dead_code)]
|
||||
#![allow(unused_imports)]
|
||||
|
||||
@@ -190,17 +190,10 @@ pub unsafe fn cleanup(ptr: *mut u8) -> Box<Any + Send + 'static> {
|
||||
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,
|
||||
|
||||
@@ -356,6 +356,7 @@ pub struct EXCEPTION_POINTERS {
|
||||
|
||||
#[link(name = "ws2_32")]
|
||||
#[link(name = "userenv")]
|
||||
#[link(name = "shell32")]
|
||||
extern "system" {
|
||||
pub fn WSAStartup(wVersionRequested: libc::WORD,
|
||||
lpWSAData: LPWSADATA) -> libc::c_int;
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
// 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.
|
||||
|
||||
// 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)]
|
||||
|
||||
#![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"]
|
||||
// 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,
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
// 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.
|
||||
|
||||
// See rsbegin.rs for details.
|
||||
|
||||
#![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;
|
||||
}
|
||||
@@ -20,3 +20,5 @@
|
||||
#[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 () {}
|
||||
|
||||
@@ -20,4 +20,5 @@
|
||||
#[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 () {}
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
#[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 {
|
||||
|
||||
Reference in New Issue
Block a user