Scale back changes made
This commit is contained in:
parent
315750ac92
commit
4cc025d83c
|
@ -1,29 +1,24 @@
|
|||
# x86_64-pc-windows-msvc configuration
|
||||
CROSS_PREFIX_x86_64-pc-windows-msvc=
|
||||
CC_x86_64-pc-windows-msvc=cl
|
||||
LINK_x86_64-pc-windows-msvc=link
|
||||
CXX_x86_64-pc-windows-msvc=g++
|
||||
CPP_x86_64-pc-windows-msvc=gcc -E
|
||||
CXX_x86_64-pc-windows-msvc=cl
|
||||
CPP_x86_64-pc-windows-msvc=cl
|
||||
AR_x86_64-pc-windows-msvc=llvm-ar
|
||||
CFG_LIB_NAME_x86_64-pc-windows-msvc=$(1).dll
|
||||
CFG_STATIC_LIB_NAME_x86_64-pc-windows-msvc=$(1).lib
|
||||
CFG_LIB_GLOB_x86_64-pc-windows-msvc=$(1)-*.dll
|
||||
CFG_LIB_DSYM_GLOB_x86_64-pc-windows-msvc=$(1)-*.dylib.dSYM
|
||||
CFG_JEMALLOC_CFLAGS_x86_64-pc-windows-msvc := $(CFLAGS)
|
||||
CFG_GCCISH_CFLAGS_x86_64-pc-windows-msvc := $(CFLAGS)
|
||||
CFG_GCCISH_CXXFLAGS_x86_64-pc-windows-msvc := -fno-rtti $(CXXFLAGS)
|
||||
CFG_GCCISH_LINK_FLAGS_x86_64-pc-windows-msvc := -shared -g -m64
|
||||
CFG_JEMALLOC_CFLAGS_x86_64-pc-windows-msvc :=
|
||||
CFG_GCCISH_CFLAGS_x86_64-pc-windows-msvc :=
|
||||
CFG_GCCISH_CXXFLAGS_x86_64-pc-windows-msvc :=
|
||||
CFG_GCCISH_LINK_FLAGS_x86_64-pc-windows-msvc :=
|
||||
CFG_GCCISH_DEF_FLAG_x86_64-pc-windows-msvc :=
|
||||
CFG_GCCISH_PRE_LIB_FLAGS_x86_64-pc-windows-msvc :=
|
||||
CFG_GCCISH_POST_LIB_FLAGS_x86_64-pc-windows-msvc :=
|
||||
CFG_DEF_SUFFIX_x86_64-pc-windows-msvc := .windows.def
|
||||
CFG_LLC_FLAGS_x86_64-pc-windows-msvc :=
|
||||
CFG_INSTALL_NAME_x86_64-pc-windows-msvc =
|
||||
CFG_EXE_SUFFIX_x86_64-pc-windows-msvc := .exe
|
||||
CFG_WINDOWSY_x86_64-pc-windows-msvc := 1
|
||||
CFG_UNIXY_x86_64-pc-windows-msvc :=
|
||||
CFG_PATH_MUNGE_x86_64-pc-windows-msvc :=
|
||||
CFG_LDPATH_x86_64-pc-windows-msvc :=
|
||||
CFG_RUN_x86_64-pc-windows-msvc=$(2)
|
||||
CFG_RUN_TARG_x86_64-pc-windows-msvc=$(call CFG_RUN_x86_64-pc-windows-msvc,,$(2))
|
||||
CFG_GNU_TRIPLE_x86_64-pc-windows-msvc := x86_64-w64-mingw32
|
||||
CFG_GNU_TRIPLE_x86_64-pc-windows-msvc := x86_64-pc-windows-msvc
|
||||
|
|
|
@ -632,7 +632,6 @@ pub fn default_configuration(sess: &Session) -> ast::CrateConfig {
|
|||
let mut ret = vec![ // Target bindings.
|
||||
attr::mk_word_item(fam.clone()),
|
||||
mk(InternedString::new("target_os"), intern(os)),
|
||||
mk(InternedString::new("target_abi"), intern(abi)),
|
||||
mk(InternedString::new("target_family"), fam),
|
||||
mk(InternedString::new("target_arch"), intern(arch)),
|
||||
mk(InternedString::new("target_endian"), intern(end)),
|
||||
|
|
|
@ -22,7 +22,6 @@ pub fn target() -> Target {
|
|||
target_env: "gnu".to_string(),
|
||||
arch: "aarch64".to_string(),
|
||||
target_os: "linux".to_string(),
|
||||
target_abi: "".to_string(),
|
||||
options: base,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,11 +22,7 @@ pub fn target() -> Target {
|
|||
target_pointer_width: "32".to_string(),
|
||||
arch: "x86".to_string(),
|
||||
target_os: "linux".to_string(),
|
||||
<<<<<<< HEAD
|
||||
target_env: "gnu".to_string(),
|
||||
=======
|
||||
target_abi: "".to_string(),
|
||||
>>>>>>> 9f1453c... Very hacky MSVC hacks.
|
||||
options: base,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -59,6 +59,7 @@ mod freebsd_base;
|
|||
mod linux_base;
|
||||
mod openbsd_base;
|
||||
mod windows_base;
|
||||
mod windows_msvc_base;
|
||||
|
||||
/// Everything `rustc` knows about how to compile for a specific target.
|
||||
///
|
||||
|
|
|
@ -13,8 +13,7 @@ use std::default::Default;
|
|||
|
||||
pub fn opts() -> TargetOptions {
|
||||
TargetOptions {
|
||||
// FIXME(#13846) this should be enabled for windows
|
||||
function_sections: false,
|
||||
function_sections: true,
|
||||
linker: "link".to_string(),
|
||||
dynamic_linking: true,
|
||||
executables: true,
|
||||
|
|
|
@ -24,7 +24,7 @@ pub fn target() -> Target {
|
|||
target_pointer_width: "64".to_string(),
|
||||
arch: "x86_64".to_string(),
|
||||
target_os: "windows".to_string(),
|
||||
target_abi: "msvc".to_string(),
|
||||
target_env: "msvc".to_string(),
|
||||
options: base,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,7 +55,6 @@ pub use self::CallConv::*;
|
|||
pub use self::Visibility::*;
|
||||
pub use self::DiagnosticSeverity::*;
|
||||
pub use self::Linkage::*;
|
||||
pub use self::DLLStorageClass::*;
|
||||
|
||||
use std::ffi::CString;
|
||||
use std::cell::RefCell;
|
||||
|
@ -115,13 +114,6 @@ pub enum Linkage {
|
|||
CommonLinkage = 14,
|
||||
}
|
||||
|
||||
#[derive(Copy)]
|
||||
pub enum DLLStorageClass {
|
||||
DefaultStorageClass = 0,
|
||||
DLLImportStorageClass = 1,
|
||||
DLLExportStorageClass = 2,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum DiagnosticSeverity {
|
||||
|
@ -2133,12 +2125,6 @@ pub fn SetLinkage(global: ValueRef, link: Linkage) {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn SetDLLStorageClass(global: ValueRef, storage_class: DLLStorageClass) {
|
||||
unsafe {
|
||||
LLVMRustSetDLLStorageClass(global, storage_class as c_uint);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn SetUnnamedAddr(global: ValueRef, unnamed: bool) {
|
||||
unsafe {
|
||||
LLVMSetUnnamedAddr(global, unnamed as Bool);
|
||||
|
|
|
@ -8,21 +8,22 @@
|
|||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use super::archive::{ArchiveBuilder, ArchiveConfig, METADATA_FILENAME};
|
||||
use super::archive::{Archive, ArchiveBuilder, ArchiveConfig, METADATA_FILENAME};
|
||||
use super::archive;
|
||||
use super::rpath;
|
||||
use super::rpath::RPathConfig;
|
||||
use super::svh::Svh;
|
||||
|
||||
use super::link_gnu;
|
||||
use super::link_msvc;
|
||||
|
||||
use session::config;
|
||||
use session::config::NoDebugInfo;
|
||||
use session::config::{OutputFilenames, Input, OutputTypeBitcode, OutputTypeExe, OutputTypeObject};
|
||||
use session::search_paths::PathKind;
|
||||
use session::Session;
|
||||
use metadata::common::LinkMeta;
|
||||
use metadata::{encoder, cstore, csearch, creader};
|
||||
use metadata::{encoder, cstore, filesearch, csearch, creader};
|
||||
use metadata::filesearch::FileDoesntMatch;
|
||||
use trans::{CrateContext, CrateTranslation, gensym_name};
|
||||
use middle::ty::{self, Ty};
|
||||
use util::common::time;
|
||||
use util::ppaux;
|
||||
use util::sha2::{Digest, Sha256};
|
||||
use util::fs::fix_windows_verbatim_for_gcc;
|
||||
|
|
|
@ -1,568 +0,0 @@
|
|||
// Copyright 2012-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.
|
||||
|
||||
use super::archive::{Archive, ArchiveConfig};
|
||||
use super::archive;
|
||||
use super::rpath;
|
||||
use super::rpath::RPathConfig;
|
||||
|
||||
use session::config;
|
||||
use session::config::NoDebugInfo;
|
||||
use session::search_paths::PathKind;
|
||||
use session::Session;
|
||||
use metadata::{cstore, filesearch, csearch};
|
||||
use metadata::filesearch::FileDoesntMatch;
|
||||
use trans::{CrateTranslation};
|
||||
use util::common::time;
|
||||
|
||||
use std::str;
|
||||
use std::old_io::{fs, TempDir, Command};
|
||||
use std::old_io;
|
||||
|
||||
// Create a dynamic library or executable
|
||||
//
|
||||
// This will invoke the system linker/cc to create the resulting file. This
|
||||
// links to all upstream files as well.
|
||||
pub fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool,
|
||||
obj_filename: &Path, out_filename: &Path) {
|
||||
let tmpdir = TempDir::new("rustc").ok().expect("needs a temp dir");
|
||||
|
||||
// The invocations of cc share some flags across platforms
|
||||
let pname = super::link::get_cc_prog(sess);
|
||||
let mut cmd = Command::new(&pname[..]);
|
||||
|
||||
cmd.args(&sess.target.target.options.pre_link_args[]);
|
||||
link_args(&mut cmd, sess, dylib, tmpdir.path(),
|
||||
trans, obj_filename, out_filename);
|
||||
cmd.args(&sess.target.target.options.post_link_args[]);
|
||||
if !sess.target.target.options.no_compiler_rt {
|
||||
cmd.arg("-lcompiler-rt");
|
||||
}
|
||||
|
||||
if sess.opts.debugging_opts.print_link_args {
|
||||
println!("{:?}", &cmd);
|
||||
}
|
||||
|
||||
// May have not found libraries in the right formats.
|
||||
sess.abort_if_errors();
|
||||
|
||||
// Invoke the system linker
|
||||
debug!("{:?}", &cmd);
|
||||
let prog = time(sess.time_passes(), "running linker", (), |()| cmd.output());
|
||||
match prog {
|
||||
Ok(prog) => {
|
||||
if !prog.status.success() {
|
||||
sess.err(&format!("linking with `{}` failed: {}",
|
||||
pname,
|
||||
prog.status)[]);
|
||||
sess.note(&format!("{:?}", &cmd)[]);
|
||||
let mut output = prog.error.clone();
|
||||
output.push_all(&prog.output[]);
|
||||
sess.note(str::from_utf8(&output[..]).unwrap());
|
||||
sess.abort_if_errors();
|
||||
}
|
||||
debug!("linker stderr:\n{}", String::from_utf8(prog.error).unwrap());
|
||||
debug!("linker stdout:\n{}", String::from_utf8(prog.output).unwrap());
|
||||
},
|
||||
Err(e) => {
|
||||
sess.err(&format!("could not exec the linker `{}`: {}",
|
||||
pname,
|
||||
e)[]);
|
||||
sess.abort_if_errors();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// On OSX, debuggers need this utility to get run to do some munging of
|
||||
// the symbols
|
||||
if sess.target.target.options.is_like_osx && sess.opts.debuginfo != NoDebugInfo {
|
||||
match Command::new("dsymutil").arg(out_filename).output() {
|
||||
Ok(..) => {}
|
||||
Err(e) => {
|
||||
sess.err(&format!("failed to run dsymutil: {}", e)[]);
|
||||
sess.abort_if_errors();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn link_args(cmd: &mut Command,
|
||||
sess: &Session,
|
||||
dylib: bool,
|
||||
tmpdir: &Path,
|
||||
trans: &CrateTranslation,
|
||||
obj_filename: &Path,
|
||||
out_filename: &Path) {
|
||||
|
||||
// The default library location, we need this to find the runtime.
|
||||
// The location of crates will be determined as needed.
|
||||
let lib_path = sess.target_filesearch(PathKind::All).get_lib_path();
|
||||
|
||||
// target descriptor
|
||||
let t = &sess.target.target;
|
||||
|
||||
cmd.arg("-L").arg(&lib_path);
|
||||
|
||||
cmd.arg("-o").arg(out_filename).arg(obj_filename);
|
||||
|
||||
|
||||
// Stack growth requires statically linking a __morestack function. Note
|
||||
// that this is listed *before* all other libraries. Due to the usage of the
|
||||
// --as-needed flag below, the standard library may only be useful for its
|
||||
// rust_stack_exhausted function. In this case, we must ensure that the
|
||||
// libmorestack.a file appears *before* the standard library (so we put it
|
||||
// at the very front).
|
||||
//
|
||||
// Most of the time this is sufficient, except for when LLVM gets super
|
||||
// clever. If, for example, we have a main function `fn main() {}`, LLVM
|
||||
// will optimize out calls to `__morestack` entirely because the function
|
||||
// doesn't need any stack at all!
|
||||
//
|
||||
// To get around this snag, we specially tell the linker to always include
|
||||
// all contents of this library. This way we're guaranteed that the linker
|
||||
// will include the __morestack symbol 100% of the time, always resolving
|
||||
// references to it even if the object above didn't use it.
|
||||
if t.options.morestack {
|
||||
if t.options.is_like_osx {
|
||||
let morestack = lib_path.join("libmorestack.a");
|
||||
|
||||
let mut v = b"-Wl,-force_load,".to_vec();
|
||||
v.push_all(morestack.as_vec());
|
||||
cmd.arg(&v[..]);
|
||||
} else {
|
||||
cmd.args(&["-Wl,--whole-archive", "-lmorestack", "-Wl,--no-whole-archive"]);
|
||||
}
|
||||
}
|
||||
|
||||
// When linking a dynamic library, we put the metadata into a section of the
|
||||
// executable. This metadata is in a separate object file from the main
|
||||
// object file, so we link that in here.
|
||||
if dylib {
|
||||
cmd.arg(obj_filename.with_extension("metadata.o"));
|
||||
}
|
||||
|
||||
if t.options.is_like_osx {
|
||||
// The dead_strip option to the linker specifies that functions and data
|
||||
// unreachable by the entry point will be removed. This is quite useful
|
||||
// with Rust's compilation model of compiling libraries at a time into
|
||||
// one object file. For example, this brings hello world from 1.7MB to
|
||||
// 458K.
|
||||
//
|
||||
// Note that this is done for both executables and dynamic libraries. We
|
||||
// won't get much benefit from dylibs because LLVM will have already
|
||||
// stripped away as much as it could. This has not been seen to impact
|
||||
// link times negatively.
|
||||
//
|
||||
// -dead_strip can't be part of the pre_link_args because it's also used for partial
|
||||
// linking when using multiple codegen units (-r). So we insert it here.
|
||||
cmd.arg("-Wl,-dead_strip");
|
||||
}
|
||||
|
||||
// If we're building a dylib, we don't use --gc-sections because LLVM has
|
||||
// already done the best it can do, and we also don't want to eliminate the
|
||||
// metadata. If we're building an executable, however, --gc-sections drops
|
||||
// the size of hello world from 1.8MB to 597K, a 67% reduction.
|
||||
if !dylib && !t.options.is_like_osx {
|
||||
cmd.arg("-Wl,--gc-sections");
|
||||
}
|
||||
|
||||
let used_link_args = sess.cstore.get_used_link_args().borrow();
|
||||
|
||||
if t.options.position_independent_executables {
|
||||
let empty_vec = Vec::new();
|
||||
let empty_str = String::new();
|
||||
let args = sess.opts.cg.link_args.as_ref().unwrap_or(&empty_vec);
|
||||
let mut args = args.iter().chain(used_link_args.iter());
|
||||
if !dylib
|
||||
&& (t.options.relocation_model == "pic"
|
||||
|| *sess.opts.cg.relocation_model.as_ref()
|
||||
.unwrap_or(&empty_str) == "pic")
|
||||
&& !args.any(|x| *x == "-static") {
|
||||
cmd.arg("-pie");
|
||||
}
|
||||
}
|
||||
|
||||
if t.options.linker_is_gnu {
|
||||
// GNU-style linkers support optimization with -O. GNU ld doesn't need a
|
||||
// numeric argument, but other linkers do.
|
||||
if sess.opts.optimize == config::Default ||
|
||||
sess.opts.optimize == config::Aggressive {
|
||||
cmd.arg("-Wl,-O1");
|
||||
}
|
||||
}
|
||||
|
||||
// We want to prevent the compiler from accidentally leaking in any system
|
||||
// libraries, so we explicitly ask gcc to not link to any libraries by
|
||||
// default. Note that this does not happen for windows because windows pulls
|
||||
// in some large number of libraries and I couldn't quite figure out which
|
||||
// subset we wanted.
|
||||
if !t.options.is_like_windows {
|
||||
cmd.arg("-nodefaultlibs");
|
||||
}
|
||||
|
||||
// Mark all dynamic libraries and executables as compatible with ASLR
|
||||
// FIXME #17098: ASLR breaks gdb
|
||||
if t.options.is_like_windows && sess.opts.debuginfo == NoDebugInfo {
|
||||
// cmd.arg("-Wl,--dynamicbase");
|
||||
}
|
||||
|
||||
// Take careful note of the ordering of the arguments we pass to the linker
|
||||
// here. Linkers will assume that things on the left depend on things to the
|
||||
// right. Things on the right cannot depend on things on the left. This is
|
||||
// all formally implemented in terms of resolving symbols (libs on the right
|
||||
// resolve unknown symbols of libs on the left, but not vice versa).
|
||||
//
|
||||
// For this reason, we have organized the arguments we pass to the linker as
|
||||
// such:
|
||||
//
|
||||
// 1. The local object that LLVM just generated
|
||||
// 2. Upstream rust libraries
|
||||
// 3. Local native libraries
|
||||
// 4. Upstream native libraries
|
||||
//
|
||||
// This is generally fairly natural, but some may expect 2 and 3 to be
|
||||
// swapped. The reason that all native libraries are put last is that it's
|
||||
// not recommended for a native library to depend on a symbol from a rust
|
||||
// crate. If this is the case then a staticlib crate is recommended, solving
|
||||
// the problem.
|
||||
//
|
||||
// Additionally, it is occasionally the case that upstream rust libraries
|
||||
// depend on a local native library. In the case of libraries such as
|
||||
// lua/glfw/etc the name of the library isn't the same across all platforms,
|
||||
// so only the consumer crate of a library knows the actual name. This means
|
||||
// that downstream crates will provide the #[link] attribute which upstream
|
||||
// crates will depend on. Hence local native libraries are after out
|
||||
// upstream rust crates.
|
||||
//
|
||||
// In theory this means that a symbol in an upstream native library will be
|
||||
// shadowed by a local native library when it wouldn't have been before, but
|
||||
// this kind of behavior is pretty platform specific and generally not
|
||||
// recommended anyway, so I don't think we're shooting ourself in the foot
|
||||
// much with that.
|
||||
add_upstream_rust_crates(cmd, sess, dylib, tmpdir, trans);
|
||||
add_local_native_libraries(cmd, sess);
|
||||
add_upstream_native_libraries(cmd, sess);
|
||||
|
||||
// # Telling the linker what we're doing
|
||||
|
||||
if dylib {
|
||||
// On mac we need to tell the linker to let this library be rpathed
|
||||
if sess.target.target.options.is_like_osx {
|
||||
cmd.args(&["-dynamiclib", "-Wl,-dylib"]);
|
||||
|
||||
if sess.opts.cg.rpath {
|
||||
let mut v = "-Wl,-install_name,@rpath/".as_bytes().to_vec();
|
||||
v.push_all(out_filename.filename().unwrap());
|
||||
cmd.arg(&v[..]);
|
||||
}
|
||||
} else {
|
||||
cmd.arg("-shared");
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME (#2397): At some point we want to rpath our guesses as to
|
||||
// where extern libraries might live, based on the
|
||||
// addl_lib_search_paths
|
||||
if sess.opts.cg.rpath {
|
||||
let sysroot = sess.sysroot();
|
||||
let target_triple = &sess.opts.target_triple[];
|
||||
let get_install_prefix_lib_path = || {
|
||||
let install_prefix = option_env!("CFG_PREFIX").expect("CFG_PREFIX");
|
||||
let tlib = filesearch::relative_target_lib_path(sysroot, target_triple);
|
||||
let mut path = Path::new(install_prefix);
|
||||
path.push(&tlib);
|
||||
|
||||
path
|
||||
};
|
||||
let rpath_config = RPathConfig {
|
||||
used_crates: sess.cstore.get_used_crates(cstore::RequireDynamic),
|
||||
out_filename: out_filename.clone(),
|
||||
has_rpath: sess.target.target.options.has_rpath,
|
||||
is_like_osx: sess.target.target.options.is_like_osx,
|
||||
get_install_prefix_lib_path: get_install_prefix_lib_path,
|
||||
realpath: ::util::fs::realpath
|
||||
};
|
||||
cmd.args(&rpath::get_rpath_flags(rpath_config)[]);
|
||||
}
|
||||
|
||||
// Finally add all the linker arguments provided on the command line along
|
||||
// with any #[link_args] attributes found inside the crate
|
||||
let empty = Vec::new();
|
||||
cmd.args(&sess.opts.cg.link_args.as_ref().unwrap_or(&empty)[]);
|
||||
cmd.args(&used_link_args[..]);
|
||||
}
|
||||
|
||||
// # Native library linking
|
||||
//
|
||||
// User-supplied library search paths (-L on the command line). These are
|
||||
// the same paths used to find Rust crates, so some of them may have been
|
||||
// added already by the previous crate linking code. This only allows them
|
||||
// to be found at compile time so it is still entirely up to outside
|
||||
// forces to make sure that library can be found at runtime.
|
||||
//
|
||||
// Also note that the native libraries linked here are only the ones located
|
||||
// in the current crate. Upstream crates with native library dependencies
|
||||
// may have their native library pulled in above.
|
||||
fn add_local_native_libraries(cmd: &mut Command, sess: &Session) {
|
||||
sess.target_filesearch(PathKind::All).for_each_lib_search_path(|path, k| {
|
||||
match k {
|
||||
PathKind::Framework => { cmd.arg("-F").arg(path); }
|
||||
_ => { cmd.arg("-L").arg(path); }
|
||||
}
|
||||
FileDoesntMatch
|
||||
});
|
||||
|
||||
// Some platforms take hints about whether a library is static or dynamic.
|
||||
// For those that support this, we ensure we pass the option if the library
|
||||
// was flagged "static" (most defaults are dynamic) to ensure that if
|
||||
// libfoo.a and libfoo.so both exist that the right one is chosen.
|
||||
let takes_hints = !sess.target.target.options.is_like_osx;
|
||||
|
||||
let libs = sess.cstore.get_used_libraries();
|
||||
let libs = libs.borrow();
|
||||
|
||||
let staticlibs = libs.iter().filter_map(|&(ref l, kind)| {
|
||||
if kind == cstore::NativeStatic {Some(l)} else {None}
|
||||
});
|
||||
let others = libs.iter().filter(|&&(_, kind)| {
|
||||
kind != cstore::NativeStatic
|
||||
});
|
||||
|
||||
// Platforms that take hints generally also support the --whole-archive
|
||||
// flag. We need to pass this flag when linking static native libraries to
|
||||
// ensure the entire library is included.
|
||||
//
|
||||
// For more details see #15460, but the gist is that the linker will strip
|
||||
// away any unused objects in the archive if we don't otherwise explicitly
|
||||
// reference them. This can occur for libraries which are just providing
|
||||
// bindings, libraries with generic functions, etc.
|
||||
if takes_hints {
|
||||
cmd.arg("-Wl,--whole-archive").arg("-Wl,-Bstatic");
|
||||
}
|
||||
let search_path = super::link::archive_search_paths(sess);
|
||||
for l in staticlibs {
|
||||
if takes_hints {
|
||||
cmd.arg(format!("-l{}", l));
|
||||
} else {
|
||||
// -force_load is the OSX equivalent of --whole-archive, but it
|
||||
// involves passing the full path to the library to link.
|
||||
let lib = archive::find_library(&l[..],
|
||||
&sess.target.target.options.staticlib_prefix,
|
||||
&sess.target.target.options.staticlib_suffix,
|
||||
&search_path[..],
|
||||
&sess.diagnostic().handler);
|
||||
let mut v = b"-Wl,-force_load,".to_vec();
|
||||
v.push_all(lib.as_vec());
|
||||
cmd.arg(&v[..]);
|
||||
}
|
||||
}
|
||||
if takes_hints {
|
||||
cmd.arg("-Wl,--no-whole-archive").arg("-Wl,-Bdynamic");
|
||||
}
|
||||
|
||||
for &(ref l, kind) in others {
|
||||
match kind {
|
||||
cstore::NativeUnknown => {
|
||||
cmd.arg(format!("-l{}", l));
|
||||
}
|
||||
cstore::NativeFramework => {
|
||||
cmd.arg("-framework").arg(&l[..]);
|
||||
}
|
||||
cstore::NativeStatic => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// # Rust Crate linking
|
||||
//
|
||||
// Rust crates are not considered at all when creating an rlib output. All
|
||||
// dependencies will be linked when producing the final output (instead of
|
||||
// the intermediate rlib version)
|
||||
fn add_upstream_rust_crates(cmd: &mut Command, sess: &Session,
|
||||
dylib: bool, tmpdir: &Path,
|
||||
trans: &CrateTranslation) {
|
||||
// All of the heavy lifting has previously been accomplished by the
|
||||
// dependency_format module of the compiler. This is just crawling the
|
||||
// output of that module, adding crates as necessary.
|
||||
//
|
||||
// Linking to a rlib involves just passing it to the linker (the linker
|
||||
// will slurp up the object files inside), and linking to a dynamic library
|
||||
// involves just passing the right -l flag.
|
||||
|
||||
let data = if dylib {
|
||||
&trans.crate_formats[config::CrateTypeDylib]
|
||||
} else {
|
||||
&trans.crate_formats[config::CrateTypeExecutable]
|
||||
};
|
||||
|
||||
// Invoke get_used_crates to ensure that we get a topological sorting of
|
||||
// crates.
|
||||
let deps = sess.cstore.get_used_crates(cstore::RequireDynamic);
|
||||
|
||||
for &(cnum, _) in &deps {
|
||||
// We may not pass all crates through to the linker. Some crates may
|
||||
// appear statically in an existing dylib, meaning we'll pick up all the
|
||||
// symbols from the dylib.
|
||||
let kind = match data[cnum as uint - 1] {
|
||||
Some(t) => t,
|
||||
None => continue
|
||||
};
|
||||
let src = sess.cstore.get_used_crate_source(cnum).unwrap();
|
||||
match kind {
|
||||
cstore::RequireDynamic => {
|
||||
add_dynamic_crate(cmd, sess, src.dylib.unwrap().0)
|
||||
}
|
||||
cstore::RequireStatic => {
|
||||
add_static_crate(cmd, sess, tmpdir, src.rlib.unwrap().0)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Converts a library file-stem into a cc -l argument
|
||||
fn unlib<'a>(config: &config::Config, stem: &'a [u8]) -> &'a [u8] {
|
||||
if stem.starts_with("lib".as_bytes()) && !config.target.options.is_like_windows {
|
||||
&stem[3..]
|
||||
} else {
|
||||
stem
|
||||
}
|
||||
}
|
||||
|
||||
// Adds the static "rlib" versions of all crates to the command line.
|
||||
fn add_static_crate(cmd: &mut Command, sess: &Session, tmpdir: &Path,
|
||||
cratepath: Path) {
|
||||
// When performing LTO on an executable output, all of the
|
||||
// bytecode from the upstream libraries has already been
|
||||
// included in our object file output. We need to modify all of
|
||||
// the upstream archives to remove their corresponding object
|
||||
// file to make sure we don't pull the same code in twice.
|
||||
//
|
||||
// We must continue to link to the upstream archives to be sure
|
||||
// to pull in native static dependencies. As the final caveat,
|
||||
// on Linux it is apparently illegal to link to a blank archive,
|
||||
// so if an archive no longer has any object files in it after
|
||||
// we remove `lib.o`, then don't link against it at all.
|
||||
//
|
||||
// If we're not doing LTO, then our job is simply to just link
|
||||
// against the archive.
|
||||
if sess.lto() {
|
||||
let name = cratepath.filename_str().unwrap();
|
||||
let name = &name[3..name.len() - 5]; // chop off lib/.rlib
|
||||
time(sess.time_passes(),
|
||||
&format!("altering {}.rlib", name)[],
|
||||
(), |()| {
|
||||
let dst = tmpdir.join(cratepath.filename().unwrap());
|
||||
match fs::copy(&cratepath, &dst) {
|
||||
Ok(..) => {}
|
||||
Err(e) => {
|
||||
sess.err(&format!("failed to copy {} to {}: {}",
|
||||
cratepath.display(),
|
||||
dst.display(),
|
||||
e)[]);
|
||||
sess.abort_if_errors();
|
||||
}
|
||||
}
|
||||
// Fix up permissions of the copy, as fs::copy() preserves
|
||||
// permissions, but the original file may have been installed
|
||||
// by a package manager and may be read-only.
|
||||
match fs::chmod(&dst, old_io::USER_READ | old_io::USER_WRITE) {
|
||||
Ok(..) => {}
|
||||
Err(e) => {
|
||||
sess.err(&format!("failed to chmod {} when preparing \
|
||||
for LTO: {}", dst.display(),
|
||||
e)[]);
|
||||
sess.abort_if_errors();
|
||||
}
|
||||
}
|
||||
let handler = &sess.diagnostic().handler;
|
||||
let config = ArchiveConfig {
|
||||
handler: handler,
|
||||
dst: dst.clone(),
|
||||
lib_search_paths: super::link::archive_search_paths(sess),
|
||||
slib_prefix: sess.target.target.options.staticlib_prefix.clone(),
|
||||
slib_suffix: sess.target.target.options.staticlib_suffix.clone(),
|
||||
maybe_ar_prog: sess.opts.cg.ar.clone()
|
||||
};
|
||||
let mut archive = Archive::open(config);
|
||||
archive.remove_file(&format!("{}.o", name)[]);
|
||||
let files = archive.files();
|
||||
if files.iter().any(|s| s[].ends_with(".o")) {
|
||||
cmd.arg(dst);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
cmd.arg(cratepath);
|
||||
}
|
||||
}
|
||||
|
||||
// Same thing as above, but for dynamic crates instead of static crates.
|
||||
fn add_dynamic_crate(cmd: &mut Command, sess: &Session, cratepath: Path) {
|
||||
// If we're performing LTO, then it should have been previously required
|
||||
// that all upstream rust dependencies were available in an rlib format.
|
||||
assert!(!sess.lto());
|
||||
|
||||
// Just need to tell the linker about where the library lives and
|
||||
// what its name is
|
||||
let dir = cratepath.dirname();
|
||||
if !dir.is_empty() { cmd.arg("-L").arg(dir); }
|
||||
|
||||
let mut v = "-l".as_bytes().to_vec();
|
||||
v.push_all(unlib(&sess.target, cratepath.filestem().unwrap()));
|
||||
cmd.arg(&v[..]);
|
||||
}
|
||||
}
|
||||
|
||||
// Link in all of our upstream crates' native dependencies. Remember that
|
||||
// all of these upstream native dependencies are all non-static
|
||||
// dependencies. We've got two cases then:
|
||||
//
|
||||
// 1. The upstream crate is an rlib. In this case we *must* link in the
|
||||
// native dependency because the rlib is just an archive.
|
||||
//
|
||||
// 2. The upstream crate is a dylib. In order to use the dylib, we have to
|
||||
// have the dependency present on the system somewhere. Thus, we don't
|
||||
// gain a whole lot from not linking in the dynamic dependency to this
|
||||
// crate as well.
|
||||
//
|
||||
// The use case for this is a little subtle. In theory the native
|
||||
// dependencies of a crate are purely an implementation detail of the crate
|
||||
// itself, but the problem arises with generic and inlined functions. If a
|
||||
// generic function calls a native function, then the generic function must
|
||||
// be instantiated in the target crate, meaning that the native symbol must
|
||||
// also be resolved in the target crate.
|
||||
fn add_upstream_native_libraries(cmd: &mut Command, sess: &Session) {
|
||||
// Be sure to use a topological sorting of crates because there may be
|
||||
// interdependencies between native libraries. When passing -nodefaultlibs,
|
||||
// for example, almost all native libraries depend on libc, so we have to
|
||||
// make sure that's all the way at the right (liblibc is near the base of
|
||||
// the dependency chain).
|
||||
//
|
||||
// This passes RequireStatic, but the actual requirement doesn't matter,
|
||||
// we're just getting an ordering of crate numbers, we're not worried about
|
||||
// the paths.
|
||||
let crates = sess.cstore.get_used_crates(cstore::RequireStatic);
|
||||
for (cnum, _) in crates {
|
||||
let libs = csearch::get_native_libraries(&sess.cstore, cnum);
|
||||
for &(kind, ref lib) in &libs {
|
||||
match kind {
|
||||
cstore::NativeUnknown => {
|
||||
cmd.arg(format!("-l{}", *lib));
|
||||
}
|
||||
cstore::NativeFramework => {
|
||||
cmd.arg("-framework");
|
||||
cmd.arg(&lib[..]);
|
||||
}
|
||||
cstore::NativeStatic => {
|
||||
sess.bug("statics shouldn't be propagated");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,460 +0,0 @@
|
|||
// Copyright 2012-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.
|
||||
|
||||
use super::archive::{Archive, ArchiveConfig};
|
||||
use super::archive;
|
||||
use super::rpath;
|
||||
use super::rpath::RPathConfig;
|
||||
|
||||
use session::config;
|
||||
use session::config::NoDebugInfo;
|
||||
use session::search_paths::PathKind;
|
||||
use session::Session;
|
||||
use metadata::{cstore, filesearch, csearch};
|
||||
use metadata::filesearch::FileDoesntMatch;
|
||||
use trans::CrateTranslation;
|
||||
use util::common::time;
|
||||
|
||||
use std::str;
|
||||
use std::old_io::{fs, TempDir, Command};
|
||||
use std::old_io;
|
||||
|
||||
// Create a dynamic library or executable
|
||||
//
|
||||
// This will invoke the system linker/cc to create the resulting file. This
|
||||
// links to all upstream files as well.
|
||||
pub fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool,
|
||||
obj_filename: &Path, out_filename: &Path) {
|
||||
let tmpdir = TempDir::new("rustc").ok().expect("needs a temp dir");
|
||||
|
||||
// The invocations of cc share some flags across platforms
|
||||
let pname = super::link::get_cc_prog(sess);
|
||||
let mut cmd = Command::new(&pname[..]);
|
||||
|
||||
cmd.args(&sess.target.target.options.pre_link_args[]);
|
||||
link_args(&mut cmd, sess, dylib, tmpdir.path(),
|
||||
trans, obj_filename, out_filename);
|
||||
cmd.args(&sess.target.target.options.post_link_args[]);
|
||||
if !sess.target.target.options.no_compiler_rt {
|
||||
cmd.arg("msvcrt.lib");
|
||||
cmd.arg("compiler-rt.lib");
|
||||
}
|
||||
|
||||
if sess.opts.debugging_opts.print_link_args {
|
||||
println!("{:?}", &cmd);
|
||||
}
|
||||
|
||||
// May have not found libraries in the right formats.
|
||||
sess.abort_if_errors();
|
||||
|
||||
// Invoke the system linker
|
||||
debug!("{:?}", &cmd);
|
||||
let prog = time(sess.time_passes(), "running linker", (), |()| cmd.output());
|
||||
match prog {
|
||||
Ok(prog) => {
|
||||
if !prog.status.success() {
|
||||
sess.err(&format!("linking with `{}` failed: {}",
|
||||
pname,
|
||||
prog.status)[]);
|
||||
sess.note(&format!("{:?}", &cmd)[]);
|
||||
let mut output = prog.error.clone();
|
||||
output.push_all(&prog.output[]);
|
||||
sess.note(str::from_utf8(&output[..]).unwrap());
|
||||
sess.abort_if_errors();
|
||||
}
|
||||
debug!("linker stderr:\n{}", String::from_utf8(prog.error).unwrap());
|
||||
debug!("linker stdout:\n{}", String::from_utf8(prog.output).unwrap());
|
||||
},
|
||||
Err(e) => {
|
||||
sess.err(&format!("could not exec the linker `{}`: {}",
|
||||
pname,
|
||||
e)[]);
|
||||
sess.abort_if_errors();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// On OSX, debuggers need this utility to get run to do some munging of
|
||||
// the symbols
|
||||
if sess.target.target.options.is_like_osx && sess.opts.debuginfo != NoDebugInfo {
|
||||
match Command::new("dsymutil").arg(out_filename).output() {
|
||||
Ok(..) => {}
|
||||
Err(e) => {
|
||||
sess.err(&format!("failed to run dsymutil: {}", e)[]);
|
||||
sess.abort_if_errors();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn link_args(cmd: &mut Command,
|
||||
sess: &Session,
|
||||
dylib: bool,
|
||||
tmpdir: &Path,
|
||||
trans: &CrateTranslation,
|
||||
obj_filename: &Path,
|
||||
out_filename: &Path) {
|
||||
|
||||
// The default library location, we need this to find the runtime.
|
||||
// The location of crates will be determined as needed.
|
||||
let lib_path = sess.target_filesearch(PathKind::All).get_lib_path();
|
||||
|
||||
// target descriptor
|
||||
let t = &sess.target.target;
|
||||
|
||||
lib_path.as_str().map(|lp| cmd.arg(format!("/LIBPATH:{}", lp)));
|
||||
out_filename.as_str().map(|out| cmd.arg(format!("/OUT:{}", out)));
|
||||
|
||||
cmd.arg(obj_filename);
|
||||
|
||||
// Stack growth requires statically linking a __morestack function. Note
|
||||
// that this is listed *before* all other libraries. Due to the usage of the
|
||||
// --as-needed flag below, the standard library may only be useful for its
|
||||
// rust_stack_exhausted function. In this case, we must ensure that the
|
||||
// libmorestack.a file appears *before* the standard library (so we put it
|
||||
// at the very front).
|
||||
//
|
||||
// Most of the time this is sufficient, except for when LLVM gets super
|
||||
// clever. If, for example, we have a main function `fn main() {}`, LLVM
|
||||
// will optimize out calls to `__morestack` entirely because the function
|
||||
// doesn't need any stack at all!
|
||||
//
|
||||
// To get around this snag, we specially tell the linker to always include
|
||||
// all contents of this library. This way we're guaranteed that the linker
|
||||
// will include the __morestack symbol 100% of the time, always resolving
|
||||
// references to it even if the object above didn't use it.
|
||||
if t.options.morestack {
|
||||
cmd.arg("morestack.lib");
|
||||
}
|
||||
|
||||
// When linking a dynamic library, we put the metadata into a section of the
|
||||
// executable. This metadata is in a separate object file from the main
|
||||
// object file, so we link that in here.
|
||||
if dylib {
|
||||
cmd.arg(obj_filename.with_extension("metadata.o"));
|
||||
}
|
||||
|
||||
let used_link_args = sess.cstore.get_used_link_args().borrow();
|
||||
|
||||
// We want to prevent the compiler from accidentally leaking in any system
|
||||
// libraries, so we explicitly ask gcc to not link to any libraries by
|
||||
// default. Note that this does not happen for windows because windows pulls
|
||||
// in some large number of libraries and I couldn't quite figure out which
|
||||
// subset we wanted.
|
||||
|
||||
// We have to keep this in for now - since we need to link to the MSVCRT for
|
||||
// things such as jemalloc.
|
||||
//cmd.arg("/nodefaultlib");
|
||||
|
||||
// Take careful note of the ordering of the arguments we pass to the linker
|
||||
// here. Linkers will assume that things on the left depend on things to the
|
||||
// right. Things on the right cannot depend on things on the left. This is
|
||||
// all formally implemented in terms of resolving symbols (libs on the right
|
||||
// resolve unknown symbols of libs on the left, but not vice versa).
|
||||
//
|
||||
// For this reason, we have organized the arguments we pass to the linker as
|
||||
// such:
|
||||
//
|
||||
// 1. The local object that LLVM just generated
|
||||
// 2. Upstream rust libraries
|
||||
// 3. Local native libraries
|
||||
// 4. Upstream native libraries
|
||||
//
|
||||
// This is generally fairly natural, but some may expect 2 and 3 to be
|
||||
// swapped. The reason that all native libraries are put last is that it's
|
||||
// not recommended for a native library to depend on a symbol from a rust
|
||||
// crate. If this is the case then a staticlib crate is recommended, solving
|
||||
// the problem.
|
||||
//
|
||||
// Additionally, it is occasionally the case that upstream rust libraries
|
||||
// depend on a local native library. In the case of libraries such as
|
||||
// lua/glfw/etc the name of the library isn't the same across all platforms,
|
||||
// so only the consumer crate of a library knows the actual name. This means
|
||||
// that downstream crates will provide the #[link] attribute which upstream
|
||||
// crates will depend on. Hence local native libraries are after out
|
||||
// upstream rust crates.
|
||||
//
|
||||
// In theory this means that a symbol in an upstream native library will be
|
||||
// shadowed by a local native library when it wouldn't have been before, but
|
||||
// this kind of behavior is pretty platform specific and generally not
|
||||
// recommended anyway, so I don't think we're shooting ourself in the foot
|
||||
// much with that.
|
||||
add_upstream_rust_crates(cmd, sess, dylib, tmpdir, trans);
|
||||
add_local_native_libraries(cmd, sess);
|
||||
add_upstream_native_libraries(cmd, sess);
|
||||
|
||||
// # Telling the linker what we're doing
|
||||
|
||||
if dylib {
|
||||
cmd.arg("/DLL");
|
||||
}
|
||||
|
||||
// FIXME (#2397): At some point we want to rpath our guesses as to
|
||||
// where extern libraries might live, based on the
|
||||
// addl_lib_search_paths
|
||||
if sess.opts.cg.rpath {
|
||||
let sysroot = sess.sysroot();
|
||||
let target_triple = &sess.opts.target_triple[];
|
||||
let get_install_prefix_lib_path = || {
|
||||
let install_prefix = option_env!("CFG_PREFIX").expect("CFG_PREFIX");
|
||||
let tlib = filesearch::relative_target_lib_path(sysroot, target_triple);
|
||||
let mut path = Path::new(install_prefix);
|
||||
path.push(&tlib);
|
||||
|
||||
path
|
||||
};
|
||||
let rpath_config = RPathConfig {
|
||||
used_crates: sess.cstore.get_used_crates(cstore::RequireDynamic),
|
||||
out_filename: out_filename.clone(),
|
||||
has_rpath: sess.target.target.options.has_rpath,
|
||||
is_like_osx: sess.target.target.options.is_like_osx,
|
||||
get_install_prefix_lib_path: get_install_prefix_lib_path,
|
||||
realpath: ::util::fs::realpath
|
||||
};
|
||||
cmd.args(&rpath::get_rpath_flags(rpath_config)[]);
|
||||
}
|
||||
|
||||
// Finally add all the linker arguments provided on the command line along
|
||||
// with any #[link_args] attributes found inside the crate
|
||||
let empty = Vec::new();
|
||||
cmd.args(&sess.opts.cg.link_args.as_ref().unwrap_or(&empty)[]);
|
||||
cmd.args(&used_link_args[..]);
|
||||
}
|
||||
|
||||
// # Native library linking
|
||||
//
|
||||
// User-supplied library search paths (-L on the command line). These are
|
||||
// the same paths used to find Rust crates, so some of them may have been
|
||||
// added already by the previous crate linking code. This only allows them
|
||||
// to be found at compile time so it is still entirely up to outside
|
||||
// forces to make sure that library can be found at runtime.
|
||||
//
|
||||
// Also note that the native libraries linked here are only the ones located
|
||||
// in the current crate. Upstream crates with native library dependencies
|
||||
// may have their native library pulled in above.
|
||||
fn add_local_native_libraries(cmd: &mut Command, sess: &Session) {
|
||||
sess.target_filesearch(PathKind::All).for_each_lib_search_path(|path, _k| {
|
||||
path.as_str().map(|s| cmd.arg(format!("/LIBPATH:{}", s)));
|
||||
FileDoesntMatch
|
||||
});
|
||||
|
||||
let libs = sess.cstore.get_used_libraries();
|
||||
let libs = libs.borrow();
|
||||
|
||||
let staticlibs = libs.iter().filter_map(|&(ref l, kind)| {
|
||||
if kind == cstore::NativeStatic {Some(l)} else {None}
|
||||
});
|
||||
let others = libs.iter().filter(|&&(_, kind)| {
|
||||
kind != cstore::NativeStatic
|
||||
});
|
||||
|
||||
let search_path = super::link::archive_search_paths(sess);
|
||||
for l in staticlibs {
|
||||
let lib = archive::find_library(&l[..],
|
||||
&sess.target.target.options.staticlib_prefix,
|
||||
&sess.target.target.options.staticlib_suffix,
|
||||
&search_path[..],
|
||||
&sess.diagnostic().handler);
|
||||
let mut v = Vec::new();
|
||||
v.push_all(lib.as_vec());
|
||||
cmd.arg(&v[..]);
|
||||
}
|
||||
|
||||
for &(ref l, kind) in others {
|
||||
match kind {
|
||||
cstore::NativeUnknown => {
|
||||
cmd.arg(format!("{}.lib", l));
|
||||
}
|
||||
cstore::NativeFramework => {}
|
||||
cstore::NativeStatic => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// # Rust Crate linking
|
||||
//
|
||||
// Rust crates are not considered at all when creating an rlib output. All
|
||||
// dependencies will be linked when producing the final output (instead of
|
||||
// the intermediate rlib version)
|
||||
fn add_upstream_rust_crates(cmd: &mut Command, sess: &Session,
|
||||
dylib: bool, tmpdir: &Path,
|
||||
trans: &CrateTranslation) {
|
||||
// All of the heavy lifting has previously been accomplished by the
|
||||
// dependency_format module of the compiler. This is just crawling the
|
||||
// output of that module, adding crates as necessary.
|
||||
//
|
||||
// Linking to a rlib involves just passing it to the linker (the linker
|
||||
// will slurp up the object files inside), and linking to a dynamic library
|
||||
// involves just passing the right -l flag.
|
||||
|
||||
let data = if dylib {
|
||||
&trans.crate_formats[config::CrateTypeDylib]
|
||||
} else {
|
||||
&trans.crate_formats[config::CrateTypeExecutable]
|
||||
};
|
||||
|
||||
// Invoke get_used_crates to ensure that we get a topological sorting of
|
||||
// crates.
|
||||
let deps = sess.cstore.get_used_crates(cstore::RequireDynamic);
|
||||
|
||||
for &(cnum, _) in &deps {
|
||||
// We may not pass all crates through to the linker. Some crates may
|
||||
// appear statically in an existing dylib, meaning we'll pick up all the
|
||||
// symbols from the dylib.
|
||||
let kind = match data[cnum as uint - 1] {
|
||||
Some(t) => t,
|
||||
None => continue
|
||||
};
|
||||
let src = sess.cstore.get_used_crate_source(cnum).unwrap();
|
||||
match kind {
|
||||
cstore::RequireDynamic => {
|
||||
add_dynamic_crate(cmd, sess, src.dylib.unwrap().0)
|
||||
}
|
||||
cstore::RequireStatic => {
|
||||
add_static_crate(cmd, sess, tmpdir, src.rlib.unwrap().0)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Converts a library file-stem into a cc -l argument
|
||||
fn unlib<'a>(config: &config::Config, stem: &'a [u8]) -> &'a [u8] {
|
||||
if stem.starts_with("lib".as_bytes()) && !config.target.options.is_like_windows {
|
||||
&stem[3..]
|
||||
} else {
|
||||
stem
|
||||
}
|
||||
}
|
||||
|
||||
// Adds the static "rlib" versions of all crates to the command line.
|
||||
fn add_static_crate(cmd: &mut Command, sess: &Session, tmpdir: &Path,
|
||||
cratepath: Path) {
|
||||
// When performing LTO on an executable output, all of the
|
||||
// bytecode from the upstream libraries has already been
|
||||
// included in our object file output. We need to modify all of
|
||||
// the upstream archives to remove their corresponding object
|
||||
// file to make sure we don't pull the same code in twice.
|
||||
//
|
||||
// We must continue to link to the upstream archives to be sure
|
||||
// to pull in native static dependencies. As the final caveat,
|
||||
// on Linux it is apparently illegal to link to a blank archive,
|
||||
// so if an archive no longer has any object files in it after
|
||||
// we remove `lib.o`, then don't link against it at all.
|
||||
//
|
||||
// If we're not doing LTO, then our job is simply to just link
|
||||
// against the archive.
|
||||
if sess.lto() {
|
||||
let name = cratepath.filename_str().unwrap();
|
||||
let name = &name[3..name.len() - 5]; // chop off lib/.rlib
|
||||
time(sess.time_passes(),
|
||||
&format!("altering {}.rlib", name)[],
|
||||
(), |()| {
|
||||
let dst = tmpdir.join(cratepath.filename().unwrap());
|
||||
match fs::copy(&cratepath, &dst) {
|
||||
Ok(..) => {}
|
||||
Err(e) => {
|
||||
sess.err(&format!("failed to copy {} to {}: {}",
|
||||
cratepath.display(),
|
||||
dst.display(),
|
||||
e)[]);
|
||||
sess.abort_if_errors();
|
||||
}
|
||||
}
|
||||
// Fix up permissions of the copy, as fs::copy() preserves
|
||||
// permissions, but the original file may have been installed
|
||||
// by a package manager and may be read-only.
|
||||
match fs::chmod(&dst, old_io::USER_READ | old_io::USER_WRITE) {
|
||||
Ok(..) => {}
|
||||
Err(e) => {
|
||||
sess.err(&format!("failed to chmod {} when preparing \
|
||||
for LTO: {}", dst.display(),
|
||||
e)[]);
|
||||
sess.abort_if_errors();
|
||||
}
|
||||
}
|
||||
let handler = &sess.diagnostic().handler;
|
||||
let config = ArchiveConfig {
|
||||
handler: handler,
|
||||
dst: dst.clone(),
|
||||
lib_search_paths: super::link::archive_search_paths(sess),
|
||||
slib_prefix: sess.target.target.options.staticlib_prefix.clone(),
|
||||
slib_suffix: sess.target.target.options.staticlib_suffix.clone(),
|
||||
maybe_ar_prog: sess.opts.cg.ar.clone()
|
||||
};
|
||||
let mut archive = Archive::open(config);
|
||||
archive.remove_file(&format!("{}.o", name)[]);
|
||||
let files = archive.files();
|
||||
if files.iter().any(|s| s[].ends_with(".o")) {
|
||||
cmd.arg(dst);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
cmd.arg(cratepath);
|
||||
}
|
||||
}
|
||||
|
||||
// Same thing as above, but for dynamic crates instead of static crates.
|
||||
fn add_dynamic_crate(cmd: &mut Command, sess: &Session, cratepath: Path) {
|
||||
// If we're performing LTO, then it should have been previously required
|
||||
// that all upstream rust dependencies were available in an rlib format.
|
||||
assert!(!sess.lto());
|
||||
|
||||
cratepath.as_str().map(|s| {
|
||||
let libname = s.replace(".dll", ".lib");
|
||||
cmd.arg(&libname[]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Link in all of our upstream crates' native dependencies. Remember that
|
||||
// all of these upstream native dependencies are all non-static
|
||||
// dependencies. We've got two cases then:
|
||||
//
|
||||
// 1. The upstream crate is an rlib. In this case we *must* link in the
|
||||
// native dependency because the rlib is just an archive.
|
||||
//
|
||||
// 2. The upstream crate is a dylib. In order to use the dylib, we have to
|
||||
// have the dependency present on the system somewhere. Thus, we don't
|
||||
// gain a whole lot from not linking in the dynamic dependency to this
|
||||
// crate as well.
|
||||
//
|
||||
// The use case for this is a little subtle. In theory the native
|
||||
// dependencies of a crate are purely an implementation detail of the crate
|
||||
// itself, but the problem arises with generic and inlined functions. If a
|
||||
// generic function calls a native function, then the generic function must
|
||||
// be instantiated in the target crate, meaning that the native symbol must
|
||||
// also be resolved in the target crate.
|
||||
fn add_upstream_native_libraries(cmd: &mut Command, sess: &Session) {
|
||||
// Be sure to use a topological sorting of crates because there may be
|
||||
// interdependencies between native libraries. When passing -nodefaultlibs,
|
||||
// for example, almost all native libraries depend on libc, so we have to
|
||||
// make sure that's all the way at the right (liblibc is near the base of
|
||||
// the dependency chain).
|
||||
//
|
||||
// This passes RequireStatic, but the actual requirement doesn't matter,
|
||||
// we're just getting an ordering of crate numbers, we're not worried about
|
||||
// the paths.
|
||||
let crates = sess.cstore.get_used_crates(cstore::RequireStatic);
|
||||
for (cnum, _) in crates {
|
||||
let libs = csearch::get_native_libraries(&sess.cstore, cnum);
|
||||
for &(kind, ref lib) in &libs {
|
||||
match kind {
|
||||
cstore::NativeUnknown => {
|
||||
cmd.arg(format!("{}.lib", lib));
|
||||
}
|
||||
cstore::NativeFramework => {
|
||||
}
|
||||
cstore::NativeStatic => {
|
||||
sess.bug("statics shouldn't be propagated");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -75,9 +75,6 @@ pub mod back {
|
|||
pub use rustc_back::x86_64;
|
||||
|
||||
pub mod link;
|
||||
mod link_gnu;
|
||||
mod link_msvc;
|
||||
|
||||
pub mod lto;
|
||||
pub mod write;
|
||||
|
||||
|
|
|
@ -1907,16 +1907,6 @@ pub fn update_linkage(ccx: &CrateContext,
|
|||
llval: ValueRef,
|
||||
id: Option<ast::NodeId>,
|
||||
llval_origin: ValueOrigin) {
|
||||
|
||||
// TODO: This should be conditionaly set based on whether we're producing a
|
||||
// dynamic library or not to follow the conventions on Windows. (ricky26)
|
||||
|
||||
if ccx.sess().target.target.options.is_like_msvc {
|
||||
llvm::SetDLLStorageClass(llval, llvm::DLLExportStorageClass);
|
||||
llvm::SetLinkage(llval, llvm::ExternalLinkage);
|
||||
return;
|
||||
}
|
||||
|
||||
match llval_origin {
|
||||
InlinedCopy => {
|
||||
// `llval` is a translation of an item defined in a separate
|
||||
|
@ -2181,7 +2171,7 @@ pub fn create_entry_wrapper(ccx: &CrateContext,
|
|||
// FIXME: #16581: Marking a symbol in the executable with `dllexport`
|
||||
// linkage forces MinGW's linker to output a `.reloc` section for ASLR
|
||||
if ccx.sess().target.target.options.is_like_windows {
|
||||
llvm::SetDLLStorageClass(llfn, llvm::DLLExportStorageClass);
|
||||
unsafe { llvm::LLVMRustSetDLLExportStorageClass(llfn) }
|
||||
}
|
||||
|
||||
let llbb = unsafe {
|
||||
|
|
|
@ -863,14 +863,6 @@ pub fn trans_static(ccx: &CrateContext, m: ast::Mutability, id: ast::NodeId) ->
|
|||
};
|
||||
llvm::LLVMSetInitializer(g, v);
|
||||
|
||||
// TODO: This should be conditionaly set based on whether we're producing a
|
||||
// dynamic library or not to follow the conventions on Windows. (ricky26)
|
||||
|
||||
if ccx.sess().target.target.options.is_like_msvc {
|
||||
llvm::SetDLLStorageClass(g, llvm::DLLExportStorageClass);
|
||||
llvm::SetLinkage(g, llvm::ExternalLinkage);
|
||||
}
|
||||
|
||||
// As an optimization, all shared statics which do not have interior
|
||||
// mutability are placed into read-only memory.
|
||||
if m != ast::MutMutable {
|
||||
|
|
|
@ -31,10 +31,7 @@ pub use core::f32::consts;
|
|||
#[allow(dead_code)]
|
||||
mod cmath {
|
||||
use libc::{c_float, c_int};
|
||||
#[cfg(windows)]
|
||||
use libc::c_double;
|
||||
|
||||
#[link_name = "m"]
|
||||
extern {
|
||||
pub fn acosf(n: c_float) -> c_float;
|
||||
pub fn asinf(n: c_float) -> c_float;
|
||||
|
@ -59,35 +56,28 @@ mod cmath {
|
|||
pub fn tanhf(n: c_float) -> c_float;
|
||||
pub fn tgammaf(n: c_float) -> c_float;
|
||||
|
||||
#[cfg(unix)]
|
||||
#[cfg_attr(all(windows, target_env = "msvc"), link_name = "__lgammaf_r")]
|
||||
pub fn lgammaf_r(n: c_float, sign: &mut c_int) -> c_float;
|
||||
#[cfg(unix)]
|
||||
#[cfg_attr(all(windows, target_env = "msvc"), link_name = "_hypotf")]
|
||||
pub fn hypotf(x: c_float, y: c_float) -> c_float;
|
||||
#[cfg(unix)]
|
||||
|
||||
#[cfg(any(unix, all(windows, not(target_env = "msvc"))))]
|
||||
pub fn frexpf(n: c_float, value: &mut c_int) -> c_float;
|
||||
#[cfg(unix)]
|
||||
#[cfg(any(unix, all(windows, not(target_env = "msvc"))))]
|
||||
pub fn ldexpf(x: c_float, n: c_int) -> c_float;
|
||||
|
||||
#[cfg(windows)]
|
||||
#[link_name="__lgammaf_r"]
|
||||
pub fn lgammaf_r(n: c_float, sign: &mut c_int) -> c_float;
|
||||
|
||||
#[cfg(windows)]
|
||||
#[link_name="_hypotf"]
|
||||
pub fn hypotf(x: c_float, y: c_float) -> c_float;
|
||||
|
||||
#[cfg(windows)]
|
||||
fn frexp(n: c_double, value: &mut c_int) -> c_double;
|
||||
|
||||
#[cfg(windows)]
|
||||
fn ldexp(x: c_double, n: c_int) -> c_double;
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub unsafe fn ldexpf(x: c_float, n: c_int) -> c_float { return ldexp(x as c_double, n) as c_float; }
|
||||
#[cfg(all(windows, target_env = "msvc"))]
|
||||
pub unsafe fn ldexpf(x: c_float, n: c_int) -> c_float {
|
||||
f64::ldexp(x as f64, n as isize) as c_float
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
pub unsafe fn frexpf(x: c_float, value: &mut c_int) -> c_float { return frexp(x as c_double, value) as c_float; }
|
||||
#[cfg(all(windows, target_env = "msvc"))]
|
||||
pub unsafe fn frexpf(x: c_float, value: &mut c_int) -> c_float {
|
||||
let (a, b) = f64::frexp(x as f64);
|
||||
*value = b as c_int;
|
||||
a as c_float
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
|
|
|
@ -68,17 +68,10 @@ mod cmath {
|
|||
pub fn y1(n: c_double) -> c_double;
|
||||
pub fn yn(i: c_int, n: c_double) -> c_double;
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn lgamma_r(n: c_double, sign: &mut c_int) -> c_double;
|
||||
#[cfg(windows)]
|
||||
#[link_name="__lgamma_r"]
|
||||
#[cfg_attr(all(windows, target_env = "msvc"), link_name = "__lgamma_r")]
|
||||
pub fn lgamma_r(n: c_double, sign: &mut c_int) -> c_double;
|
||||
|
||||
#[cfg(unix)]
|
||||
pub fn hypot(x: c_double, y: c_double) -> c_double;
|
||||
|
||||
#[cfg(windows)]
|
||||
#[link_name="_hypot"]
|
||||
#[cfg_attr(all(windows, target_env = "msvc"), link_name = "_hypot")]
|
||||
pub fn hypot(x: c_double, y: c_double) -> c_double;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,17 +39,10 @@ mod macros;
|
|||
|
||||
// These should be refactored/moved/made private over time
|
||||
pub mod util;
|
||||
pub mod unwind;
|
||||
pub mod args;
|
||||
|
||||
#[cfg(not(all(target_os = "windows", target_abi = "msvc")))]
|
||||
pub mod unwind;
|
||||
#[cfg(all(target_os = "windows", target_abi = "msvc"))]
|
||||
#[path = "unwind_msvc.rs"]
|
||||
pub mod unwind;
|
||||
|
||||
mod at_exit_imp;
|
||||
|
||||
#[cfg(not(all(target_os = "windows", target_abi = "msvc")))]
|
||||
mod libunwind;
|
||||
|
||||
/// The default error code of the rust runtime if the main thread panics instead
|
||||
|
|
|
@ -1,302 +0,0 @@
|
|||
// Copyright 2013 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.
|
||||
|
||||
//! Implementation of Rust stack unwinding
|
||||
//!
|
||||
//! For background on exception handling and stack unwinding please see
|
||||
//! "Exception Handling in LLVM" (llvm.org/docs/ExceptionHandling.html) and
|
||||
//! documents linked from it.
|
||||
//! These are also good reads:
|
||||
//! http://theofilos.cs.columbia.edu/blog/2013/09/22/base_abi/
|
||||
//! http://monoinfinito.wordpress.com/series/exception-handling-in-c/
|
||||
//! http://www.airs.com/blog/index.php?s=exception+frames
|
||||
//!
|
||||
//! ## A brief summary
|
||||
//!
|
||||
//! Exception handling happens in two phases: a search phase and a cleanup phase.
|
||||
//!
|
||||
//! In both phases the unwinder walks stack frames from top to bottom using
|
||||
//! information from the stack frame unwind sections of the current process's
|
||||
//! modules ("module" here refers to an OS module, i.e. an executable or a
|
||||
//! dynamic library).
|
||||
//!
|
||||
//! For each stack frame, it invokes the associated "personality routine", whose
|
||||
//! address is also stored in the unwind info section.
|
||||
//!
|
||||
//! In the search phase, the job of a personality routine is to examine exception
|
||||
//! 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.
|
||||
//!
|
||||
//! ## 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.
|
||||
//!
|
||||
//! 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.
|
||||
|
||||
use prelude::v1::*;
|
||||
|
||||
use any::Any;
|
||||
use cell::Cell;
|
||||
use cmp;
|
||||
use panicking;
|
||||
use fmt;
|
||||
use intrinsics;
|
||||
use mem;
|
||||
use sync::atomic::{self, Ordering};
|
||||
use sync::{Once, ONCE_INIT};
|
||||
|
||||
pub type Callback = fn(msg: &(Any + Send), file: &'static str, line: uint);
|
||||
|
||||
// Variables used for invoking callbacks when a thread starts to unwind.
|
||||
//
|
||||
// For more information, see below.
|
||||
const MAX_CALLBACKS: uint = 16;
|
||||
static CALLBACKS: [atomic::AtomicUsize; MAX_CALLBACKS] =
|
||||
[atomic::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT,
|
||||
atomic::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT,
|
||||
atomic::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT,
|
||||
atomic::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT,
|
||||
atomic::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT,
|
||||
atomic::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT,
|
||||
atomic::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT,
|
||||
atomic::ATOMIC_USIZE_INIT, atomic::ATOMIC_USIZE_INIT];
|
||||
static CALLBACK_CNT: atomic::AtomicUsize = atomic::ATOMIC_USIZE_INIT;
|
||||
|
||||
thread_local! { static PANICKING: Cell<bool> = Cell::new(false) }
|
||||
|
||||
/// Invoke a closure, capturing the cause of panic if one occurs.
|
||||
///
|
||||
/// This function will return `Ok(())` if the closure did not panic, and will
|
||||
/// return `Err(cause)` if the closure panics. The `cause` returned is the
|
||||
/// object with which panic was originally invoked.
|
||||
///
|
||||
/// This function also is unsafe for a variety of reasons:
|
||||
///
|
||||
/// * This is not safe to call in a nested fashion. The unwinding
|
||||
/// interface for Rust is designed to have at most one try/catch block per
|
||||
/// thread, not multiple. No runtime checking is currently performed to uphold
|
||||
/// this invariant, so this function is not safe. A nested try/catch block
|
||||
/// may result in corruption of the outer try/catch block's state, especially
|
||||
/// if this is used within a thread itself.
|
||||
///
|
||||
/// * It is not sound to trigger unwinding while already unwinding. Rust threads
|
||||
/// have runtime checks in place to ensure this invariant, but it is not
|
||||
/// guaranteed that a rust thread is in place when invoking this function.
|
||||
/// Unwinding twice can lead to resource leaks where some destructors are not
|
||||
/// run.
|
||||
pub unsafe fn try<F: FnOnce()>(f: F) -> Result<(), Box<Any + Send>> {
|
||||
f();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Determines whether the current thread is unwinding because of panic.
|
||||
pub fn panicking() -> bool {
|
||||
PANICKING.with(|s| s.get())
|
||||
}
|
||||
|
||||
// An uninlined, unmangled function upon which to slap yer breakpoints
|
||||
#[inline(never)]
|
||||
#[no_mangle]
|
||||
#[allow(private_no_mangle_fns)]
|
||||
fn rust_panic(_cause: Box<Any + Send + 'static>) -> ! {
|
||||
loop {}
|
||||
}
|
||||
|
||||
// See also: rt/rust_try.ll
|
||||
#[cfg(all(not(test)))]
|
||||
#[doc(hidden)]
|
||||
#[allow(non_camel_case_types, non_snake_case)]
|
||||
pub mod eabi {
|
||||
pub use self::EXCEPTION_DISPOSITION::*;
|
||||
use libc::c_void;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct EXCEPTION_RECORD;
|
||||
#[repr(C)]
|
||||
pub struct CONTEXT;
|
||||
#[repr(C)]
|
||||
pub struct DISPATCHER_CONTEXT;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy)]
|
||||
pub enum EXCEPTION_DISPOSITION {
|
||||
ExceptionContinueExecution,
|
||||
ExceptionContinueSearch,
|
||||
ExceptionNestedException,
|
||||
ExceptionCollidedUnwind
|
||||
}
|
||||
|
||||
#[lang="eh_personality"]
|
||||
#[no_mangle] // referenced from rust_try.ll
|
||||
#[allow(private_no_mangle_fns)]
|
||||
extern "C" fn rust_eh_personality(
|
||||
_exceptionRecord: *mut EXCEPTION_RECORD,
|
||||
_establisherFrame: *mut c_void,
|
||||
_contextRecord: *mut CONTEXT,
|
||||
_dispatcherContext: *mut DISPATCHER_CONTEXT
|
||||
) -> EXCEPTION_DISPOSITION
|
||||
{
|
||||
EXCEPTION_DISPOSITION::ExceptionContinueSearch
|
||||
}
|
||||
|
||||
#[no_mangle] // referenced from rust_try.ll
|
||||
pub extern "C" fn rust_eh_personality_catch(
|
||||
_exceptionRecord: *mut EXCEPTION_RECORD,
|
||||
_establisherFrame: *mut c_void,
|
||||
_contextRecord: *mut CONTEXT,
|
||||
_dispatcherContext: *mut DISPATCHER_CONTEXT
|
||||
) -> EXCEPTION_DISPOSITION
|
||||
{
|
||||
EXCEPTION_DISPOSITION::ExceptionContinueSearch
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
/// Entry point of panic from the libcore crate.
|
||||
#[lang = "panic_fmt"]
|
||||
pub extern fn rust_begin_unwind(msg: fmt::Arguments,
|
||||
file: &'static str, line: uint) -> ! {
|
||||
begin_unwind_fmt(msg, &(file, line))
|
||||
}
|
||||
|
||||
/// The entry point for unwinding with a formatted message.
|
||||
///
|
||||
/// This is designed to reduce the amount of code required at the call
|
||||
/// site as much as possible (so that `panic!()` has as low an impact
|
||||
/// on (e.g.) the inlining of other functions as possible), by moving
|
||||
/// the actual formatting into this shared place.
|
||||
#[inline(never)] #[cold]
|
||||
#[stable(since = "1.0.0", feature = "rust1")]
|
||||
pub fn begin_unwind_fmt(msg: fmt::Arguments, file_line: &(&'static str, uint)) -> ! {
|
||||
use fmt::Write;
|
||||
|
||||
// We do two allocations here, unfortunately. But (a) they're
|
||||
// required with the current scheme, and (b) we don't handle
|
||||
// panic + OOM properly anyway (see comment in begin_unwind
|
||||
// below).
|
||||
|
||||
let mut s = String::new();
|
||||
let _ = write!(&mut s, "{}", msg);
|
||||
begin_unwind_inner(box s, file_line)
|
||||
}
|
||||
|
||||
/// This is the entry point of unwinding for panic!() and assert!().
|
||||
#[inline(never)] #[cold] // avoid code bloat at the call sites as much as possible
|
||||
#[stable(since = "1.0.0", feature = "rust1")]
|
||||
pub fn begin_unwind<M: Any + Send>(msg: M, file_line: &(&'static str, uint)) -> ! {
|
||||
// Note that this should be the only allocation performed in this code path.
|
||||
// Currently this means that panic!() on OOM will invoke this code path,
|
||||
// but then again we're not really ready for panic on OOM anyway. If
|
||||
// we do start doing this, then we should propagate this allocation to
|
||||
// be performed in the parent of this thread instead of the thread that's
|
||||
// panicking.
|
||||
|
||||
// see below for why we do the `Any` coercion here.
|
||||
begin_unwind_inner(box msg, file_line)
|
||||
}
|
||||
|
||||
/// The core of the unwinding.
|
||||
///
|
||||
/// This is non-generic to avoid instantiation bloat in other crates
|
||||
/// (which makes compilation of small crates noticeably slower). (Note:
|
||||
/// we need the `Any` object anyway, we're not just creating it to
|
||||
/// avoid being generic.)
|
||||
///
|
||||
/// Doing this split took the LLVM IR line counts of `fn main() { panic!()
|
||||
/// }` from ~1900/3700 (-O/no opts) to 180/590.
|
||||
#[inline(never)] #[cold] // this is the slow path, please never inline this
|
||||
fn begin_unwind_inner(msg: Box<Any + Send>, file_line: &(&'static str, uint)) -> ! {
|
||||
// Make sure the default panic handler is registered before we look at the
|
||||
// callbacks.
|
||||
static INIT: Once = ONCE_INIT;
|
||||
INIT.call_once(|| unsafe { register(panicking::on_panic); });
|
||||
|
||||
// First, invoke call the user-defined callbacks triggered on thread panic.
|
||||
//
|
||||
// By the time that we see a callback has been registered (by reading
|
||||
// MAX_CALLBACKS), the actual callback itself may have not been stored yet,
|
||||
// so we just chalk it up to a race condition and move on to the next
|
||||
// callback. Additionally, CALLBACK_CNT may briefly be higher than
|
||||
// MAX_CALLBACKS, so we're sure to clamp it as necessary.
|
||||
let callbacks = {
|
||||
let amt = CALLBACK_CNT.load(Ordering::SeqCst);
|
||||
&CALLBACKS[..cmp::min(amt, MAX_CALLBACKS)]
|
||||
};
|
||||
for cb in callbacks {
|
||||
match cb.load(Ordering::SeqCst) {
|
||||
0 => {}
|
||||
n => {
|
||||
let f: Callback = unsafe { mem::transmute(n) };
|
||||
let (file, line) = *file_line;
|
||||
f(&*msg, file, line);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Now that we've run all the necessary unwind callbacks, we actually
|
||||
// perform the unwinding.
|
||||
if panicking() {
|
||||
// If a thread panics while it's already unwinding then we
|
||||
// have limited options. Currently our preference is to
|
||||
// just abort. In the future we may consider resuming
|
||||
// unwinding or otherwise exiting the thread cleanly.
|
||||
rterrln!("thread panicked while panicking. aborting.");
|
||||
unsafe { intrinsics::abort() }
|
||||
}
|
||||
PANICKING.with(|s| s.set(true));
|
||||
rust_panic(msg);
|
||||
}
|
||||
|
||||
/// Register a callback to be invoked when a thread unwinds.
|
||||
///
|
||||
/// This is an unsafe and experimental API which allows for an arbitrary
|
||||
/// callback to be invoked when a thread panics. This callback is invoked on both
|
||||
/// the initial unwinding and a double unwinding if one occurs. Additionally,
|
||||
/// the local `Task` will be in place for the duration of the callback, and
|
||||
/// the callback must ensure that it remains in place once the callback returns.
|
||||
///
|
||||
/// Only a limited number of callbacks can be registered, and this function
|
||||
/// returns whether the callback was successfully registered or not. It is not
|
||||
/// currently possible to unregister a callback once it has been registered.
|
||||
#[unstable(feature = "std_misc")]
|
||||
pub unsafe fn register(f: Callback) -> bool {
|
||||
match CALLBACK_CNT.fetch_add(1, Ordering::SeqCst) {
|
||||
// The invocation code has knowledge of this window where the count has
|
||||
// been incremented, but the callback has not been stored. We're
|
||||
// guaranteed that the slot we're storing into is 0.
|
||||
n if n < MAX_CALLBACKS => {
|
||||
let prev = CALLBACKS[n].swap(mem::transmute(f), Ordering::SeqCst);
|
||||
rtassert!(prev == 0);
|
||||
true
|
||||
}
|
||||
// If we accidentally bumped the count too high, pull it back.
|
||||
_ => {
|
||||
CALLBACK_CNT.store(MAX_CALLBACKS, Ordering::SeqCst);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue