Very hacky MSVC hacks.

Conflicts:
	mk/platform.mk
	src/librustc/session/config.rs
	src/librustc_back/target/aarch64_apple_ios.rs
	src/librustc_back/target/aarch64_linux_android.rs
	src/librustc_back/target/arm_linux_androideabi.rs
	src/librustc_back/target/arm_unknown_linux_gnueabi.rs
	src/librustc_back/target/arm_unknown_linux_gnueabihf.rs
	src/librustc_back/target/armv7_apple_ios.rs
	src/librustc_back/target/armv7s_apple_ios.rs
	src/librustc_back/target/i386_apple_ios.rs
	src/librustc_back/target/i686_apple_darwin.rs
	src/librustc_back/target/i686_pc_windows_gnu.rs
	src/librustc_back/target/i686_unknown_dragonfly.rs
	src/librustc_back/target/i686_unknown_linux_gnu.rs
	src/librustc_back/target/mips_unknown_linux_gnu.rs
	src/librustc_back/target/mipsel_unknown_linux_gnu.rs
	src/librustc_back/target/mod.rs
	src/librustc_back/target/powerpc_unknown_linux_gnu.rs
	src/librustc_back/target/x86_64_apple_darwin.rs
	src/librustc_back/target/x86_64_apple_ios.rs
	src/librustc_back/target/x86_64_pc_windows_gnu.rs
	src/librustc_back/target/x86_64_unknown_dragonfly.rs
	src/librustc_back/target/x86_64_unknown_freebsd.rs
	src/librustc_back/target/x86_64_unknown_linux_gnu.rs
	src/librustc_back/target/x86_64_unknown_openbsd.rs
	src/librustc_llvm/lib.rs
	src/librustc_trans/back/link.rs
	src/librustc_trans/trans/base.rs
	src/libstd/os.rs
	src/rustllvm/RustWrapper.cpp
This commit is contained in:
Ricky Taylor 2015-03-04 22:58:59 +00:00 committed by Alex Crichton
parent 3ca008dcf1
commit 315750ac92
27 changed files with 1534 additions and 28 deletions

9
configure vendored
View File

@ -610,7 +610,7 @@ CFG_TARGET=$(to_llvm_triple $CFG_TARGET)
# there's no rpath. This is where the build system itself puts libraries;
# --libdir is used to configure the installation directory.
# FIXME: This needs to parameterized over target triples. Do it in platform.mk
if [ "$CFG_OSTYPE" = "pc-windows-gnu" ]
if [ "$CFG_OSTYPE" = "pc-windows-gnu" ] || [ "$CFG_OSTYPE" = "pc-windows-msvc" ]
then
CFG_LIBDIR_RELATIVE=bin
else
@ -628,7 +628,8 @@ esac
CFG_LIBDIR_RELATIVE=`echo ${CFG_LIBDIR} | cut -c$((${#CFG_PREFIX}+${CAT_INC}))-`
if [ "$CFG_OSTYPE" = "pc-windows-gnu" ] && [ "$CFG_LIBDIR_RELATIVE" != "bin" ]; then
if ( [ "$CFG_OSTYPE" = "pc-windows-gnu" ] || [ "$CFG_OSTYPE" = "pc-windows-msvc" ] ) \
&& [ "$CFG_LIBDIR_RELATIVE" != "bin" ]; then
err "libdir on windows should be set to 'bin'"
fi
@ -803,7 +804,7 @@ then
fi
BIN_SUF=
if [ "$CFG_OSTYPE" = "pc-windows-gnu" ]
if [ "$CFG_OSTYPE" = "pc-windows-gnu" ] || [ "$CFG_OSTYPE" = "pc-windows-msvc" ]
then
BIN_SUF=.exe
fi
@ -1311,7 +1312,7 @@ do
# (llvm's configure tries to find pthread first, so we have to disable it explicitly.)
# Also note that pthreads works badly on mingw-w64 systems: #8996
case "$CFG_BUILD" in
(*-windows-*)
(*-windows-gnu)
LLVM_OPTS="$LLVM_OPTS --disable-pthreads"
;;
esac

View File

@ -5,6 +5,7 @@ ifneq ($(findstring darwin,$(CFG_OSTYPE)),)
CFG_IOS_SDK_aarch64-apple-ios := $(shell xcrun --show-sdk-path -sdk iphoneos 2>/dev/null)
CFG_IOS_SDK_FLAGS_aarch64-apple-ios := -target aarch64-apple-darwin -isysroot $(CFG_IOS_SDK_aarch64-apple-ios) -mios-version-min=7.0 -arch arm64
CC_aarch64-apple-ios = $(shell xcrun -find -sdk iphoneos clang)
LINK_aarch64-apple-ios = $(shell xcrun -find -sdk iphoneos clang)
CXX_aarch64-apple-ios = $(shell xcrun -find -sdk iphoneos clang++)
CPP_aarch64-apple-ios = $(shell xcrun -find -sdk iphoneos clang++)
AR_aarch64-apple-ios = $(shell xcrun -find -sdk iphoneos ar)

View File

@ -1,6 +1,7 @@
# aarch64-linux-android configuration
# CROSS_PREFIX_aarch64-linux-android-
CC_aarch64-linux-android=$(CFG_ANDROID_CROSS_PATH)/bin/aarch64-linux-android-gcc
LINK_aarch64-linux-android=$(CFG_ANDROID_CROSS_PATH)/bin/aarch64-linux-android-gcc
CXX_aarch64-linux-android=$(CFG_ANDROID_CROSS_PATH)/bin/aarch64-linux-android-g++
CPP_aarch64-linux-android=$(CFG_ANDROID_CROSS_PATH)/bin/aarch64-linux-android-gcc -E
AR_aarch64-linux-android=$(CFG_ANDROID_CROSS_PATH)/bin/aarch64-linux-android-ar

View File

@ -1,6 +1,7 @@
# aarch64-unknown-linux-gnu configuration
CROSS_PREFIX_aarch64-unknown-linux-gnu=aarch64-linux-gnu-
CC_aarch64-unknown-linux-gnu=gcc
LINK_aarch64-unknown-linux-gnu=gcc
CXX_aarch64-unknown-linux-gnu=g++
CPP_aarch64-unknown-linux-gnu=gcc -E
AR_aarch64-unknown-linux-gnu=ar

View File

@ -1,4 +1,5 @@
# arm-linux-androideabi configuration
LINK_arm-linux-androideabi=$(CFG_ANDROID_CROSS_PATH)/bin/arm-linux-androideabi-gcc
CC_arm-linux-androideabi=$(CFG_ANDROID_CROSS_PATH)/bin/arm-linux-androideabi-gcc
CXX_arm-linux-androideabi=$(CFG_ANDROID_CROSS_PATH)/bin/arm-linux-androideabi-g++
CPP_arm-linux-androideabi=$(CFG_ANDROID_CROSS_PATH)/bin/arm-linux-androideabi-gcc -E

View File

@ -1,6 +1,7 @@
# x86_64-pc-windows-gnu configuration
CROSS_PREFIX_x86_64-pc-windows-gnu=x86_64-w64-mingw32-
CC_x86_64-pc-windows-gnu=gcc
LINK_x86_64-pc-windows-gnu=gcc
CXX_x86_64-pc-windows-gnu=g++
CPP_x86_64-pc-windows-gnu=gcc -E
AR_x86_64-pc-windows-gnu=ar

View File

@ -0,0 +1,29 @@
# 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
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_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

View File

@ -149,7 +149,8 @@ define CFG_MAKE_TOOLCHAIN
CXX_$(1)=$(CROSS_PREFIX_$(1))$(CXX_$(1))
CPP_$(1)=$(CROSS_PREFIX_$(1))$(CPP_$(1))
AR_$(1)=$(CROSS_PREFIX_$(1))$(AR_$(1))
RUSTC_CROSS_FLAGS_$(1)=-C linker=$$(call FIND_COMPILER,$$(CC_$(1))) \
LINK_$(1)=$(CROSS_PREFIX_$(1))$(LINK_$(1))
RUSTC_CROSS_FLAGS_$(1)=-C linker=$$(call FIND_COMPILER,$$(LINK_$(1))) \
-C ar=$$(call FIND_COMPILER,$$(AR_$(1))) $(RUSTC_CROSS_FLAGS_$(1))
RUSTC_FLAGS_$(1)=$$(RUSTC_CROSS_FLAGS_$(1)) $(RUSTC_FLAGS_$(1))

View File

@ -632,6 +632,7 @@ 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)),

View File

@ -22,6 +22,7 @@ pub fn target() -> Target {
target_env: "gnu".to_string(),
arch: "aarch64".to_string(),
target_os: "linux".to_string(),
target_abi: "".to_string(),
options: base,
}
}

View File

@ -22,7 +22,11 @@ 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,
}
}

View File

@ -145,6 +145,7 @@ pub struct TargetOptions {
/// only really used for figuring out how to find libraries, since Windows uses its own
/// library naming convention. Defaults to false.
pub is_like_windows: bool,
pub is_like_msvc: bool,
/// Whether the target toolchain is like Android's. Only useful for compiling against Android.
/// Defaults to false.
pub is_like_android: bool,
@ -188,6 +189,7 @@ impl Default for TargetOptions {
is_like_osx: false,
is_like_windows: false,
is_like_android: false,
is_like_msvc: false,
linker_is_gnu: false,
has_rpath: false,
no_compiler_rt: false,
@ -371,7 +373,9 @@ impl Target {
armv7s_apple_ios,
x86_64_pc_windows_gnu,
i686_pc_windows_gnu
i686_pc_windows_gnu,
x86_64_pc_windows_msvc
);

View File

@ -0,0 +1,33 @@
// 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.
use target::TargetOptions;
use std::default::Default;
pub fn opts() -> TargetOptions {
TargetOptions {
// FIXME(#13846) this should be enabled for windows
function_sections: false,
linker: "link".to_string(),
dynamic_linking: true,
executables: true,
dll_prefix: "".to_string(),
dll_suffix: ".dll".to_string(),
exe_suffix: ".exe".to_string(),
staticlib_prefix: "".to_string(),
staticlib_suffix: ".lib".to_string(),
morestack: false,
is_like_windows: true,
is_like_msvc: true,
pre_link_args: Vec::new(),
.. Default::default()
}
}

View File

@ -0,0 +1,30 @@
// 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.
use target::Target;
pub fn target() -> Target {
let mut base = super::windows_msvc_base::opts();
base.cpu = "x86-64".to_string();
Target {
// FIXME: Test this. Copied from linux (#2398)
data_layout: "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-\
f32:32:32-f64:64:64-v64:64:64-v128:128:128-a:0:64-\
s0:64:64-f80:128:128-n8:16:32:64-S128".to_string(),
llvm_target: "x86_64-pc-windows-msvc".to_string(),
target_endian: "little".to_string(),
target_pointer_width: "64".to_string(),
arch: "x86_64".to_string(),
target_os: "windows".to_string(),
target_abi: "msvc".to_string(),
options: base,
}
}

View File

@ -55,6 +55,7 @@ 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;
@ -114,6 +115,13 @@ 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 {
@ -2125,6 +2133,12 @@ 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);

View File

@ -8,22 +8,21 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use super::archive::{Archive, ArchiveBuilder, ArchiveConfig, METADATA_FILENAME};
use super::archive;
use super::rpath;
use super::rpath::RPathConfig;
use super::archive::{ArchiveBuilder, ArchiveConfig, METADATA_FILENAME};
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, filesearch, csearch, creader};
use metadata::{encoder, cstore, 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;

View File

@ -0,0 +1,568 @@
// 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");
}
}
}
}
}

View File

@ -0,0 +1,460 @@
// 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");
}
}
}
}
}

View File

@ -75,6 +75,9 @@ pub mod back {
pub use rustc_back::x86_64;
pub mod link;
mod link_gnu;
mod link_msvc;
pub mod lto;
pub mod write;

View File

@ -1907,6 +1907,16 @@ 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
@ -2171,7 +2181,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 {
unsafe { llvm::LLVMRustSetDLLExportStorageClass(llfn) }
llvm::SetDLLStorageClass(llfn, llvm::DLLExportStorageClass);
}
let llbb = unsafe {

View File

@ -863,6 +863,14 @@ 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 {

View File

@ -31,6 +31,8 @@ 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 {
@ -44,13 +46,10 @@ mod cmath {
pub fn erfcf(n: c_float) -> c_float;
pub fn expm1f(n: c_float) -> c_float;
pub fn fdimf(a: c_float, b: c_float) -> c_float;
pub fn frexpf(n: c_float, value: &mut c_int) -> c_float;
pub fn fmaxf(a: c_float, b: c_float) -> c_float;
pub fn fminf(a: c_float, b: c_float) -> c_float;
pub fn fmodf(a: c_float, b: c_float) -> c_float;
pub fn nextafterf(x: c_float, y: c_float) -> c_float;
pub fn hypotf(x: c_float, y: c_float) -> c_float;
pub fn ldexpf(x: c_float, n: c_int) -> c_float;
pub fn logbf(n: c_float) -> c_float;
pub fn log1pf(n: c_float) -> c_float;
pub fn ilogbf(n: c_float) -> c_int;
@ -62,11 +61,33 @@ mod cmath {
#[cfg(unix)]
pub fn lgammaf_r(n: c_float, sign: &mut c_int) -> c_float;
#[cfg(unix)]
pub fn hypotf(x: c_float, y: c_float) -> c_float;
#[cfg(unix)]
pub fn frexpf(n: c_float, value: &mut c_int) -> c_float;
#[cfg(unix)]
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(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(not(test))]

View File

@ -48,7 +48,6 @@ mod cmath {
pub fn fmod(a: c_double, b: c_double) -> c_double;
pub fn nextafter(x: c_double, y: c_double) -> c_double;
pub fn frexp(n: c_double, value: &mut c_int) -> c_double;
pub fn hypot(x: c_double, y: c_double) -> c_double;
pub fn ldexp(x: c_double, n: c_int) -> c_double;
pub fn logb(n: c_double) -> c_double;
pub fn log1p(n: c_double) -> c_double;
@ -74,6 +73,13 @@ mod cmath {
#[cfg(windows)]
#[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"]
pub fn hypot(x: c_double, y: c_double) -> c_double;
}
}

View File

@ -279,6 +279,7 @@ mod imp {
const CRYPT_VERIFYCONTEXT: DWORD = 0xF0000000;
#[allow(non_snake_case)]
#[link(name = "advapi32")]
extern "system" {
fn CryptAcquireContextA(phProv: *mut HCRYPTPROV,
pszContainer: LPCSTR,

View File

@ -39,10 +39,17 @@ 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

View File

@ -0,0 +1,302 @@
// 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
}
}
}

View File

@ -191,9 +191,7 @@ rust_dbg_abi_2(struct floats f) {
}
int
rust_dbg_static_mut;
int rust_dbg_static_mut = 3;
rust_dbg_static_mut = 3;
void
rust_dbg_static_mut_check_four() {