From 315750ac92a8114a96b35352ec88f82d21d5fbec Mon Sep 17 00:00:00 2001 From: Ricky Taylor Date: Wed, 4 Mar 2015 22:58:59 +0000 Subject: [PATCH 01/31] 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 --- configure | 9 +- mk/cfg/aarch64-apple-ios.mk | 1 + mk/cfg/aarch64-linux-android.mk | 1 + mk/cfg/aarch64-unknown-linux-gnu.mk | 1 + mk/cfg/arm-linux-androideabi.mk | 1 + mk/cfg/x86_64-pc-windows-gnu.mk | 1 + mk/cfg/x86_64-pc-windows-msvc.mk | 29 + mk/platform.mk | 17 +- src/librustc/session/config.rs | 1 + .../target/aarch64_unknown_linux_gnu.rs | 1 + .../target/i686_unknown_linux_gnu.rs | 4 + src/librustc_back/target/mod.rs | 6 +- src/librustc_back/target/windows_msvc_base.rs | 33 + .../target/x86_64_pc_windows_msvc.rs | 30 + src/librustc_llvm/lib.rs | 14 + src/librustc_trans/back/link.rs | 13 +- src/librustc_trans/back/link_gnu.rs | 568 ++++++++++++++++++ src/librustc_trans/back/link_msvc.rs | 460 ++++++++++++++ src/librustc_trans/lib.rs | 3 + src/librustc_trans/trans/base.rs | 12 +- src/librustc_trans/trans/consts.rs | 8 + src/libstd/num/f32.rs | 27 +- src/libstd/num/f64.rs | 8 +- src/libstd/rand/os.rs | 1 + src/libstd/rt/mod.rs | 9 +- src/libstd/rt/unwind_msvc.rs | 302 ++++++++++ src/rt/rust_test_helpers.c | 4 +- 27 files changed, 1535 insertions(+), 29 deletions(-) create mode 100644 mk/cfg/x86_64-pc-windows-msvc.mk create mode 100644 src/librustc_back/target/windows_msvc_base.rs create mode 100644 src/librustc_back/target/x86_64_pc_windows_msvc.rs create mode 100644 src/librustc_trans/back/link_gnu.rs create mode 100644 src/librustc_trans/back/link_msvc.rs create mode 100644 src/libstd/rt/unwind_msvc.rs diff --git a/configure b/configure index 9b9de9da06716..4e150488224b8 100755 --- a/configure +++ b/configure @@ -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 diff --git a/mk/cfg/aarch64-apple-ios.mk b/mk/cfg/aarch64-apple-ios.mk index 7767129a5e218..8cd09fa9043c8 100644 --- a/mk/cfg/aarch64-apple-ios.mk +++ b/mk/cfg/aarch64-apple-ios.mk @@ -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) diff --git a/mk/cfg/aarch64-linux-android.mk b/mk/cfg/aarch64-linux-android.mk index d7a1405c3d0a8..9e0245e093d8a 100644 --- a/mk/cfg/aarch64-linux-android.mk +++ b/mk/cfg/aarch64-linux-android.mk @@ -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 diff --git a/mk/cfg/aarch64-unknown-linux-gnu.mk b/mk/cfg/aarch64-unknown-linux-gnu.mk index 6637423e4951a..88d7700db820f 100644 --- a/mk/cfg/aarch64-unknown-linux-gnu.mk +++ b/mk/cfg/aarch64-unknown-linux-gnu.mk @@ -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 diff --git a/mk/cfg/arm-linux-androideabi.mk b/mk/cfg/arm-linux-androideabi.mk index fdd38ba75fe58..a66f70f6305ed 100644 --- a/mk/cfg/arm-linux-androideabi.mk +++ b/mk/cfg/arm-linux-androideabi.mk @@ -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 diff --git a/mk/cfg/x86_64-pc-windows-gnu.mk b/mk/cfg/x86_64-pc-windows-gnu.mk index 4118ea26c072b..10aaf137e8b3a 100644 --- a/mk/cfg/x86_64-pc-windows-gnu.mk +++ b/mk/cfg/x86_64-pc-windows-gnu.mk @@ -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 diff --git a/mk/cfg/x86_64-pc-windows-msvc.mk b/mk/cfg/x86_64-pc-windows-msvc.mk new file mode 100644 index 0000000000000..3235dcece6d49 --- /dev/null +++ b/mk/cfg/x86_64-pc-windows-msvc.mk @@ -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 diff --git a/mk/platform.mk b/mk/platform.mk index 9545a1fb52d9e..01865319b3fa7 100644 --- a/mk/platform.mk +++ b/mk/platform.mk @@ -145,14 +145,15 @@ FIND_COMPILER = $(word 1,$(1:ccache=)) define CFG_MAKE_TOOLCHAIN # Prepend the tools with their prefix if cross compiling ifneq ($(CFG_BUILD),$(1)) - CC_$(1)=$(CROSS_PREFIX_$(1))$(CC_$(1)) - 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))) \ - -C ar=$$(call FIND_COMPILER,$$(AR_$(1))) $(RUSTC_CROSS_FLAGS_$(1)) - - RUSTC_FLAGS_$(1)=$$(RUSTC_CROSS_FLAGS_$(1)) $(RUSTC_FLAGS_$(1)) + CC_$(1)=$(CROSS_PREFIX_$(1))$(CC_$(1)) + CXX_$(1)=$(CROSS_PREFIX_$(1))$(CXX_$(1)) + CPP_$(1)=$(CROSS_PREFIX_$(1))$(CPP_$(1)) + AR_$(1)=$(CROSS_PREFIX_$(1))$(AR_$(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)) endif CFG_COMPILE_C_$(1) = $$(CC_$(1)) \ diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index b999929c4af9e..4154ee29388c0 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -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)), diff --git a/src/librustc_back/target/aarch64_unknown_linux_gnu.rs b/src/librustc_back/target/aarch64_unknown_linux_gnu.rs index 18e67d066d036..796aa3b08ca4e 100644 --- a/src/librustc_back/target/aarch64_unknown_linux_gnu.rs +++ b/src/librustc_back/target/aarch64_unknown_linux_gnu.rs @@ -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, } } diff --git a/src/librustc_back/target/i686_unknown_linux_gnu.rs b/src/librustc_back/target/i686_unknown_linux_gnu.rs index 21094ad905e90..425077e97388d 100644 --- a/src/librustc_back/target/i686_unknown_linux_gnu.rs +++ b/src/librustc_back/target/i686_unknown_linux_gnu.rs @@ -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, } } diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs index c5f1882fa1dd7..de8086bec4f67 100644 --- a/src/librustc_back/target/mod.rs +++ b/src/librustc_back/target/mod.rs @@ -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 ); diff --git a/src/librustc_back/target/windows_msvc_base.rs b/src/librustc_back/target/windows_msvc_base.rs new file mode 100644 index 0000000000000..f53f0df0e639d --- /dev/null +++ b/src/librustc_back/target/windows_msvc_base.rs @@ -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 or the MIT license +// , 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() + } +} diff --git a/src/librustc_back/target/x86_64_pc_windows_msvc.rs b/src/librustc_back/target/x86_64_pc_windows_msvc.rs new file mode 100644 index 0000000000000..1cbf7aabeefe0 --- /dev/null +++ b/src/librustc_back/target/x86_64_pc_windows_msvc.rs @@ -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 or the MIT license +// , 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, + } +} diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs index 2a2aa2bf4cf93..74d7dc95b24af 100644 --- a/src/librustc_llvm/lib.rs +++ b/src/librustc_llvm/lib.rs @@ -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); diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index 38ad909dd012e..aa51afac1a4a4 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -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; diff --git a/src/librustc_trans/back/link_gnu.rs b/src/librustc_trans/back/link_gnu.rs new file mode 100644 index 0000000000000..b1582a1a3c596 --- /dev/null +++ b/src/librustc_trans/back/link_gnu.rs @@ -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 or the MIT license +// , 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"); + } + } + } + } +} diff --git a/src/librustc_trans/back/link_msvc.rs b/src/librustc_trans/back/link_msvc.rs new file mode 100644 index 0000000000000..0237ac1231cda --- /dev/null +++ b/src/librustc_trans/back/link_msvc.rs @@ -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 or the MIT license +// , 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"); + } + } + } + } +} diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index 3e2db80a9c556..97c672df148f0 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -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; diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 4879975dde695..413e5e1f49ffc 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -1907,6 +1907,16 @@ pub fn update_linkage(ccx: &CrateContext, llval: ValueRef, id: Option, 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 { diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs index 503bdf8dadb94..989ef8d8bf45e 100644 --- a/src/librustc_trans/trans/consts.rs +++ b/src/librustc_trans/trans/consts.rs @@ -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 { diff --git a/src/libstd/num/f32.rs b/src/libstd/num/f32.rs index 1ee3aab2727a8..934cf056ec19d 100644 --- a/src/libstd/num/f32.rs +++ b/src/libstd/num/f32.rs @@ -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))] diff --git a/src/libstd/num/f64.rs b/src/libstd/num/f64.rs index 398afcb553c1c..a09a82b85522e 100644 --- a/src/libstd/num/f64.rs +++ b/src/libstd/num/f64.rs @@ -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; } } diff --git a/src/libstd/rand/os.rs b/src/libstd/rand/os.rs index 3c36f0f1d490c..885adf19ca1fc 100644 --- a/src/libstd/rand/os.rs +++ b/src/libstd/rand/os.rs @@ -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, diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index 0d26206f26bcf..d7eede6e953bc 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -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 diff --git a/src/libstd/rt/unwind_msvc.rs b/src/libstd/rt/unwind_msvc.rs new file mode 100644 index 0000000000000..b71db510c00b3 --- /dev/null +++ b/src/libstd/rt/unwind_msvc.rs @@ -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 or the MIT license +// , 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 = 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: F) -> Result<(), Box> { + 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) -> ! { + 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(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, 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 + } + } +} diff --git a/src/rt/rust_test_helpers.c b/src/rt/rust_test_helpers.c index c755cf67caa9f..ac925e4260a54 100644 --- a/src/rt/rust_test_helpers.c +++ b/src/rt/rust_test_helpers.c @@ -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() { From 4cc025d83c5e3e54130dff08987982a0926e4cfa Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 8 May 2015 14:44:17 -0700 Subject: [PATCH 02/31] Scale back changes made --- mk/cfg/x86_64-pc-windows-msvc.mk | 19 +- mk/platform.mk | 18 +- src/librustc/session/config.rs | 1 - .../target/aarch64_unknown_linux_gnu.rs | 1 - .../target/i686_unknown_linux_gnu.rs | 4 - src/librustc_back/target/mod.rs | 1 + src/librustc_back/target/windows_msvc_base.rs | 3 +- .../target/x86_64_pc_windows_msvc.rs | 2 +- src/librustc_llvm/lib.rs | 14 - src/librustc_trans/back/link.rs | 13 +- src/librustc_trans/back/link_gnu.rs | 568 ------------------ src/librustc_trans/back/link_msvc.rs | 460 -------------- src/librustc_trans/lib.rs | 3 - src/librustc_trans/trans/base.rs | 12 +- src/librustc_trans/trans/consts.rs | 8 - src/libstd/num/f32.rs | 40 +- src/libstd/num/f64.rs | 11 +- src/libstd/rt/mod.rs | 9 +- src/libstd/rt/unwind_msvc.rs | 302 ---------- 19 files changed, 45 insertions(+), 1444 deletions(-) delete mode 100644 src/librustc_trans/back/link_gnu.rs delete mode 100644 src/librustc_trans/back/link_msvc.rs delete mode 100644 src/libstd/rt/unwind_msvc.rs diff --git a/mk/cfg/x86_64-pc-windows-msvc.mk b/mk/cfg/x86_64-pc-windows-msvc.mk index 3235dcece6d49..4a77b658d46fb 100644 --- a/mk/cfg/x86_64-pc-windows-msvc.mk +++ b/mk/cfg/x86_64-pc-windows-msvc.mk @@ -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 diff --git a/mk/platform.mk b/mk/platform.mk index 01865319b3fa7..f66e451f2367c 100644 --- a/mk/platform.mk +++ b/mk/platform.mk @@ -145,15 +145,15 @@ FIND_COMPILER = $(word 1,$(1:ccache=)) define CFG_MAKE_TOOLCHAIN # Prepend the tools with their prefix if cross compiling ifneq ($(CFG_BUILD),$(1)) - CC_$(1)=$(CROSS_PREFIX_$(1))$(CC_$(1)) - CXX_$(1)=$(CROSS_PREFIX_$(1))$(CXX_$(1)) - CPP_$(1)=$(CROSS_PREFIX_$(1))$(CPP_$(1)) - AR_$(1)=$(CROSS_PREFIX_$(1))$(AR_$(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)) + CC_$(1)=$(CROSS_PREFIX_$(1))$(CC_$(1)) + CXX_$(1)=$(CROSS_PREFIX_$(1))$(CXX_$(1)) + CPP_$(1)=$(CROSS_PREFIX_$(1))$(CPP_$(1)) + AR_$(1)=$(CROSS_PREFIX_$(1))$(AR_$(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)) endif CFG_COMPILE_C_$(1) = $$(CC_$(1)) \ diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 4154ee29388c0..b999929c4af9e 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -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)), diff --git a/src/librustc_back/target/aarch64_unknown_linux_gnu.rs b/src/librustc_back/target/aarch64_unknown_linux_gnu.rs index 796aa3b08ca4e..18e67d066d036 100644 --- a/src/librustc_back/target/aarch64_unknown_linux_gnu.rs +++ b/src/librustc_back/target/aarch64_unknown_linux_gnu.rs @@ -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, } } diff --git a/src/librustc_back/target/i686_unknown_linux_gnu.rs b/src/librustc_back/target/i686_unknown_linux_gnu.rs index 425077e97388d..21094ad905e90 100644 --- a/src/librustc_back/target/i686_unknown_linux_gnu.rs +++ b/src/librustc_back/target/i686_unknown_linux_gnu.rs @@ -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, } } diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs index de8086bec4f67..a3cb357dc05c2 100644 --- a/src/librustc_back/target/mod.rs +++ b/src/librustc_back/target/mod.rs @@ -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. /// diff --git a/src/librustc_back/target/windows_msvc_base.rs b/src/librustc_back/target/windows_msvc_base.rs index f53f0df0e639d..f4f25ea856944 100644 --- a/src/librustc_back/target/windows_msvc_base.rs +++ b/src/librustc_back/target/windows_msvc_base.rs @@ -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, diff --git a/src/librustc_back/target/x86_64_pc_windows_msvc.rs b/src/librustc_back/target/x86_64_pc_windows_msvc.rs index 1cbf7aabeefe0..aac1afbb97cd6 100644 --- a/src/librustc_back/target/x86_64_pc_windows_msvc.rs +++ b/src/librustc_back/target/x86_64_pc_windows_msvc.rs @@ -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, } } diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs index 74d7dc95b24af..2a2aa2bf4cf93 100644 --- a/src/librustc_llvm/lib.rs +++ b/src/librustc_llvm/lib.rs @@ -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); diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index aa51afac1a4a4..38ad909dd012e 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -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; diff --git a/src/librustc_trans/back/link_gnu.rs b/src/librustc_trans/back/link_gnu.rs deleted file mode 100644 index b1582a1a3c596..0000000000000 --- a/src/librustc_trans/back/link_gnu.rs +++ /dev/null @@ -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 or the MIT license -// , 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"); - } - } - } - } -} diff --git a/src/librustc_trans/back/link_msvc.rs b/src/librustc_trans/back/link_msvc.rs deleted file mode 100644 index 0237ac1231cda..0000000000000 --- a/src/librustc_trans/back/link_msvc.rs +++ /dev/null @@ -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 or the MIT license -// , 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"); - } - } - } - } -} diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index 97c672df148f0..3e2db80a9c556 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -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; diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 413e5e1f49ffc..4879975dde695 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -1907,16 +1907,6 @@ pub fn update_linkage(ccx: &CrateContext, llval: ValueRef, id: Option, 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 { diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs index 989ef8d8bf45e..503bdf8dadb94 100644 --- a/src/librustc_trans/trans/consts.rs +++ b/src/librustc_trans/trans/consts.rs @@ -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 { diff --git a/src/libstd/num/f32.rs b/src/libstd/num/f32.rs index 934cf056ec19d..e31d97b324038 100644 --- a/src/libstd/num/f32.rs +++ b/src/libstd/num/f32.rs @@ -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))] diff --git a/src/libstd/num/f64.rs b/src/libstd/num/f64.rs index a09a82b85522e..e87855ffd4eed 100644 --- a/src/libstd/num/f64.rs +++ b/src/libstd/num/f64.rs @@ -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)] + #[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(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"] + #[cfg_attr(all(windows, target_env = "msvc"), link_name = "_hypot")] pub fn hypot(x: c_double, y: c_double) -> c_double; } } diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index d7eede6e953bc..0d26206f26bcf 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -39,17 +39,10 @@ mod macros; // These should be refactored/moved/made private over time pub mod util; -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; +pub mod args; 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 diff --git a/src/libstd/rt/unwind_msvc.rs b/src/libstd/rt/unwind_msvc.rs deleted file mode 100644 index b71db510c00b3..0000000000000 --- a/src/libstd/rt/unwind_msvc.rs +++ /dev/null @@ -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 or the MIT license -// , 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 = 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: F) -> Result<(), Box> { - 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) -> ! { - 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(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, 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 - } - } -} From 2d5e5777fd94fcad0bf259e31982477e42bd5bf8 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 8 May 2015 15:31:23 -0700 Subject: [PATCH 03/31] rustc_trans: Abstract linker support behind a trait This trait will be used to correctly build a command line for link.exe with MSVC and may perhaps one day be used to generate a command line for `lld`, but this commit currently just refactors the bindings used to call `ld`. --- src/librustc_trans/back/link.rs | 203 +++++++++--------------------- src/librustc_trans/back/linker.rs | 175 ++++++++++++++++++++++++++ src/librustc_trans/lib.rs | 1 + 3 files changed, 237 insertions(+), 142 deletions(-) create mode 100644 src/librustc_trans/back/linker.rs diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index 38ad909dd012e..e748f8b44cbed 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -9,9 +9,9 @@ // except according to those terms. use super::archive::{Archive, ArchiveBuilder, ArchiveConfig, METADATA_FILENAME}; -use super::archive; -use super::rpath; +use super::linker::{Linker, GnuLinker}; use super::rpath::RPathConfig; +use super::rpath; use super::svh::Svh; use session::config; use session::config::NoDebugInfo; @@ -29,7 +29,6 @@ use util::sha2::{Digest, Sha256}; use util::fs::fix_windows_verbatim_for_gcc; use rustc_back::tempdir::TempDir; -use std::ffi::OsString; use std::fs::{self, PathExt}; use std::io::{self, Read, Write}; use std::mem; @@ -801,10 +800,13 @@ fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool, cmd.arg(root.join(obj)); } - link_args(&mut cmd, sess, dylib, tmpdir.path(), - trans, obj_filename, out_filename); - if !sess.target.target.options.no_compiler_rt { - cmd.arg("-lcompiler-rt"); + { + let mut linker = GnuLinker { cmd: &mut cmd, sess: &sess }; + link_args(&mut linker, sess, dylib, tmpdir.path(), + trans, obj_filename, out_filename); + if !sess.target.target.options.no_compiler_rt { + linker.link_staticlib("compiler-rt"); + } } for obj in &sess.target.target.options.post_link_objects { cmd.arg(root.join(obj)); @@ -858,7 +860,7 @@ fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool, } } -fn link_args(cmd: &mut Command, +fn link_args(cmd: &mut Linker, sess: &Session, dylib: bool, tmpdir: &Path, @@ -873,10 +875,10 @@ fn link_args(cmd: &mut Command, // target descriptor let t = &sess.target.target; - cmd.arg("-L").arg(&fix_windows_verbatim_for_gcc(&lib_path)); - - cmd.arg("-o").arg(out_filename).arg(obj_filename); + cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path)); + cmd.output_filename(out_filename); + cmd.add_object(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 @@ -895,89 +897,44 @@ fn link_args(cmd: &mut Command, // 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 = OsString::from("-Wl,-force_load,"); - v.push(&morestack); - cmd.arg(&v); - } else { - cmd.args(&["-Wl,--whole-archive", "-lmorestack", "-Wl,--no-whole-archive"]); - } + cmd.link_whole_staticlib("morestack", &[lib_path]); } // 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")); + cmd.add_object(&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"); - } + // Try to strip as much out of the generated object by removing unused + // sections if possible. See more comments in linker.rs + cmd.gc_sections(dylib); let used_link_args = sess.cstore.get_used_link_args().borrow(); - if t.options.position_independent_executables { + if !dylib && 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") + let relocation_model = sess.opts.cg.relocation_model.as_ref() + .unwrap_or(&empty_str); + if (t.options.relocation_model == "pic" || *relocation_model == "pic") && !args.any(|x| *x == "-static") { - cmd.arg("-pie"); + cmd.position_independent_executable(); } } - 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"); - } - } + // Pass optimization flags down to the linker. + cmd.optimize(); // 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"); - } + cmd.no_default_libraries(); // 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 @@ -1019,18 +976,7 @@ fn link_args(cmd: &mut Command, // # 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 = OsString::from("-Wl,-install_name,@rpath/"); - v.push(out_filename.file_name().unwrap()); - cmd.arg(&v); - } - } else { - cmd.arg("-shared"); - } + cmd.build_dylib(out_filename); } // FIXME (#2397): At some point we want to rpath our guesses as to @@ -1059,9 +1005,10 @@ fn link_args(cmd: &mut Command, // 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[..]); + if let Some(ref args) = sess.opts.cg.link_args { + cmd.args(args); + } + cmd.args(&used_link_args); } // # Native library linking @@ -1075,21 +1022,15 @@ fn link_args(cmd: &mut Command, // 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) { +fn add_local_native_libraries(cmd: &mut Linker, 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(&fix_windows_verbatim_for_gcc(path)); } + PathKind::Framework => { cmd.framework_path(path); } + _ => { cmd.include_path(&fix_windows_verbatim_for_gcc(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(); @@ -1100,46 +1041,29 @@ fn add_local_native_libraries(cmd: &mut Command, sess: &Session) { 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"); - } + // 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. + cmd.hint_static(); + let search_path = 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 = OsString::from("-Wl,-force_load,"); - v.push(&lib); - cmd.arg(&v); - } - } - if takes_hints { - cmd.arg("-Wl,--no-whole-archive").arg("-Wl,-Bdynamic"); + // Here we explicitly ask that the entire archive is included into the + // result artifact. 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. + cmd.link_whole_staticlib(l, &search_path); } + cmd.hint_dynamic(); + for &(ref l, kind) in others { match kind { - cstore::NativeUnknown => { - cmd.arg(&format!("-l{}", l)); - } - cstore::NativeFramework => { - cmd.arg("-framework").arg(&l[..]); - } + cstore::NativeUnknown => cmd.link_dylib(l), + cstore::NativeFramework => cmd.link_framework(l), cstore::NativeStatic => unreachable!(), } } @@ -1150,7 +1074,7 @@ fn add_local_native_libraries(cmd: &mut Command, sess: &Session) { // 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, +fn add_upstream_rust_crates(cmd: &mut Linker, sess: &Session, dylib: bool, tmpdir: &Path, trans: &CrateTranslation) { // All of the heavy lifting has previously been accomplished by the @@ -1201,7 +1125,7 @@ fn add_upstream_rust_crates(cmd: &mut Command, sess: &Session, } // Adds the static "rlib" versions of all crates to the command line. - fn add_static_crate(cmd: &mut Command, sess: &Session, tmpdir: &Path, + fn add_static_crate(cmd: &mut Linker, sess: &Session, tmpdir: &Path, cratepath: &Path) { // When performing LTO on an executable output, all of the // bytecode from the upstream libraries has already been @@ -1263,16 +1187,16 @@ fn add_upstream_rust_crates(cmd: &mut Command, sess: &Session, archive.remove_file(&format!("{}.o", name)); let files = archive.files(); if files.iter().any(|s| s.ends_with(".o")) { - cmd.arg(&dst); + cmd.link_rlib(&dst); } }); } else { - cmd.arg(&fix_windows_verbatim_for_gcc(cratepath)); + cmd.link_rlib(&fix_windows_verbatim_for_gcc(cratepath)); } } // Same thing as above, but for dynamic crates instead of static crates. - fn add_dynamic_crate(cmd: &mut Command, sess: &Session, cratepath: &Path) { + fn add_dynamic_crate(cmd: &mut Linker, 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()); @@ -1280,10 +1204,10 @@ fn add_upstream_rust_crates(cmd: &mut Command, sess: &Session, // Just need to tell the linker about where the library lives and // what its name is if let Some(dir) = cratepath.parent() { - cmd.arg("-L").arg(&fix_windows_verbatim_for_gcc(dir)); + cmd.include_path(&fix_windows_verbatim_for_gcc(dir)); } let filestem = cratepath.file_stem().unwrap().to_str().unwrap(); - cmd.arg(&format!("-l{}", unlib(&sess.target, filestem))); + cmd.link_dylib(&unlib(&sess.target, filestem)); } } @@ -1305,7 +1229,7 @@ fn add_upstream_rust_crates(cmd: &mut Command, sess: &Session, // 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) { +fn add_upstream_native_libraries(cmd: &mut Linker, 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 @@ -1320,13 +1244,8 @@ fn add_upstream_native_libraries(cmd: &mut Command, sess: &Session) { 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::NativeUnknown => cmd.link_dylib(lib), + cstore::NativeFramework => cmd.link_framework(lib), cstore::NativeStatic => { sess.bug("statics shouldn't be propagated"); } diff --git a/src/librustc_trans/back/linker.rs b/src/librustc_trans/back/linker.rs new file mode 100644 index 0000000000000..da1ec29a41e71 --- /dev/null +++ b/src/librustc_trans/back/linker.rs @@ -0,0 +1,175 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::ffi::OsString; +use std::path::{Path, PathBuf}; +use std::process::Command; + +use rustc_back::archive; +use session::Session; +use session::config; + +/// Linker abstraction used by back::link to build up the command to invoke a +/// linker. +/// +/// This trait is the total list of requirements needed by `back::link` and +/// represents the meaning of each option being passed down. This trait is then +/// used to dispatch on whether a GNU-like linker (generally `ld.exe`) or an +/// MSVC linker (e.g. `link.exe`) is being used. +pub trait Linker { + fn link_dylib(&mut self, lib: &str); + fn link_framework(&mut self, framework: &str); + fn link_staticlib(&mut self, lib: &str); + fn link_rlib(&mut self, lib: &Path); + fn link_whole_staticlib(&mut self, lib: &str, search_path: &[PathBuf]); + fn include_path(&mut self, path: &Path); + fn framework_path(&mut self, path: &Path); + fn output_filename(&mut self, path: &Path); + fn add_object(&mut self, path: &Path); + fn gc_sections(&mut self, is_dylib: bool); + fn position_independent_executable(&mut self); + fn optimize(&mut self); + fn no_default_libraries(&mut self); + fn build_dylib(&mut self, out_filename: &Path); + fn args(&mut self, args: &[String]); + fn hint_static(&mut self); + fn hint_dynamic(&mut self); + fn whole_archives(&mut self); + fn no_whole_archives(&mut self); +} + +pub struct GnuLinker<'a> { + pub cmd: &'a mut Command, + pub sess: &'a Session, +} + +impl<'a> GnuLinker<'a> { + fn takes_hints(&self) -> bool { + !self.sess.target.target.options.is_like_osx + } +} + +impl<'a> Linker for GnuLinker<'a> { + fn link_dylib(&mut self, lib: &str) { self.cmd.arg("-l").arg(lib); } + fn link_staticlib(&mut self, lib: &str) { self.cmd.arg("-l").arg(lib); } + fn link_rlib(&mut self, lib: &Path) { self.cmd.arg(lib); } + fn include_path(&mut self, path: &Path) { self.cmd.arg("-L").arg(path); } + fn framework_path(&mut self, path: &Path) { self.cmd.arg("-F").arg(path); } + fn output_filename(&mut self, path: &Path) { self.cmd.arg("-o").arg(path); } + fn add_object(&mut self, path: &Path) { self.cmd.arg(path); } + fn position_independent_executable(&mut self) { self.cmd.arg("-pie"); } + fn args(&mut self, args: &[String]) { self.cmd.args(args); } + + fn link_framework(&mut self, framework: &str) { + self.cmd.arg("-framework").arg(framework); + } + + fn link_whole_staticlib(&mut self, lib: &str, search_path: &[PathBuf]) { + let target = &self.sess.target.target; + if !target.options.is_like_osx { + self.cmd.arg("-Wl,--whole-archive") + .arg("-l").arg(lib) + .arg("-Wl,--no-whole-archive"); + } else { + // -force_load is the OSX equivalent of --whole-archive, but it + // involves passing the full path to the library to link. + let mut v = OsString::from("-Wl,-force_load,"); + v.push(&archive::find_library(lib, + &target.options.staticlib_prefix, + &target.options.staticlib_suffix, + search_path, + &self.sess.diagnostic().handler)); + self.cmd.arg(&v); + } + } + + fn gc_sections(&mut self, is_dylib: bool) { + // 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. + if self.sess.target.target.options.is_like_osx { + self.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. + } else if !is_dylib { + self.cmd.arg("-Wl,--gc-sections"); + } + } + + fn optimize(&mut self) { + if !self.sess.target.target.options.linker_is_gnu { return } + + // GNU-style linkers support optimization with -O. GNU ld doesn't + // need a numeric argument, but other linkers do. + if self.sess.opts.optimize == config::Default || + self.sess.opts.optimize == config::Aggressive { + self.cmd.arg("-Wl,-O1"); + } + } + + fn no_default_libraries(&mut self) { + // Unfortunately right now passing -nodefaultlibs to gcc on windows + // doesn't work so hot (in terms of native dependencies). This if + // statement should hopefully be removed one day though! + if !self.sess.target.target.options.is_like_windows { + self.cmd.arg("-nodefaultlibs"); + } + } + + fn build_dylib(&mut self, out_filename: &Path) { + // On mac we need to tell the linker to let this library be rpathed + if self.sess.target.target.options.is_like_osx { + self.cmd.args(&["-dynamiclib", "-Wl,-dylib"]); + + if self.sess.opts.cg.rpath { + let mut v = OsString::from("-Wl,-install_name,@rpath/"); + v.push(out_filename.file_name().unwrap()); + self.cmd.arg(&v); + } + } else { + self.cmd.arg("-shared"); + } + } + + fn whole_archives(&mut self) { + if !self.takes_hints() { return } + self.cmd.arg("-Wl,--whole-archive"); + } + + fn no_whole_archives(&mut self) { + if !self.takes_hints() { return } + self.cmd.arg("-Wl,--no-whole-archive"); + } + + fn hint_static(&mut self) { + if !self.takes_hints() { return } + self.cmd.arg("-Wl,-Bstatic"); + } + + fn hint_dynamic(&mut self) { + if !self.takes_hints() { return } + self.cmd.arg("-Wl,-Bdynamic"); + } +} diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index 3e2db80a9c556..2a823c69276f6 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -74,6 +74,7 @@ pub mod back { pub use rustc_back::x86; pub use rustc_back::x86_64; + pub mod linker; pub mod link; pub mod lto; pub mod write; From 40570eb49eb1cb688637cb58d14cdb9664ea1039 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 11 May 2015 12:24:12 -0700 Subject: [PATCH 04/31] rustc_llvm: Expose setting more DLL storage classes Currently only `dllexport` is used, but more integration will require using `dllimport` as well. --- src/librustc_llvm/lib.rs | 19 ++++++++++++++++++- src/rustllvm/RustWrapper.cpp | 5 +++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs index 2a2aa2bf4cf93..54031e4b04d25 100644 --- a/src/librustc_llvm/lib.rs +++ b/src/librustc_llvm/lib.rs @@ -55,6 +55,7 @@ pub use self::CallConv::*; pub use self::Visibility::*; pub use self::DiagnosticSeverity::*; pub use self::Linkage::*; +pub use self::DLLStorageClassTypes::*; use std::ffi::CString; use std::cell::RefCell; @@ -123,6 +124,15 @@ pub enum DiagnosticSeverity { Note, } + +#[repr(C)] +#[derive(Copy, Clone)] +pub enum DLLStorageClassTypes { + DefaultStorageClass = 0, + DLLImportStorageClass = 1, + DLLExportStorageClass = 2, +} + bitflags! { flags Attribute : u32 { const ZExt = 1 << 0, @@ -2075,7 +2085,8 @@ extern { pub fn LLVMRustArchiveIteratorFree(AIR: ArchiveIteratorRef); pub fn LLVMRustDestroyArchive(AR: ArchiveRef); - pub fn LLVMRustSetDLLExportStorageClass(V: ValueRef); + pub fn LLVMRustSetDLLStorageClass(V: ValueRef, + C: DLLStorageClassTypes); pub fn LLVMRustGetSectionName(SI: SectionIteratorRef, data: *mut *const c_char) -> c_int; @@ -2125,6 +2136,12 @@ pub fn SetLinkage(global: ValueRef, link: Linkage) { } } +pub fn SetDLLStorageClass(global: ValueRef, class: DLLStorageClassTypes) { + unsafe { + LLVMRustSetDLLStorageClass(global, class); + } +} + pub fn SetUnnamedAddr(global: ValueRef, unnamed: bool) { unsafe { LLVMSetUnnamedAddr(global, unnamed as Bool); diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index 375c5fc746c5b..5a2d1ec42071f 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -837,9 +837,10 @@ LLVMRustArchiveChildData(Archive::Child *child, size_t *size) { } extern "C" void -LLVMRustSetDLLExportStorageClass(LLVMValueRef Value) { +LLVMRustSetDLLStorageClass(LLVMValueRef Value, + GlobalValue::DLLStorageClassTypes Class) { GlobalValue *V = unwrap(Value); - V->setDLLStorageClass(GlobalValue::DLLExportStorageClass); + V->setDLLStorageClass(Class); } // Note that the two following functions look quite similar to the From cb513c7b46c21c786c30901e70b62de2835a5687 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 11 May 2015 12:24:56 -0700 Subject: [PATCH 05/31] rt: Clean up to build with cl.exe * Detect the #define _MSC_VER in addition to __WIN32__ * Don't include valgrind.h for windows * Remove unused `rust_valgrind_stack_{un,}register` functions * Add stub definition for `rust_running_on_valgrind` for windows * Conditionally define `rust_dbg_extern_empty_struct` as empty structures are not allowed by cl.exe apparently. --- src/rt/rust_builtin.c | 27 ++++++++++++--------------- src/rt/rust_test_helpers.c | 3 +++ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/rt/rust_builtin.c b/src/rt/rust_builtin.c index 362439c146912..5939a5a3c90f5 100644 --- a/src/rt/rust_builtin.c +++ b/src/rt/rust_builtin.c @@ -14,7 +14,8 @@ #include #include -#if !defined(__WIN32__) + +#if !defined(_WIN32) #include #include #include @@ -40,7 +41,9 @@ /* Foreign builtins. */ //include valgrind.h after stdint.h so that uintptr_t is defined for msys2 w64 +#ifndef _WIN32 #include "valgrind/valgrind.h" +#endif #ifndef _WIN32 char* @@ -84,12 +87,7 @@ rust_dirent_t_size() { } #endif -uintptr_t -rust_running_on_valgrind() { - return RUNNING_ON_VALGRIND; -} - -#if defined(__WIN32__) +#if defined(_WIN32) int get_num_cpus() { SYSTEM_INFO sysinfo; @@ -136,14 +134,13 @@ rust_get_num_cpus() { return get_num_cpus(); } -unsigned int -rust_valgrind_stack_register(void *start, void *end) { - return VALGRIND_STACK_REGISTER(start, end); -} - -void -rust_valgrind_stack_deregister(unsigned int id) { - VALGRIND_STACK_DEREGISTER(id); +uintptr_t +rust_running_on_valgrind() { +#ifdef _WIN32 + return 0; +#else + return RUNNING_ON_VALGRIND; +#endif } #if defined(__DragonFly__) diff --git a/src/rt/rust_test_helpers.c b/src/rt/rust_test_helpers.c index ac925e4260a54..8824cef2a816c 100644 --- a/src/rt/rust_test_helpers.c +++ b/src/rt/rust_test_helpers.c @@ -135,6 +135,8 @@ struct ManyInts { struct TwoU8s arg6; }; +// MSVC doesn't allow empty structs or unions +#ifndef _MSC_VER struct Empty { }; @@ -148,6 +150,7 @@ rust_dbg_extern_empty_struct(struct ManyInts v1, struct Empty e, struct ManyInts assert(v1.arg6.one == v2.arg6.one + 1); assert(v1.arg6.two == v2.arg6.two + 1); } +#endif intptr_t rust_get_test_int() { From eb5bf151a57a6d499ddc9ec7591d0f0648c14330 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 11 May 2015 12:27:26 -0700 Subject: [PATCH 06/31] mk: Remove generation of .d files Looks like cl.exe doesn't support this and we're also barely using them anyway as we have very few header files and C code in general. --- Makefile.in | 5 ----- mk/main.mk | 4 ---- mk/platform.mk | 10 ++-------- 3 files changed, 2 insertions(+), 17 deletions(-) diff --git a/Makefile.in b/Makefile.in index 8fc66a764da61..d3bb5a541a470 100644 --- a/Makefile.in +++ b/Makefile.in @@ -266,8 +266,3 @@ ifneq ($(strip $(findstring TAGS.emacs,$(MAKECMDGOALS)) \ CFG_INFO := $(info cfg: including ctags rules) include $(CFG_SRC_DIR)mk/ctags.mk endif - -# Find all of the .d files and include them to add information about -# header file dependencies. -ALL_DEP_FILES := $(ALL_OBJ_FILES:%.o=%.d) --include $(ALL_DEP_FILES) diff --git a/mk/main.mk b/mk/main.mk index 738580cb5b5e0..44e6537d863c2 100644 --- a/mk/main.mk +++ b/mk/main.mk @@ -85,10 +85,6 @@ CFG_INFO := $(info cfg: version $(CFG_VERSION)) # More configuration ###################################################################### -# We track all of the object files we might build so that we can find -# and include all of the .d files in one fell swoop. -ALL_OBJ_FILES := - MKFILE_DEPS := config.stamp $(call rwildcard,$(CFG_SRC_DIR)mk/,*) MKFILES_FOR_TARBALL:=$(MKFILE_DEPS) ifneq ($(NO_MKFILE_DEPS),) diff --git a/mk/platform.mk b/mk/platform.mk index f66e451f2367c..2643c3d115d6c 100644 --- a/mk/platform.mk +++ b/mk/platform.mk @@ -90,10 +90,6 @@ ifneq ($(findstring linux,$(CFG_OSTYPE)),) endif endif -# These flags will cause the compiler to produce a .d file -# next to the .o file that lists header deps. -CFG_DEPEND_FLAGS = -MMD -MP -MT $(1) -MF $(1:%.o=%.d) - AR := ar define SET_FROM_CFG @@ -159,7 +155,6 @@ define CFG_MAKE_TOOLCHAIN CFG_COMPILE_C_$(1) = $$(CC_$(1)) \ $$(CFG_GCCISH_CFLAGS) \ $$(CFG_GCCISH_CFLAGS_$(1)) \ - $$(CFG_DEPEND_FLAGS) \ -c -o $$(1) $$(2) CFG_LINK_C_$(1) = $$(CC_$(1)) \ $$(CFG_GCCISH_LINK_FLAGS) -o $$(1) \ @@ -171,7 +166,6 @@ define CFG_MAKE_TOOLCHAIN $$(CFG_GCCISH_CXXFLAGS) \ $$(CFG_GCCISH_CFLAGS_$(1)) \ $$(CFG_GCCISH_CXXFLAGS_$(1)) \ - $$(CFG_DEPEND_FLAGS) \ -c -o $$(1) $$(2) CFG_LINK_CXX_$(1) = $$(CXX_$(1)) \ $$(CFG_GCCISH_LINK_FLAGS) -o $$(1) \ @@ -190,7 +184,7 @@ define CFG_MAKE_TOOLCHAIN # We're using llvm-mc as our assembler because it supports # .cfi pseudo-ops on mac - CFG_ASSEMBLE_$(1)=$$(CPP_$(1)) -E $$(CFG_DEPEND_FLAGS) $$(2) | \ + CFG_ASSEMBLE_$(1)=$$(CPP_$(1)) -E $$(2) | \ $$(LLVM_MC_$$(CFG_BUILD)) \ -assemble \ -relocation-model=$$(LLVM_MC_RELOCATION_MODEL) \ @@ -202,7 +196,7 @@ define CFG_MAKE_TOOLCHAIN # For the ARM, AARCH64, MIPS and POWER crosses, use the toolchain assembler # FIXME: We should be able to use the LLVM assembler CFG_ASSEMBLE_$(1)=$$(CC_$(1)) $$(CFG_GCCISH_CFLAGS_$(1)) \ - $$(CFG_DEPEND_FLAGS) $$(2) -c -o $$(1) + $$(2) -c -o $$(1) endif From 150663c3b6ab2628be08a5c48d98f17a2f9e6e7e Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 11 May 2015 12:29:08 -0700 Subject: [PATCH 07/31] mk: Correct names of installed libs on windows Previously libmorestack.a and libcompiler-rt.a were installed, but link.exe looks for morestack.lib and compiler-rt.lib by default, so we need to install these with the correct name --- mk/crates.mk | 3 --- mk/main.mk | 2 -- mk/platform.mk | 8 ++++++++ mk/prepare.mk | 2 +- mk/rt.mk | 7 +------ 5 files changed, 10 insertions(+), 12 deletions(-) diff --git a/mk/crates.mk b/mk/crates.mk index 546b16c1b850b..62dc1019066f0 100644 --- a/mk/crates.mk +++ b/mk/crates.mk @@ -125,9 +125,6 @@ ONLY_RLIB_rustc_bitflags := 1 # Documented-by-default crates DOC_CRATES := std alloc collections core libc rustc_unicode -# Installed objects/libraries by default -INSTALLED_OBJECTS := libmorestack.a libcompiler-rt.a - ################################################################################ # You should not need to edit below this line ################################################################################ diff --git a/mk/main.mk b/mk/main.mk index 44e6537d863c2..a3e34bfeedbce 100644 --- a/mk/main.mk +++ b/mk/main.mk @@ -396,8 +396,6 @@ endif # Prerequisites for using the stageN compiler to build target artifacts TSREQ$(1)_T_$(2)_H_$(3) = \ $$(HSREQ$(1)_H_$(3)) \ - $$(foreach obj,$$(INSTALLED_OBJECTS),\ - $$(TLIB$(1)_T_$(2)_H_$(3))/$$(obj)) \ $$(foreach obj,$$(INSTALLED_OBJECTS_$(2)),\ $$(TLIB$(1)_T_$(2)_H_$(3))/$$(obj)) diff --git a/mk/platform.mk b/mk/platform.mk index 2643c3d115d6c..8e7e61c92a487 100644 --- a/mk/platform.mk +++ b/mk/platform.mk @@ -112,6 +112,14 @@ CFG_RLIB_GLOB=lib$(1)-*.rlib include $(wildcard $(CFG_SRC_DIR)mk/cfg/*.mk) +define ADD_INSTALLED_OBJECTS + INSTALLED_OBJECTS_$(1) += $$(call CFG_STATIC_LIB_NAME_$(1),morestack) \ + $$(call CFG_STATIC_LIB_NAME_$(1),compiler-rt) +endef + +$(foreach target,$(CFG_TARGET), \ + $(eval $(call ADD_INSTALLED_OBJECTS,$(target)))) + # The -Qunused-arguments sidesteps spurious warnings from clang define FILTER_FLAGS ifeq ($$(CFG_USING_CLANG),1) diff --git a/mk/prepare.mk b/mk/prepare.mk index 1ae6a61c95a8c..a7f5a489dc5b4 100644 --- a/mk/prepare.mk +++ b/mk/prepare.mk @@ -140,7 +140,7 @@ prepare-target-$(2)-host-$(3)-$(1)-$(4): prepare-maybe-clean-$(4) \ $$(if $$(findstring $(2),$$(CFG_HOST)), \ $$(foreach crate,$$(HOST_CRATES), \ $$(call PREPARE_LIB,$$(call CFG_LIB_GLOB_$(2),$$(crate)))),) \ - $$(foreach object,$$(INSTALLED_OBJECTS) $$(INSTALLED_OBJECTS_$(2)),\ + $$(foreach object,$$(INSTALLED_OBJECTS_$(2)),\ $$(call PREPARE_LIB,$$(object))),),),) endef diff --git a/mk/rt.mk b/mk/rt.mk index bd6578d3b724b..c913fe385d629 100644 --- a/mk/rt.mk +++ b/mk/rt.mk @@ -111,11 +111,6 @@ $$(RT_OUTPUT_DIR_$(1))/$$(NATIVE_$(2)_$(1)): $$(OBJS_$(2)_$(1)) @$$(call E, link: $$@) $$(Q)$$(AR_$(1)) rcs $$@ $$^ -ifeq ($$(findstring windows,$(1)),windows) -$$(RT_OUTPUT_DIR_$(1))/lib$(2).a: $$(RT_OUTPUT_DIR_$(1))/$$(NATIVE_$(2)_$(1)) - $$(Q)cp $$^ $$@ -endif - endef $(foreach target,$(CFG_TARGET), \ @@ -227,7 +222,7 @@ COMPRT_DEPS := $(wildcard \ $(S)src/compiler-rt/*/*/*/*) endif -COMPRT_NAME_$(1) := libcompiler-rt.a +COMPRT_NAME_$(1) := $$(call CFG_STATIC_LIB_NAME_$(1),compiler-rt) COMPRT_LIB_$(1) := $$(RT_OUTPUT_DIR_$(1))/$$(COMPRT_NAME_$(1)) COMPRT_BUILD_DIR_$(1) := $$(RT_OUTPUT_DIR_$(1))/compiler-rt From ee258c548f0303b8d1e315b0c9cb54fa59ad25d1 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 11 May 2015 12:30:47 -0700 Subject: [PATCH 08/31] mk: Fix native LLVM deps for cross-host builds We use a script called `mklldeps.py` to run `llvm-config` to generate a list of LLVM libraries and native dependencies needed by LLVM, but all cross-compiled LLVM builds were using the *host triple's* `llvm-config` instead of the *target's* `llvm-config`. This commit alters this to require the right `llvmdeps.rs` to be generated which will run the correct `llvm-config`. --- mk/llvm.mk | 2 +- mk/target.mk | 2 +- mk/tests.mk | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mk/llvm.mk b/mk/llvm.mk index 1861dd313ce71..cce1cab396836 100644 --- a/mk/llvm.mk +++ b/mk/llvm.mk @@ -66,7 +66,7 @@ $(foreach host,$(CFG_HOST), \ # This can't be done in target.mk because it's included before this file. define LLVM_LINKAGE_DEPS -$$(TLIB$(1)_T_$(2)_H_$(3))/stamp.rustc_llvm: $$(LLVM_LINKAGE_PATH_$(3)) +$$(TLIB$(1)_T_$(2)_H_$(3))/stamp.rustc_llvm: $$(LLVM_LINKAGE_PATH_$(2)) endef $(foreach source,$(CFG_HOST), \ diff --git a/mk/target.mk b/mk/target.mk index 319f44fd35b77..97b08ebb0339c 100644 --- a/mk/target.mk +++ b/mk/target.mk @@ -83,7 +83,7 @@ $$(TLIB$(1)_T_$(2)_H_$(3))/stamp.$(4): \ $$(dir $$@)$$(call CFG_LIB_GLOB_$(2),$(4))) $$(call REMOVE_ALL_OLD_GLOB_MATCHES, \ $$(dir $$@)$$(call CFG_RLIB_GLOB,$(4))) - $(Q)CFG_LLVM_LINKAGE_FILE=$$(LLVM_LINKAGE_PATH_$(3)) \ + $(Q)CFG_LLVM_LINKAGE_FILE=$$(LLVM_LINKAGE_PATH_$(2)) \ $$(subst @,,$$(STAGE$(1)_T_$(2)_H_$(3))) \ $$(RUST_LIB_FLAGS_ST$(1)) \ -L "$$(RT_OUTPUT_DIR_$(2))" \ diff --git a/mk/tests.mk b/mk/tests.mk index f391d8555fc2b..44c661c4e20ea 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -382,7 +382,7 @@ $(3)/stage$(1)/test/$(4)test-$(2)$$(X_$(2)): \ $$(CRATEFILE_$(4)) \ $$(TESTDEP_$(1)_$(2)_$(3)_$(4)) @$$(call E, rustc: $$@) - $(Q)CFG_LLVM_LINKAGE_FILE=$$(LLVM_LINKAGE_PATH_$(3)) \ + $(Q)CFG_LLVM_LINKAGE_FILE=$$(LLVM_LINKAGE_PATH_$(2)) \ $$(subst @,,$$(STAGE$(1)_T_$(2)_H_$(3))) -o $$@ $$< --test \ -L "$$(RT_OUTPUT_DIR_$(2))" \ $$(LLVM_LIBDIR_RUSTFLAGS_$(2)) \ @@ -894,7 +894,7 @@ ifeq ($(2),$$(CFG_BUILD)) $$(call TEST_OK_FILE,$(1),$(2),$(3),doc-crate-$(4)): $$(CRATEDOCTESTDEP_$(1)_$(2)_$(3)_$(4)) @$$(call E, run doc-crate-$(4) [$(2)]) $$(Q)touch $$@.start_time - $$(Q)CFG_LLVM_LINKAGE_FILE=$$(LLVM_LINKAGE_PATH_$(3)) \ + $$(Q)CFG_LLVM_LINKAGE_FILE=$$(LLVM_LINKAGE_PATH_$(2)) \ $$(RUSTDOC_$(1)_T_$(2)_H_$(3)) --test --cfg dox \ $$(CRATEFILE_$(4)) --test-args "$$(TESTARGS)" && \ touch -r $$@.start_time $$@ && rm $$@.start_time From 7cf0b1798bddad33876258d6715b363896252e40 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 11 May 2015 13:59:51 -0700 Subject: [PATCH 09/31] configure: Start adding MSVC support This commit starts to add MSVC support to the ./configure script to enable the build system to detect and build an MSVC target with the cl.exe compiler and toolchain. The primary change here is a large sanity check when an MSVC target is requested (and currently only `x86_64-pc-windows-msvc` is recognized). When building an MSVC target, the configure script either requires the `--msvc-root` argument or for `cl.exe` to be in `PATH`. It also requires that if in the path `cl.exe` is the 64-bit version of the compiler. Once detected the configure script will run the `vcvarsall.bat` script provided by Visual Studio to learn about the `INCLUDE` and `LIB` variables needed by the `cl.exe` compiler to run (the default include/lib paths for the compiler/linker). These variables are then reexported when running `make` to ensure that our own compiles are running the same toolchain. The purpose of this detection and environment variable scraping is to avoid requiring the build itself to be run inside of a `cmd.exe` shell but rather allow it to run in the currently expected MinGW/MSYS shell. --- configure | 65 +++++++++++++++++++++++++++++--- mk/cfg/x86_64-pc-windows-msvc.mk | 29 +++++++++++--- 2 files changed, 83 insertions(+), 11 deletions(-) diff --git a/configure b/configure index 4e150488224b8..b57a2f0fbefe4 100755 --- a/configure +++ b/configure @@ -1084,6 +1084,65 @@ do err "musl libc $CFG_MUSL_ROOT/lib/libc.a not found" fi ;; + + x86_64-*-msvc) + # Currently the build system is not configured to build jemalloc + # with MSVC, so we omit this optional dependency. + step_msg "targeting MSVC, disabling jemalloc" + CFG_DISABLE_JEMALLOC=1 + putvar CFG_DISABLE_JEMALLOC + + # There are some MSYS python builds which will auto-translate + # windows-style paths to MSYS-style paths in Python itself. + # Unfortunately this breaks LLVM's build system as somewhere along + # the line LLVM prints a path into a file from Python and then CMake + # later tries to interpret that path. If Python prints a MSYS path + # and CMake tries to use it as a Windows path, you're gonna have a + # Bad Time. + # + # Consequently here we try to detect when that happens and print an + # error if it does. + if $CFG_PYTHON -c 'import sys; print sys.argv[1]' `pwd` | grep '^/' + then + err "python is silently translating windows paths to MSYS paths \ + and the build will fail if this python is used.\n\n \ + Either an official python install must be used or an \ + alternative python package in MinGW must be used." + fi + + # MSVC requires cmake because that's how we're going to build LLVM + probe_need CFG_CMAKE cmake + + # Use the REG program to figure out where VS is installed + # We need to figure out where cl.exe and link.exe are, so we do some + # munging and some probing here. We also look for the default + # INCLUDE and LIB variables for MSVC so we can set those in the + # build system as well. + install=$(reg QUERY \ + 'HKLM\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\12.0' \ + -v InstallDir) + need_ok "couldn't find visual studio install root" + CFG_MSVC_ROOT=$(echo "$install" | grep InstallDir | sed 's/.*REG_SZ[ ]*//') + CFG_MSVC_ROOT=$(dirname "$CFG_MSVC_ROOT") + CFG_MSVC_ROOT=$(dirname "$CFG_MSVC_ROOT") + CFG_MSVC_CL="${CFG_MSVC_ROOT}/VC/bin/amd64/cl.exe" + CFG_MSVC_LIB="${CFG_MSVC_ROOT}/VC/bin/amd64/lib.exe" + CFG_MSVC_LINK="${CFG_MSVC_ROOT}/VC/bin/amd64/link.exe" + + vcvarsall="${CFG_MSVC_ROOT}/VC/vcvarsall.bat" + CFG_MSVC_INCLUDE_PATH=$(cmd /c "\"$vcvarsall\" amd64 && cmd /c echo %INCLUDE%") + need_ok "failed to learn about MSVC's INCLUDE" + CFG_MSVC_LIB_PATH=$(cmd /c "\"$vcvarsall\" amd64 && cmd /c echo %LIB%") + need_ok "failed to learn about MSVC's LIB" + + putvar CFG_MSVC_ROOT + putvar CFG_MSVC_CL + putvar CFG_MSVC_LIB + putvar CFG_MSVC_LINK + putvar CFG_MSVC_INCLUDE_PATH + putvar CFG_MSVC_LIB_PATH + ;; + *) ;; esac @@ -1125,6 +1184,7 @@ do do make_dir $t/rt/stage$s make_dir $t/rt/jemalloc + make_dir $t/rt/compiler-rt for i in \ isaac sync test \ arch/i386 arch/x86_64 arch/arm arch/aarch64 arch/mips arch/powerpc @@ -1496,11 +1556,6 @@ do putvar $CFG_LLVM_INST_DIR done -# Munge any paths that appear in config.mk back to posix-y -cp config.tmp config.tmp.bak -sed -e 's@ \([a-zA-Z]\):[/\\]@ /\1/@g;' config.tmp -rm -f config.tmp.bak - msg copy_if_changed ${CFG_SRC_DIR}Makefile.in ./Makefile move_if_changed config.tmp config.mk diff --git a/mk/cfg/x86_64-pc-windows-msvc.mk b/mk/cfg/x86_64-pc-windows-msvc.mk index 4a77b658d46fb..4e97ae3abe11c 100644 --- a/mk/cfg/x86_64-pc-windows-msvc.mk +++ b/mk/cfg/x86_64-pc-windows-msvc.mk @@ -1,9 +1,9 @@ # x86_64-pc-windows-msvc configuration -CC_x86_64-pc-windows-msvc=cl -LINK_x86_64-pc-windows-msvc=link -CXX_x86_64-pc-windows-msvc=cl -CPP_x86_64-pc-windows-msvc=cl -AR_x86_64-pc-windows-msvc=llvm-ar +CC_x86_64-pc-windows-msvc="$(CFG_MSVC_CL)" -nologo +LINK_x86_64-pc-windows-msvc="$(CFG_MSVC_LINK)" -nologo +CXX_x86_64-pc-windows-msvc="$(CFG_MSVC_CL)" -nologo +CPP_x86_64-pc-windows-msvc="$(CFG_MSVC_CL)" -nologo +AR_x86_64-pc-windows-msvc="$(CFG_MSVC_LIB)" -nologo 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 @@ -21,4 +21,21 @@ CFG_UNIXY_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-pc-windows-msvc +CFG_GNU_TRIPLE_x86_64-pc-windows-msvc := x86_64-pc-win32 + +# These two environment variables are scraped by the `./configure` script and +# are necessary for `cl.exe` to find standard headers (the INCLUDE variable) and +# for `link.exe` to find standard libraries (the LIB variable). +ifdef CFG_MSVC_INCLUDE_PATH +export INCLUDE := $(CFG_MSVC_INCLUDE_PATH) +endif +ifdef CFG_MSVC_LIB_PATH +export LIB := $(CFG_MSVC_LIB_PATH) +endif + +# Unfortunately `link.exe` is also a program in `/usr/bin` on MinGW installs, +# but it's not the one that we want. As a result we make sure that our detected +# `link.exe` shows up in PATH first. +ifdef CFG_MSVC_LINK +export PATH := $(CFG_MSVC_ROOT)/VC/bin/amd64:$(PATH) +endif From b56d47cc80a7df471c0e2f96fa62a3e3983972ec Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 11 May 2015 14:03:45 -0700 Subject: [PATCH 10/31] mk: Enable building LLVM targeting MSVC This commit modifies the makefiles to enable building LLVM with cmake and Visual Studio to generate an LLVM that targets MSVC. Rust's configure script requires cmake to be installed when targeting MSVC and will configure LLVM with cmake instead of the normal `./configure` script LLVM provides. The build will then run cmake to execute the build instead of the normal `make`. Currently `make clean-llvm` isn't supported on MSVC as I can't figure out how to run a "clean" target for the Visual Studio files. --- configure | 34 +++++++++++++++++++++++++++++++++- mk/clean.mk | 13 ------------- mk/llvm.mk | 26 ++++++++++++++++++++++++++ mk/main.mk | 4 ++++ 4 files changed, 63 insertions(+), 14 deletions(-) diff --git a/configure b/configure index b57a2f0fbefe4..29bddf687b2d2 100755 --- a/configure +++ b/configure @@ -1348,7 +1348,39 @@ do done fi - if [ ${do_reconfigure} -ne 0 ] + use_cmake=0 + case "$t" in + (*-msvc) + use_cmake=1 + ;; + esac + + if [ ${do_reconfigure} -ne 0 ] && [ ${use_cmake} -ne 0 ] + then + msg "configuring LLVM for $t with cmake" + + CMAKE_ARGS="-DLLVM_INCLUDE_TESTS=OFF" + if [ ! -z "$CFG_DISABLE_OPTIMIZE_LLVM" ]; then + CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_BUILD_TYPE=Debug" + else + CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_BUILD_TYPE=Release" + fi + if [ -z "$CFG_ENABLE_LLVM_ASSERTIONS" ] + then + CMAKE_ARGS="$CMAKE_ARGS -DLLVM_ENABLE_ASSERTIONS=OFF" + else + CMAKE_ARGS="$CMAKE_ARGS -DLLVM_ENABLE_ASSERTIONS=ON" + fi + + msg "configuring LLVM with:" + msg "$CMAKE_ARGS" + (cd $LLVM_BUILD_DIR && "$CFG_CMAKE" $CFG_LLVM_SRC_DIR \ + -G "Visual Studio 12 2013 Win64" \ + $CMAKE_ARGS) + need_ok "LLVM cmake configure failed" + fi + + if [ ${do_reconfigure} -ne 0 ] && [ ${use_cmake} -eq 0 ] then # LLVM's configure doesn't recognize the new Windows triples yet gnu_t=$(to_gnu_triple $t) diff --git a/mk/clean.mk b/mk/clean.mk index 5b90d41ceeceb..c04ef89ebc58a 100644 --- a/mk/clean.mk +++ b/mk/clean.mk @@ -118,16 +118,3 @@ $(foreach host, $(CFG_HOST), \ $(eval $(foreach target, $(CFG_TARGET), \ $(eval $(foreach stage, 0 1 2 3, \ $(eval $(call CLEAN_TARGET_STAGE_N,$(stage),$(target),$(host)))))))) - -define DEF_CLEAN_LLVM_HOST -ifeq ($(CFG_LLVM_ROOT),) -clean-llvm$(1): - $$(Q)$$(MAKE) -C $$(CFG_LLVM_BUILD_DIR_$(1)) clean -else -clean-llvm$(1): ; - -endif -endef - -$(foreach host, $(CFG_HOST), \ - $(eval $(call DEF_CLEAN_LLVM_HOST,$(host)))) diff --git a/mk/llvm.mk b/mk/llvm.mk index cce1cab396836..356366bd5c6d7 100644 --- a/mk/llvm.mk +++ b/mk/llvm.mk @@ -19,6 +19,12 @@ LLVM_DEPS_INC=$(call rwildcard,$(CFG_LLVM_SRC_DIR)include,*cpp *hpp) LLVM_DEPS=$(LLVM_DEPS_SRC) $(LLVM_DEPS_INC) endif +ifdef CFG_DISABLE_OPTIMIZE_LLVM +LLVM_BUILD_CONFIG_MODE := Debug +else +LLVM_BUILD_CONFIG_MODE := Release +endif + define DEF_LLVM_RULES # If CFG_LLVM_ROOT is defined then we don't build LLVM ourselves @@ -26,10 +32,30 @@ ifeq ($(CFG_LLVM_ROOT),) LLVM_STAMP_$(1) = $$(CFG_LLVM_BUILD_DIR_$(1))/llvm-auto-clean-stamp +ifeq ($$(findstring msvc,$(1)),msvc) + +$$(LLVM_CONFIG_$(1)): $$(LLVM_DEPS) $$(LLVM_STAMP_$(1)) + @$$(call E, cmake: llvm) + $$(Q)$$(CFG_CMAKE) --build $$(CFG_LLVM_BUILD_DIR_$(1)) \ + --config $$(LLVM_BUILD_CONFIG_MODE) + $$(Q)touch $$(LLVM_CONFIG_$(1)) + +clean-llvm$(1): + +else + $$(LLVM_CONFIG_$(1)): $$(LLVM_DEPS) $$(LLVM_STAMP_$(1)) @$$(call E, make: llvm) $$(Q)$$(MAKE) -C $$(CFG_LLVM_BUILD_DIR_$(1)) $$(CFG_LLVM_BUILD_ENV_$(1)) ONLY_TOOLS="$$(LLVM_TOOLS)" $$(Q)touch $$(LLVM_CONFIG_$(1)) + +clean-llvm$(1): + $$(Q)$$(MAKE) -C $$(CFG_LLVM_BUILD_DIR_$(1)) clean + +endif + +else +clean-llvm$(1): endif # This is used to independently force an LLVM clean rebuild diff --git a/mk/main.mk b/mk/main.mk index a3e34bfeedbce..a70200e4d8a22 100644 --- a/mk/main.mk +++ b/mk/main.mk @@ -299,9 +299,13 @@ LLVM_LIBDIR_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --libdir) LLVM_LIBDIR_RUSTFLAGS_$(1)=-L "$$(LLVM_LIBDIR_$(1))" LLVM_LIBS_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --libs $$(LLVM_COMPONENTS)) LLVM_LDFLAGS_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --ldflags) +ifeq ($$(findstring freebsd,$(1)),freebsd) # On FreeBSD, it may search wrong headers (that are for pre-installed LLVM), # so we replace -I with -iquote to ensure that it searches bundled LLVM first. LLVM_CXXFLAGS_$(1)=$$(subst -I, -iquote , $$(shell "$$(LLVM_CONFIG_$(1))" --cxxflags)) +else +LLVM_CXXFLAGS_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --cxxflags) +endif LLVM_HOST_TRIPLE_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --host-target) LLVM_AS_$(1)=$$(CFG_LLVM_INST_DIR_$(1))/bin/llvm-as$$(X_$(1)) From fcf7ecd1d709c3a4ecc652349763710914271fb0 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 11 May 2015 14:07:42 -0700 Subject: [PATCH 11/31] mk: Add build system support for cl.exe We have a number of support C/C++ files in Rust that we link into the standard library and other various locations, and these all need to be built with cl.exe instead of gcc.exe when targeting MSVC. This commit adds helper macros for this functionality to use different sets of programs/flags/invocations on MSVC than on GNU-like platforms. --- mk/platform.mk | 19 +++++++++++++++++-- mk/rt.mk | 6 +++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/mk/platform.mk b/mk/platform.mk index 8e7e61c92a487..53fb8138916f5 100644 --- a/mk/platform.mk +++ b/mk/platform.mk @@ -133,6 +133,21 @@ endef $(foreach target,$(CFG_TARGET), \ $(eval $(call FILTER_FLAGS,$(target)))) +# Configure various macros to pass gcc or cl.exe style arguments +define CC_MACROS + CFG_CC_INCLUDE_$(1)=-I $$(1) + ifeq ($$(findstring msvc,$(1)),msvc) + CFG_CC_OUTPUT_$(1)=-Fo:$$(1) + CFG_CREATE_ARCHIVE_$(1)=$$(AR_$(1)) -OUT:$$(1) + else + CFG_CC_OUTPUT_$(1)=-o $$(1) + CFG_CREATE_ARCHIVE_$(1)=$$(AR_$(1)) crus $$(1) + endif +endef + +$(foreach target,$(CFG_TARGET), \ + $(eval $(call CC_MACROS,$(target)))) + ifeq ($(CFG_CCACHE_CPP2),1) CCACHE_CPP2=1 @@ -163,7 +178,7 @@ define CFG_MAKE_TOOLCHAIN CFG_COMPILE_C_$(1) = $$(CC_$(1)) \ $$(CFG_GCCISH_CFLAGS) \ $$(CFG_GCCISH_CFLAGS_$(1)) \ - -c -o $$(1) $$(2) + -c $$(call CFG_CC_OUTPUT_$(1),$$(1)) $$(2) CFG_LINK_C_$(1) = $$(CC_$(1)) \ $$(CFG_GCCISH_LINK_FLAGS) -o $$(1) \ $$(CFG_GCCISH_LINK_FLAGS_$(1)) \ @@ -174,7 +189,7 @@ define CFG_MAKE_TOOLCHAIN $$(CFG_GCCISH_CXXFLAGS) \ $$(CFG_GCCISH_CFLAGS_$(1)) \ $$(CFG_GCCISH_CXXFLAGS_$(1)) \ - -c -o $$(1) $$(2) + -c $$(call CFG_CC_OUTPUT_$(1),$$(1)) $$(2) CFG_LINK_CXX_$(1) = $$(CXX_$(1)) \ $$(CFG_GCCISH_LINK_FLAGS) -o $$(1) \ $$(CFG_GCCISH_LINK_FLAGS_$(1)) \ diff --git a/mk/rt.mk b/mk/rt.mk index c913fe385d629..3d3e4c8866c6e 100644 --- a/mk/rt.mk +++ b/mk/rt.mk @@ -81,8 +81,8 @@ $$(RT_OUTPUT_DIR_$(1))/%.o: $(S)src/rt/%.c $$(MKFILE_DEPS) @mkdir -p $$(@D) @$$(call E, compile: $$@) $$(Q)$$(call CFG_COMPILE_C_$(1), $$@, \ - -I $$(S)src/rt/hoedown/src \ - -I $$(S)src/rt \ + $$(call CFG_CC_INCLUDE_$(1),$$(S)src/rt/hoedown/src) \ + $$(call CFG_CC_INCLUDE_$(1),$$(S)src/rt) \ $$(RUNTIME_CFLAGS_$(1))) $$< $$(RT_OUTPUT_DIR_$(1))/%.o: $(S)src/rt/%.S $$(MKFILE_DEPS) \ @@ -109,7 +109,7 @@ OBJS_$(2)_$(1) := $$(OBJS_$(2)_$(1):.S=.o) NATIVE_$(2)_$(1) := $$(call CFG_STATIC_LIB_NAME_$(1),$(2)) $$(RT_OUTPUT_DIR_$(1))/$$(NATIVE_$(2)_$(1)): $$(OBJS_$(2)_$(1)) @$$(call E, link: $$@) - $$(Q)$$(AR_$(1)) rcs $$@ $$^ + $$(Q)$$(call CFG_CREATE_ARCHIVE_$(1),$$@) $$^ endef From ee64bab76cdaa375cc015a5eb3e3fd7581d2a7b3 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 11 May 2015 14:08:46 -0700 Subject: [PATCH 12/31] mk: Don't add cross prefixes for MSVC Currently the MSVC compilers don't have any cross prefixes and we're only able to make an MSVC compiler with a cross compile, so just avoid this logic on msvc for now. --- mk/platform.mk | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mk/platform.mk b/mk/platform.mk index 53fb8138916f5..26a6db1c5bd1a 100644 --- a/mk/platform.mk +++ b/mk/platform.mk @@ -164,6 +164,7 @@ FIND_COMPILER = $(word 1,$(1:ccache=)) define CFG_MAKE_TOOLCHAIN # Prepend the tools with their prefix if cross compiling ifneq ($(CFG_BUILD),$(1)) + ifneq ($$(findstring msvc,$(1)),msvc) CC_$(1)=$(CROSS_PREFIX_$(1))$(CC_$(1)) CXX_$(1)=$(CROSS_PREFIX_$(1))$(CXX_$(1)) CPP_$(1)=$(CROSS_PREFIX_$(1))$(CPP_$(1)) @@ -173,6 +174,7 @@ define CFG_MAKE_TOOLCHAIN -C ar=$$(call FIND_COMPILER,$$(AR_$(1))) $(RUSTC_CROSS_FLAGS_$(1)) RUSTC_FLAGS_$(1)=$$(RUSTC_CROSS_FLAGS_$(1)) $(RUSTC_FLAGS_$(1)) + endif endif CFG_COMPILE_C_$(1) = $$(CC_$(1)) \ From 64412a49bed9d6a743ed4d9108fc2da49ed5e9a9 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 11 May 2015 14:09:25 -0700 Subject: [PATCH 13/31] mk: Fix building compiler-rt on MSVC It looks like compiler-rt has a cmake build sytem inside its source, but I have been unable to figure out how to use it and actually build the right library. For now this commit hard-wires MSVC-targeting builds of libcompiler-rt to continue using `make` as the primary bulid system, but some frobbing of the flags are necessary to ensure that the right compiler is used. --- mk/rt.mk | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/mk/rt.mk b/mk/rt.mk index 3d3e4c8866c6e..777a2a0fd3b4b 100644 --- a/mk/rt.mk +++ b/mk/rt.mk @@ -226,18 +226,32 @@ COMPRT_NAME_$(1) := $$(call CFG_STATIC_LIB_NAME_$(1),compiler-rt) COMPRT_LIB_$(1) := $$(RT_OUTPUT_DIR_$(1))/$$(COMPRT_NAME_$(1)) COMPRT_BUILD_DIR_$(1) := $$(RT_OUTPUT_DIR_$(1))/compiler-rt +# Note that on MSVC-targeting builds we hardwire CC/AR to gcc/ar even though +# we're targeting MSVC. This is because although compiler-rt has a CMake build +# config I can't actually figure out how to use it, so I'm not sure how to use +# cl.exe to build the objects. Additionally, the compiler-rt library when built +# with gcc has the same ABI as cl.exe, so they're largely compatible +COMPRT_CC_$(1) := $$(CC_$(1)) +COMPRT_AR_$(1) := $$(AR_$(1)) +COMPRT_CFLAGS_$(1) := $$(CFG_GCCISH_CFLAGS_$(1)) +ifeq ($$(findstring msvc,$(1)),msvc) +COMPRT_CC_$(1) := gcc +COMPRT_AR_$(1) := ar +COMPRT_CFLAGS_$(1) := $$(CFG_GCCISH_CFLAGS_$(1)) -m64 +endif + $$(COMPRT_LIB_$(1)): $$(COMPRT_DEPS) $$(MKFILE_DEPS) @$$(call E, make: compiler-rt) $$(Q)$$(MAKE) -C "$(S)src/compiler-rt" \ ProjSrcRoot="$(S)src/compiler-rt" \ ProjObjRoot="$$(abspath $$(COMPRT_BUILD_DIR_$(1)))" \ - CC="$$(CC_$(1))" \ - AR="$$(AR_$(1))" \ - RANLIB="$$(AR_$(1)) s" \ - CFLAGS="$$(CFG_GCCISH_CFLAGS_$(1))" \ + CC='$$(COMPRT_CC_$(1))' \ + AR='$$(COMPRT_AR_$(1))' \ + RANLIB='$$(COMPRT_AR_$(1)) s' \ + CFLAGS="$$(COMPRT_CFLAGS_$(1))" \ TargetTriple=$(1) \ triple-builtins - $$(Q)cp $$(COMPRT_BUILD_DIR_$(1))/triple/builtins/libcompiler_rt.a $$(COMPRT_LIB_$(1)) + $$(Q)cp $$(COMPRT_BUILD_DIR_$(1))/triple/builtins/libcompiler_rt.a $$@ ################################################################################ # libbacktrace From 6122a5f559362dad03ed6e2433b8d71cece649c5 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 11 May 2015 14:11:37 -0700 Subject: [PATCH 14/31] mk: Fix MSVC build for rustllvm.lib This commit updates the rustllvm.mk file with the necessary flags and such to build rustllvm.lib with cl.exe instead of gcc. Some comments can be found in the commit itself. --- mk/rustllvm.mk | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/mk/rustllvm.mk b/mk/rustllvm.mk index 363022e8781d0..50d993701421e 100644 --- a/mk/rustllvm.mk +++ b/mk/rustllvm.mk @@ -18,27 +18,41 @@ define DEF_RUSTLLVM_TARGETS # to find the llvm includes (probably because we're not actually installing # llvm, but using it straight out of the build directory) ifdef CFG_WINDOWSY_$(1) -LLVM_EXTRA_INCDIRS_$(1)= -iquote $(S)src/llvm/include \ - -iquote $$(CFG_LLVM_BUILD_DIR_$(1))/include +LLVM_EXTRA_INCDIRS_$(1)= $$(call CFG_CC_INCLUDE_$(1),$(S)src/llvm/include) \ + $$(call CFG_CC_INCLUDE_$(1),\ + $$(CFG_LLVM_BUILD_DIR_$(1))/include) endif RUSTLLVM_OBJS_CS_$(1) := $$(addprefix rustllvm/, \ ExecutionEngineWrapper.cpp RustWrapper.cpp PassWrapper.cpp) RUSTLLVM_INCS_$(1) = $$(LLVM_EXTRA_INCDIRS_$(1)) \ - -iquote $$(LLVM_INCDIR_$(1)) \ - -iquote $$(S)src/rustllvm/include + $$(call CFG_CC_INCLUDE_$(1),$$(LLVM_INCDIR_$(1))) \ + $$(call CFG_CC_INCLUDE_$(1),$$(S)src/rustllvm/include) RUSTLLVM_OBJS_OBJS_$(1) := $$(RUSTLLVM_OBJS_CS_$(1):rustllvm/%.cpp=$(1)/rustllvm/%.o) -ALL_OBJ_FILES += $$(RUSTLLVM_OBJS_OBJS_$(1)) + +# Note that we appease `cl.exe` and its need for some sort of exception +# handling flag with the `EHsc` argument here as well. +ifeq ($$(findstring msvc,$(1)),msvc) +EXTRA_RUSTLLVM_CXXFLAGS_$(1) := //EHsc +endif $$(RT_OUTPUT_DIR_$(1))/$$(call CFG_STATIC_LIB_NAME_$(1),rustllvm): \ $$(RUSTLLVM_OBJS_OBJS_$(1)) @$$(call E, link: $$@) - $$(Q)$$(AR_$(1)) rcs $$@ $$(RUSTLLVM_OBJS_OBJS_$(1)) + $$(Q)$$(call CFG_CREATE_ARCHIVE_$(1),$$@) $$^ +# On MSVC we need to double-escape arguments that llvm-config printed which +# start with a '/'. The shell we're running in will auto-translate the argument +# `/foo` to `C:/msys64/foo` but we really want it to be passed through as `/foo` +# so the argument passed to our shell must be `//foo`. $(1)/rustllvm/%.o: $(S)src/rustllvm/%.cpp $$(MKFILE_DEPS) $$(LLVM_CONFIG_$(1)) @$$(call E, compile: $$@) - $$(Q)$$(call CFG_COMPILE_CXX_$(1), $$@, $$(LLVM_CXXFLAGS_$(1)) $$(RUSTLLVM_INCS_$(1))) $$< + $$(Q)$$(call CFG_COMPILE_CXX_$(1), $$@,) \ + $$(subst /,//,$$(LLVM_CXXFLAGS_$(1))) \ + $$(EXTRA_RUSTLLVM_CXXFLAGS_$(1)) \ + $$(RUSTLLVM_INCS_$(1)) \ + $$< endef # Instantiate template for all stages From a4ef308473f284a93d2d9f32763e09ba7424540b Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 11 May 2015 14:14:11 -0700 Subject: [PATCH 15/31] mk: Add the ability to depend on native LLVM tools The compiler will require that `llvm-ar.exe` be available for MSVC-targeting builds (more comments on this soon), so this commit adds support for targets to depend on LLVM tools. The `core` library for MSVC depends on `llvm-ar.exe` which will be copied into place for the target before the compiler starts to run. Note that these targets all depend on `llvm-config.exe` to ensure that they're built before they're attempted to be copied. --- mk/cfg/x86_64-pc-windows-msvc.mk | 6 ++++++ mk/llvm.mk | 2 ++ mk/main.mk | 1 + mk/target.mk | 9 ++++++++- 4 files changed, 17 insertions(+), 1 deletion(-) diff --git a/mk/cfg/x86_64-pc-windows-msvc.mk b/mk/cfg/x86_64-pc-windows-msvc.mk index 4e97ae3abe11c..209c53f045203 100644 --- a/mk/cfg/x86_64-pc-windows-msvc.mk +++ b/mk/cfg/x86_64-pc-windows-msvc.mk @@ -39,3 +39,9 @@ endif ifdef CFG_MSVC_LINK export PATH := $(CFG_MSVC_ROOT)/VC/bin/amd64:$(PATH) endif + +# There are more comments about this available in the target specification for +# Windows MSVC in the compiler, but the gist of it is that we use `llvm-ar.exe` +# instead of `lib.exe` for assembling archives, so we need to inject this custom +# dependency here. +NATIVE_TOOL_DEPS_core_T_x86_64-pc-windows-msvc += llvm-ar.exe diff --git a/mk/llvm.mk b/mk/llvm.mk index 356366bd5c6d7..d5b608e88daf8 100644 --- a/mk/llvm.mk +++ b/mk/llvm.mk @@ -58,6 +58,8 @@ else clean-llvm$(1): endif +$$(LLVM_AR_$(1)): $$(LLVM_CONFIG_$(1)) + # This is used to independently force an LLVM clean rebuild # when we changed something not otherwise captured by builtin # dependencies. In these cases, commit a change that touches diff --git a/mk/main.mk b/mk/main.mk index a70200e4d8a22..bf96d8f660f1c 100644 --- a/mk/main.mk +++ b/mk/main.mk @@ -292,6 +292,7 @@ endif # Any rules that depend on LLVM should depend on LLVM_CONFIG LLVM_CONFIG_$(1):=$$(CFG_LLVM_INST_DIR_$(1))/bin/llvm-config$$(X_$(1)) LLVM_MC_$(1):=$$(CFG_LLVM_INST_DIR_$(1))/bin/llvm-mc$$(X_$(1)) +LLVM_AR_$(1):=$$(CFG_LLVM_INST_DIR_$(1))/bin/llvm-ar$$(X_$(1)) LLVM_VERSION_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --version) LLVM_BINDIR_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --bindir) LLVM_INCDIR_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --includedir) diff --git a/mk/target.mk b/mk/target.mk index 97b08ebb0339c..4359eec8447b2 100644 --- a/mk/target.mk +++ b/mk/target.mk @@ -37,7 +37,9 @@ CRATE_FULLDEPS_$(1)_T_$(2)_H_$(3)_$(4) := \ $$(foreach dep,$$(NATIVE_DEPS_$(4)), \ $$(RT_OUTPUT_DIR_$(2))/$$(call CFG_STATIC_LIB_NAME_$(2),$$(dep))) \ $$(foreach dep,$$(NATIVE_DEPS_$(4)_T_$(2)), \ - $$(RT_OUTPUT_DIR_$(2))/$$(dep)) + $$(RT_OUTPUT_DIR_$(2))/$$(dep)) \ + $$(foreach dep,$$(NATIVE_TOOL_DEPS_$(4)_T_$(2)), \ + $$(TBIN$(1)_T_$(3)_H_$(3))/$$(dep)) endef $(foreach host,$(CFG_HOST), \ @@ -149,6 +151,11 @@ $$(TLIB$(1)_T_$(2)_H_$(3))/%: $$(RT_OUTPUT_DIR_$(2))/% \ | $$(TLIB$(1)_T_$(2)_H_$(3))/ $$(SNAPSHOT_RUSTC_POST_CLEANUP) @$$(call E, cp: $$@) $$(Q)cp $$< $$@ + +$$(TBIN$(1)_T_$(2)_H_$(3))/%: $$(CFG_LLVM_INST_DIR_$(2))/bin/% \ + | $$(TBIN$(1)_T_$(2)_H_$(3))/ $$(SNAPSHOT_RUSTC_POST_CLEANUP) + @$$(call E, cp: $$@) + $$(Q)cp $$< $$@ endef $(foreach source,$(CFG_HOST), \ From f9846e902dae169255c2d2b1766e7b9846488a89 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 11 May 2015 14:31:06 -0700 Subject: [PATCH 16/31] rustc: Shorten MSVC metadata section name It looks like section names in objects generated by `link.exe` are limited to at most 8 characters in length, so shorten `.note.rustc` to just `.rustc` --- src/librustc/metadata/loader.rs | 40 ++++++++++++++++++++++---------- src/librustc_driver/lib.rs | 2 +- src/librustc_trans/trans/base.rs | 2 +- 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/src/librustc/metadata/loader.rs b/src/librustc/metadata/loader.rs index 062a156637a35..e8dcd493074bc 100644 --- a/src/librustc/metadata/loader.rs +++ b/src/librustc/metadata/loader.rs @@ -526,8 +526,7 @@ impl<'a> Context<'a> { for (lib, kind) in m { info!("{} reading metadata from: {}", flavor, lib.display()); - let metadata = match get_metadata_section(self.target.options.is_like_osx, - &lib) { + let metadata = match get_metadata_section(self.target, &lib) { Ok(blob) => { if self.crate_matches(blob.as_slice(), &lib) { blob @@ -715,17 +714,19 @@ impl ArchiveMetadata { } // Just a small wrapper to time how long reading metadata takes. -fn get_metadata_section(is_osx: bool, filename: &Path) -> Result { +fn get_metadata_section(target: &Target, filename: &Path) + -> Result { let mut ret = None; let dur = Duration::span(|| { - ret = Some(get_metadata_section_imp(is_osx, filename)); + ret = Some(get_metadata_section_imp(target, filename)); }); info!("reading {:?} => {}ms", filename.file_name().unwrap(), dur.num_milliseconds()); return ret.unwrap();; } -fn get_metadata_section_imp(is_osx: bool, filename: &Path) -> Result { +fn get_metadata_section_imp(target: &Target, filename: &Path) + -> Result { if !filename.exists() { return Err(format!("no such file: '{}'", filename.display())); } @@ -769,7 +770,7 @@ fn get_metadata_section_imp(is_osx: bool, filename: &Path) -> Result Result &'static str { - if is_osx { +pub fn meta_section_name(target: &Target) -> &'static str { + if target.options.is_like_osx { "__DATA,__note.rustc" + } else if target.options.is_like_msvc { + // When using link.exe it was seen that the section name `.note.rustc` + // was getting shortened to `.note.ru`, and according to the PE and COFF + // specification: + // + // > Executable images do not use a string table and do not support + // > section names longer than 8 characters + // + // https://msdn.microsoft.com/en-us/library/windows/hardware/gg463119.aspx + // + // As a result, we choose a slightly shorter name! As to why + // `.note.rustc` works on MinGW, that's another good question... + ".rustc" } else { ".note.rustc" } } -pub fn read_meta_section_name(is_osx: bool) -> &'static str { - if is_osx { +pub fn read_meta_section_name(target: &Target) -> &'static str { + if target.options.is_like_osx { "__note.rustc" + } else if target.options.is_like_msvc { + ".rustc" } else { ".note.rustc" } } // A diagnostic function for dumping crate metadata to an output stream -pub fn list_file_metadata(is_osx: bool, path: &Path, +pub fn list_file_metadata(target: &Target, path: &Path, out: &mut io::Write) -> io::Result<()> { - match get_metadata_section(is_osx, path) { + match get_metadata_section(target, path) { Ok(bytes) => decoder::list_crate_metadata(bytes.as_slice(), out), Err(msg) => { write!(out, "{}\n", msg) diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index a618f4b6ef675..6e0d68ec3e173 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -402,7 +402,7 @@ impl RustcDefaultCalls { &Input::File(ref ifile) => { let path = &(*ifile); let mut v = Vec::new(); - metadata::loader::list_file_metadata(sess.target.target.options.is_like_osx, + metadata::loader::list_file_metadata(&sess.target.target, path, &mut v).unwrap(); println!("{}", String::from_utf8(v).unwrap()); diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 4879975dde695..1bfe2fd0eec95 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -2527,7 +2527,7 @@ pub fn write_metadata(cx: &SharedCrateContext, krate: &ast::Crate) -> Vec { }; unsafe { llvm::LLVMSetInitializer(llglobal, llconst); - let name = loader::meta_section_name(cx.sess().target.target.options.is_like_osx); + let name = loader::meta_section_name(&cx.sess().target.target); let name = CString::new(name).unwrap(); llvm::LLVMSetSection(llglobal, name.as_ptr()) } From 839dcfd5a7220ef513e46280349c1aa09aed20c1 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 11 May 2015 14:32:53 -0700 Subject: [PATCH 17/31] rustc_back: Refactor Archive to better express intent This commit was initially written to target either `ar` or `lib.exe` for MSVC, but it ended up not needing `lib.exe` support after all. I would personally like to refactor this to one day not invoke processes at all (but rather use the `llvm-ar.cpp` file in LLVM as alibrary) so I chose to preserve this refactoring to allow it to be easily done in the future. --- src/librustc_back/archive.rs | 174 +++++++++++++++++------------------ 1 file changed, 87 insertions(+), 87 deletions(-) diff --git a/src/librustc_back/archive.rs b/src/librustc_back/archive.rs index 37d784692fd0e..cd2e27cf58d60 100644 --- a/src/librustc_back/archive.rs +++ b/src/librustc_back/archive.rs @@ -30,16 +30,11 @@ pub struct ArchiveConfig<'a> { pub lib_search_paths: Vec, pub slib_prefix: String, pub slib_suffix: String, - pub maybe_ar_prog: Option + pub ar_prog: String } pub struct Archive<'a> { - handler: &'a ErrorHandler, - dst: PathBuf, - lib_search_paths: Vec, - slib_prefix: String, - slib_suffix: String, - maybe_ar_prog: Option + config: ArchiveConfig<'a>, } /// Helper for adding many files to an archive with a single invocation of @@ -53,47 +48,10 @@ pub struct ArchiveBuilder<'a> { should_update_symbols: bool, } -fn run_ar(handler: &ErrorHandler, maybe_ar_prog: &Option, - args: &str, cwd: Option<&Path>, - paths: &[&Path]) -> Output { - let ar = match *maybe_ar_prog { - Some(ref ar) => &ar[..], - None => "ar" - }; - let mut cmd = Command::new(ar); - - cmd.arg(args).args(paths).stdout(Stdio::piped()).stderr(Stdio::piped()); - debug!("{:?}", cmd); - - match cwd { - Some(p) => { - cmd.current_dir(p); - debug!("inside {:?}", p.display()); - } - None => {} - } - - match cmd.spawn() { - Ok(prog) => { - let o = prog.wait_with_output().unwrap(); - if !o.status.success() { - handler.err(&format!("{:?} failed with: {}", cmd, o.status)); - handler.note(&format!("stdout ---\n{}", - str::from_utf8(&o.stdout).unwrap())); - handler.note(&format!("stderr ---\n{}", - str::from_utf8(&o.stderr).unwrap()) - ); - handler.abort_if_errors(); - } - o - }, - Err(e) => { - handler.err(&format!("could not exec `{}`: {}", &ar[..], - e)); - handler.abort_if_errors(); - panic!("rustc::back::archive::run_ar() should not reach this point"); - } - } +enum Action<'a> { + Remove(&'a Path), + AddObjects(&'a [&'a PathBuf], bool), + UpdateSymbols, } pub fn find_library(name: &str, osprefix: &str, ossuffix: &str, @@ -120,43 +78,89 @@ pub fn find_library(name: &str, osprefix: &str, ossuffix: &str, impl<'a> Archive<'a> { fn new(config: ArchiveConfig<'a>) -> Archive<'a> { - let ArchiveConfig { handler, dst, lib_search_paths, slib_prefix, slib_suffix, - maybe_ar_prog } = config; - Archive { - handler: handler, - dst: dst, - lib_search_paths: lib_search_paths, - slib_prefix: slib_prefix, - slib_suffix: slib_suffix, - maybe_ar_prog: maybe_ar_prog - } + Archive { config: config } } /// Opens an existing static archive pub fn open(config: ArchiveConfig<'a>) -> Archive<'a> { let archive = Archive::new(config); - assert!(archive.dst.exists()); + assert!(archive.config.dst.exists()); archive } /// Removes a file from this archive pub fn remove_file(&mut self, file: &str) { - run_ar(self.handler, &self.maybe_ar_prog, "d", None, &[&self.dst, &Path::new(file)]); + self.run(None, Action::Remove(Path::new(file))); } /// Lists all files in an archive pub fn files(&self) -> Vec { - let output = run_ar(self.handler, &self.maybe_ar_prog, "t", None, &[&self.dst]); - let output = str::from_utf8(&output.stdout).unwrap(); - // use lines_any because windows delimits output with `\r\n` instead of - // just `\n` - output.lines_any().map(|s| s.to_string()).collect() + let archive = match ArchiveRO::open(&self.config.dst) { + Some(ar) => ar, + None => return Vec::new(), + }; + let ret = archive.iter().filter_map(|child| child.name()) + .map(|name| name.to_string()) + .collect(); + return ret; } /// Creates an `ArchiveBuilder` for adding files to this archive. pub fn extend(self) -> ArchiveBuilder<'a> { ArchiveBuilder::new(self) } + + fn run(&self, cwd: Option<&Path>, action: Action) -> Output { + let abs_dst = env::current_dir().unwrap().join(&self.config.dst); + let ar = &self.config.ar_prog; + let mut cmd = Command::new(ar); + cmd.stdout(Stdio::piped()).stderr(Stdio::piped()); + self.prepare_ar_action(&mut cmd, &abs_dst, action); + info!("{:?}", cmd); + + if let Some(p) = cwd { + cmd.current_dir(p); + info!("inside {:?}", p.display()); + } + + let handler = &self.config.handler; + match cmd.spawn() { + Ok(prog) => { + let o = prog.wait_with_output().unwrap(); + if !o.status.success() { + handler.err(&format!("{:?} failed with: {}", cmd, o.status)); + handler.note(&format!("stdout ---\n{}", + str::from_utf8(&o.stdout).unwrap())); + handler.note(&format!("stderr ---\n{}", + str::from_utf8(&o.stderr).unwrap())); + handler.abort_if_errors(); + } + o + }, + Err(e) => { + handler.err(&format!("could not exec `{}`: {}", + self.config.ar_prog, e)); + handler.abort_if_errors(); + panic!("rustc::back::archive::run() should not reach this point"); + } + } + } + + fn prepare_ar_action(&self, cmd: &mut Command, dst: &Path, action: Action) { + match action { + Action::Remove(file) => { + cmd.arg("d").arg(dst).arg(file); + } + Action::AddObjects(objs, update_symbols) => { + cmd.arg(if update_symbols {"crus"} else {"cruS"}) + .arg(dst) + .args(objs); + } + Action::UpdateSymbols => { + cmd.arg("s").arg(dst); + } + } + } } impl<'a> ArchiveBuilder<'a> { @@ -179,10 +183,10 @@ impl<'a> ArchiveBuilder<'a> { /// search in the relevant locations for a library named `name`. pub fn add_native_library(&mut self, name: &str) -> io::Result<()> { let location = find_library(name, - &self.archive.slib_prefix, - &self.archive.slib_suffix, - &self.archive.lib_search_paths, - self.archive.handler); + &self.archive.config.slib_prefix, + &self.archive.config.slib_suffix, + &self.archive.config.lib_search_paths, + self.archive.config.handler); self.add_archive(&location, name, |_| false) } @@ -229,17 +233,13 @@ impl<'a> ArchiveBuilder<'a> { pub fn build(self) -> Archive<'a> { // Get an absolute path to the destination, so `ar` will work even // though we run it from `self.work_dir`. - let abs_dst = env::current_dir().unwrap().join(&self.archive.dst); - assert!(!abs_dst.is_relative()); - let mut args = vec![&*abs_dst]; - let mut total_len = abs_dst.to_string_lossy().len(); + let mut objects = Vec::new(); + let mut total_len = self.archive.config.dst.to_string_lossy().len(); if self.members.is_empty() { - // OSX `ar` does not allow using `r` with no members, but it does - // allow running `ar s file.a` to update symbols only. if self.should_update_symbols { - run_ar(self.archive.handler, &self.archive.maybe_ar_prog, - "s", Some(self.work_dir.path()), &args[..]); + self.archive.run(Some(self.work_dir.path()), + Action::UpdateSymbols); } return self.archive; } @@ -257,24 +257,22 @@ impl<'a> ArchiveBuilder<'a> { // string, not an array of strings.) if total_len + len + 1 > ARG_LENGTH_LIMIT { // Add the archive members seen so far, without updating the - // symbol table (`S`). - run_ar(self.archive.handler, &self.archive.maybe_ar_prog, - "cruS", Some(self.work_dir.path()), &args[..]); + // symbol table. + self.archive.run(Some(self.work_dir.path()), + Action::AddObjects(&objects, false)); - args.clear(); - args.push(&abs_dst); - total_len = abs_dst.to_string_lossy().len(); + objects.clear(); + total_len = self.archive.config.dst.to_string_lossy().len(); } - args.push(member_name); + objects.push(member_name); total_len += len + 1; } // Add the remaining archive members, and update the symbol table if // necessary. - let flags = if self.should_update_symbols { "crus" } else { "cruS" }; - run_ar(self.archive.handler, &self.archive.maybe_ar_prog, - flags, Some(self.work_dir.path()), &args[..]); + self.archive.run(Some(self.work_dir.path()), + Action::AddObjects(&objects, self.should_update_symbols)); self.archive } @@ -305,6 +303,8 @@ impl<'a> ArchiveBuilder<'a> { }; if filename.contains(".SYMDEF") { continue } if skip(filename) { continue } + let filename = Path::new(filename).file_name().unwrap() + .to_str().unwrap(); // An archive can contain files of the same name multiple times, so // we need to be sure to not have them overwrite one another when we From 37659a1803e398a5e374968aee5aabe541632021 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 11 May 2015 14:34:41 -0700 Subject: [PATCH 18/31] rustc_back: Remove unneeded explicit variable This value is the default anyway --- src/librustc_back/target/linux_base.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/librustc_back/target/linux_base.rs b/src/librustc_back/target/linux_base.rs index 823a4a81fa4c1..3ae70ca854bdb 100644 --- a/src/librustc_back/target/linux_base.rs +++ b/src/librustc_back/target/linux_base.rs @@ -13,7 +13,6 @@ use std::default::Default; pub fn opts() -> TargetOptions { TargetOptions { - linker: "cc".to_string(), dynamic_linking: true, executables: true, morestack: true, From af56e2efde5cd82564e32598889d25d798c02722 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 11 May 2015 14:41:27 -0700 Subject: [PATCH 19/31] rustc_back: Tweak the MSVC target spec This change primarily changes the default ar utility used by MSVC-targeting compilers as `llvm-ar`, adding comments along the way as to why. --- src/librustc_back/target/mod.rs | 20 ++++++---- src/librustc_back/target/windows_msvc_base.rs | 38 ++++++++++++++++++- .../target/x86_64_pc_windows_msvc.rs | 5 ++- src/librustc_trans/back/link.rs | 10 ++++- 4 files changed, 60 insertions(+), 13 deletions(-) diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs index a3cb357dc05c2..ceac37513ee8c 100644 --- a/src/librustc_back/target/mod.rs +++ b/src/librustc_back/target/mod.rs @@ -93,6 +93,8 @@ pub struct Target { pub struct TargetOptions { /// Linker to invoke. Defaults to "cc". pub linker: String, + /// Archive utility to use when managing archives. Defaults to "ar". + pub ar: String, /// Linker arguments that are unconditionally passed *before* any /// user-defined libraries. pub pre_link_args: Vec, @@ -154,22 +156,24 @@ pub struct TargetOptions { pub linker_is_gnu: bool, /// Whether the linker support rpaths or not. Defaults to false. pub has_rpath: bool, - /// Whether to disable linking to compiler-rt. Defaults to false, as LLVM will emit references - /// to the functions that compiler-rt provides. + /// Whether to disable linking to compiler-rt. Defaults to false, as LLVM + /// will emit references to the functions that compiler-rt provides. pub no_compiler_rt: bool, - /// Dynamically linked executables can be compiled as position independent if the default - /// relocation model of position independent code is not changed. This is a requirement to take - /// advantage of ASLR, as otherwise the functions in the executable are not randomized and can - /// be used during an exploit of a vulnerability in any code. + /// Dynamically linked executables can be compiled as position independent + /// if the default relocation model of position independent code is not + /// changed. This is a requirement to take advantage of ASLR, as otherwise + /// the functions in the executable are not randomized and can be used + /// during an exploit of a vulnerability in any code. pub position_independent_executables: bool, } impl Default for TargetOptions { - /// Create a set of "sane defaults" for any target. This is still incomplete, and if used for - /// compilation, will certainly not work. + /// Create a set of "sane defaults" for any target. This is still + /// incomplete, and if used for compilation, will certainly not work. fn default() -> TargetOptions { TargetOptions { linker: "cc".to_string(), + ar: "ar".to_string(), pre_link_args: Vec::new(), post_link_args: Vec::new(), cpu: "generic".to_string(), diff --git a/src/librustc_back/target/windows_msvc_base.rs b/src/librustc_back/target/windows_msvc_base.rs index f4f25ea856944..30d74c807352c 100644 --- a/src/librustc_back/target/windows_msvc_base.rs +++ b/src/librustc_back/target/windows_msvc_base.rs @@ -14,7 +14,38 @@ use std::default::Default; pub fn opts() -> TargetOptions { TargetOptions { function_sections: true, - linker: "link".to_string(), + linker: "link.exe".to_string(), + // When taking a look at the value of this `ar` field, one might expect + // `lib.exe` to be the value here! The `lib.exe` program is the default + // tool for managing `.lib` archives on Windows, but unfortunately the + // compiler cannot use it. + // + // To recap, we use `ar` here to manage rlibs (which are just archives). + // LLVM does not expose bindings for modifying archives so we have to + // invoke this utility for write operations (e.g. deleting files, adding + // files, etc). Normally archives only have object files within them, + // but the compiler also uses archives for storing metadata and + // compressed bytecode, so we don't exactly fall within "normal use + // cases". + // + // MSVC's `lib.exe` tool by default will choke when adding a non-object + // file to an archive, which we do on a regular basis, making it + // inoperable for us. Luckily, however, LLVM has already rewritten `ar` + // in the form of `llvm-ar` which is built by default when we build + // LLVM. This tool, unlike `lib.exe`, works just fine with non-object + // files, so we use it instead. + // + // Note that there's a few caveats associated with this: + // + // * This still requires that the *linker* (the consumer of rlibs) will + // ignore non-object files. Thankfully `link.exe` on Windows does + // indeed ignore non-object files in archives. + // * This requires `llvm-ar.exe` to be distributed with the compiler + // itself, but we already make sure of this elsewhere. + // + // Perhaps one day we won't even need this tool at all and we'll just be + // able to make library calls into LLVM! + ar: "llvm-ar.exe".to_string(), dynamic_linking: true, executables: true, dll_prefix: "".to_string(), @@ -25,7 +56,10 @@ pub fn opts() -> TargetOptions { morestack: false, is_like_windows: true, is_like_msvc: true, - pre_link_args: Vec::new(), + pre_link_args: vec![ + "/NOLOGO".to_string(), + "/NXCOMPAT".to_string(), + ], .. Default::default() } diff --git a/src/librustc_back/target/x86_64_pc_windows_msvc.rs b/src/librustc_back/target/x86_64_pc_windows_msvc.rs index aac1afbb97cd6..f7c3ca4b3f6ed 100644 --- a/src/librustc_back/target/x86_64_pc_windows_msvc.rs +++ b/src/librustc_back/target/x86_64_pc_windows_msvc.rs @@ -15,7 +15,10 @@ pub fn target() -> Target { base.cpu = "x86-64".to_string(); Target { - // FIXME: Test this. Copied from linux (#2398) + // This is currently in sync with the specification for + // x86_64-pc-windows-gnu but there's a comment in that file questioning + // whether this is valid or not. Sounds like the two should stay in sync + // at least for now. 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(), diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index e748f8b44cbed..c6a59ff30a6e2 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -365,6 +365,12 @@ pub fn get_cc_prog(sess: &Session) -> String { } } +pub fn get_ar_prog(sess: &Session) -> String { + sess.opts.cg.ar.clone().unwrap_or_else(|| { + sess.target.target.options.ar.clone() + }) +} + pub fn remove(sess: &Session, path: &Path) { match fs::remove_file(path) { Ok(..) => {} @@ -547,7 +553,7 @@ fn link_rlib<'a>(sess: &'a Session, lib_search_paths: 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() + ar_prog: get_ar_prog(sess), }; let mut ab = ArchiveBuilder::create(config); ab.add_file(obj_filename).unwrap(); @@ -1181,7 +1187,7 @@ fn add_upstream_rust_crates(cmd: &mut Linker, sess: &Session, lib_search_paths: 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() + ar_prog: get_ar_prog(sess), }; let mut archive = Archive::open(config); archive.remove_file(&format!("{}.o", name)); From d97b4af15353c488ddd48a7e208906f84f7654c5 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 11 May 2015 14:47:04 -0700 Subject: [PATCH 20/31] mklldeps: Don't link stdc++/c++ on MSVC These libraries don't exist! The linker for MSVC is highly likely to not pass `/NODEFAULTLIB` in which case the right standard library will automatically be selected. --- src/etc/mklldeps.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/etc/mklldeps.py b/src/etc/mklldeps.py index fe9feb3538d15..7a925fa3f3367 100644 --- a/src/etc/mklldeps.py +++ b/src/etc/mklldeps.py @@ -80,10 +80,13 @@ def run(args): assert('stdlib=libc++' not in out) f.write("#[link(name = \"stdc++\", kind = \"static\")]\n") else: + # Note that we use `cfg_attr` here because on MSVC the C++ standard library + # is not c++ or stdc++, but rather the linker takes care of linking the + # right standard library. if 'stdlib=libc++' in out: - f.write("#[link(name = \"c++\")]\n") + f.write("#[cfg_attr(not(target_env = \"msvc\"), link(name = \"c++\"))]\n") else: - f.write("#[link(name = \"stdc++\")]\n") + f.write("#[cfg_attr(not(target_env = \"msvc\"), link(name = \"stdc++\"))]\n") # Attach everything to an extern block f.write("extern {}\n") From e2854b38932e9cc3aa5ac09b55b59845919860c4 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 11 May 2015 14:47:59 -0700 Subject: [PATCH 21/31] libc: Add necessary libraries for MSVC These libs seem to be required by the standard library at least to link successfully! --- src/liblibc/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/liblibc/lib.rs b/src/liblibc/lib.rs index 55934da00a37c..42e9bd9c9a0d3 100644 --- a/src/liblibc/lib.rs +++ b/src/liblibc/lib.rs @@ -150,6 +150,12 @@ extern {} #[link(name = "c", kind = "static")] extern {} +#[cfg(all(windows, target_env = "msvc"))] +#[link(name = "kernel32")] +#[link(name = "shell32")] +#[link(name = "msvcrt")] +extern {} + // libnacl provides functions that require a trip through the IRT to work. // ie: _exit, mmap, nanosleep, etc. Anything that would otherwise require a trip // to the kernel. From eb50ffd2343eef3afcfbf0696d04fcc3fe8a075a Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 11 May 2015 14:56:49 -0700 Subject: [PATCH 22/31] rustc_trans: Clean up some style in back::link * Add some logging here and there * Move some `err` + `abort_if_errors` to just using `fatal` * Clean up some line-lengths --- src/librustc_trans/back/link.rs | 48 +++++++++++++-------------------- 1 file changed, 18 insertions(+), 30 deletions(-) diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index c6a59ff30a6e2..54a0bcd22b765 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -546,6 +546,7 @@ fn link_rlib<'a>(sess: &'a Session, trans: Option<&CrateTranslation>, // None == no metadata/bytecode obj_filename: &Path, out_filename: &Path) -> ArchiveBuilder<'a> { + info!("preparing rlib from {:?} to {:?}", obj_filename, out_filename); let handler = &sess.diagnostic().handler; let config = ArchiveConfig { handler: handler, @@ -560,9 +561,7 @@ fn link_rlib<'a>(sess: &'a Session, for &(ref l, kind) in &*sess.cstore.get_used_libraries().borrow() { match kind { - cstore::NativeStatic => { - ab.add_native_library(&l[..]).unwrap(); - } + cstore::NativeStatic => ab.add_native_library(&l).unwrap(), cstore::NativeFramework | cstore::NativeUnknown => {} } } @@ -613,10 +612,8 @@ fn link_rlib<'a>(sess: &'a Session, }) { Ok(..) => {} Err(e) => { - sess.err(&format!("failed to write {}: {}", - metadata.display(), - e)); - sess.abort_if_errors(); + sess.fatal(&format!("failed to write {}: {}", + metadata.display(), e)); } } ab.add_file(&metadata).unwrap(); @@ -658,9 +655,8 @@ fn link_rlib<'a>(sess: &'a Session, &bc_data_deflated) { Ok(()) => {} Err(e) => { - sess.err(&format!("failed to write compressed bytecode: \ - {}", e)); - sess.abort_if_errors() + sess.fatal(&format!("failed to write compressed \ + bytecode: {}", e)); } }; @@ -794,6 +790,8 @@ fn link_staticlib(sess: &Session, obj_filename: &Path, out_filename: &Path) { // links to all upstream files as well. fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool, obj_filename: &Path, out_filename: &Path) { + info!("preparing dylib? ({}) from {:?} to {:?}", dylib, obj_filename, + out_filename); let tmpdir = TempDir::new("rustc").ok().expect("needs a temp dir"); // The invocations of cc share some flags across platforms @@ -827,7 +825,7 @@ fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool, sess.abort_if_errors(); // Invoke the system linker - debug!("{:?}", &cmd); + info!("{:?}", &cmd); let prog = time(sess.time_passes(), "running linker", (), |()| cmd.output()); match prog { Ok(prog) => { @@ -841,14 +839,11 @@ fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool, sess.note(str::from_utf8(&output[..]).unwrap()); sess.abort_if_errors(); } - debug!("linker stderr:\n{}", String::from_utf8(prog.stderr).unwrap()); - debug!("linker stdout:\n{}", String::from_utf8(prog.stdout).unwrap()); + info!("linker stderr:\n{}", String::from_utf8(prog.stderr).unwrap()); + info!("linker stdout:\n{}", String::from_utf8(prog.stdout).unwrap()); }, Err(e) => { - sess.err(&format!("could not exec the linker `{}`: {}", - pname, - e)); - sess.abort_if_errors(); + sess.fatal(&format!("could not exec the linker `{}`: {}", pname, e)); } } @@ -858,10 +853,7 @@ fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool, 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(); - } + Err(e) => sess.fatal(&format!("failed to run dsymutil: {}", e)), } } } @@ -1157,11 +1149,9 @@ fn add_upstream_rust_crates(cmd: &mut Linker, sess: &Session, match fs::copy(&cratepath, &dst) { Ok(..) => {} Err(e) => { - sess.err(&format!("failed to copy {} to {}: {}", - cratepath.display(), - dst.display(), - e)); - sess.abort_if_errors(); + sess.fatal(&format!("failed to copy {} to {}: {}", + cratepath.display(), + dst.display(), e)); } } // Fix up permissions of the copy, as fs::copy() preserves @@ -1174,10 +1164,8 @@ fn add_upstream_rust_crates(cmd: &mut Linker, sess: &Session, }) { Ok(..) => {} Err(e) => { - sess.err(&format!("failed to chmod {} when preparing \ - for LTO: {}", dst.display(), - e)); - sess.abort_if_errors(); + sess.fatal(&format!("failed to chmod {} when preparing \ + for LTO: {}", dst.display(), e)); } } let handler = &sess.diagnostic().handler; From 181dbd71d6e979215c43356d1b9b72898b649dc4 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 11 May 2015 14:57:21 -0700 Subject: [PATCH 23/31] rustc_trans: Add MSVC linker support This commit adds an implementation of the `Linker` trait which is used to drive MSVC's `link.exe` support. Nothing too surprising here as it's mostly just filling out the necessary tidbits here and there. --- src/librustc_trans/back/link.rs | 13 ++++-- src/librustc_trans/back/linker.rs | 78 +++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+), 5 deletions(-) diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index 54a0bcd22b765..844a0a698677f 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -9,7 +9,7 @@ // except according to those terms. use super::archive::{Archive, ArchiveBuilder, ArchiveConfig, METADATA_FILENAME}; -use super::linker::{Linker, GnuLinker}; +use super::linker::{Linker, GnuLinker, MsvcLinker}; use super::rpath::RPathConfig; use super::rpath; use super::svh::Svh; @@ -805,8 +805,12 @@ fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool, } { - let mut linker = GnuLinker { cmd: &mut cmd, sess: &sess }; - link_args(&mut linker, sess, dylib, tmpdir.path(), + let mut linker = if sess.target.target.options.is_like_msvc { + Box::new(MsvcLinker { cmd: &mut cmd, sess: &sess }) as Box + } else { + Box::new(GnuLinker { cmd: &mut cmd, sess: &sess }) as Box + }; + link_args(&mut *linker, sess, dylib, tmpdir.path(), trans, obj_filename, out_filename); if !sess.target.target.options.no_compiler_rt { linker.link_staticlib("compiler-rt"); @@ -874,9 +878,8 @@ fn link_args(cmd: &mut Linker, let t = &sess.target.target; cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path)); - - cmd.output_filename(out_filename); cmd.add_object(obj_filename); + cmd.output_filename(out_filename); // Stack growth requires statically linking a __morestack function. Note // that this is listed *before* all other libraries. Due to the usage of the diff --git a/src/librustc_trans/back/linker.rs b/src/librustc_trans/back/linker.rs index da1ec29a41e71..1eacec46c87bb 100644 --- a/src/librustc_trans/back/linker.rs +++ b/src/librustc_trans/back/linker.rs @@ -173,3 +173,81 @@ impl<'a> Linker for GnuLinker<'a> { self.cmd.arg("-Wl,-Bdynamic"); } } + +pub struct MsvcLinker<'a> { + pub cmd: &'a mut Command, + pub sess: &'a Session, +} + +impl<'a> Linker for MsvcLinker<'a> { + fn link_rlib(&mut self, lib: &Path) { self.cmd.arg(lib); } + fn add_object(&mut self, path: &Path) { self.cmd.arg(path); } + fn args(&mut self, args: &[String]) { self.cmd.args(args); } + fn build_dylib(&mut self, _out_filename: &Path) { self.cmd.arg("/DLL"); } + fn gc_sections(&mut self, _is_dylib: bool) { self.cmd.arg("/OPT:REF,ICF"); } + + fn link_dylib(&mut self, lib: &str) { + self.cmd.arg(&format!("{}.lib", lib)); + } + fn link_staticlib(&mut self, lib: &str) { + self.cmd.arg(&format!("{}.lib", lib)); + } + + fn position_independent_executable(&mut self) { + // noop + } + + fn no_default_libraries(&mut self) { + // Currently we don't pass the /NODEFAULTLIB flag to the linker on MSVC + // as there's been trouble in the past of linking the C++ standard + // library required by LLVM. This likely needs to happen one day, but + // in general Windows is also a more controlled environment than + // Unix, so it's not necessarily as critical that this be implemented. + // + // Note that there are also some licensing worries about statically + // linking some libraries which require a specific agreement, so it may + // not ever be possible for us to pass this flag. + } + + fn include_path(&mut self, path: &Path) { + let mut arg = OsString::from("/LIBPATH:"); + arg.push(path); + self.cmd.arg(&arg); + } + + fn output_filename(&mut self, path: &Path) { + let mut arg = OsString::from("/OUT:"); + arg.push(path); + self.cmd.arg(&arg); + } + + fn framework_path(&mut self, _path: &Path) { + panic!("frameworks are not supported on windows") + } + fn link_framework(&mut self, _framework: &str) { + panic!("frameworks are not supported on windows") + } + + fn link_whole_staticlib(&mut self, lib: &str, _search_path: &[PathBuf]) { + // not supported? + self.link_staticlib(lib); + } + fn optimize(&mut self) { + // Needs more investigation of `/OPT` arguments + } + fn whole_archives(&mut self) { + // hints not supported? + } + fn no_whole_archives(&mut self) { + // hints not supported? + } + + // On windows static libraries are of the form `foo.lib` and dynamic + // libraries are not linked against directly, but rather through their + // import libraries also called `foo.lib`. As a result there's no + // possibility for a native library to appear both dynamically and + // statically in the same folder so we don't have to worry about hints like + // we do on Unix platforms. + fn hint_static(&mut self) {} + fn hint_dynamic(&mut self) {} +} From ce8b317558a83021b7ea0f40c5719995e234da03 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 11 May 2015 14:58:58 -0700 Subject: [PATCH 24/31] rustc_trans: Tidy up some style and line lengths Match the surrounding style in the rest of the `rustc_trans::trans` module. --- src/librustc_trans/trans/base.rs | 3 +- src/librustc_trans/trans/declare.rs | 98 +++++++++++++++-------------- 2 files changed, 53 insertions(+), 48 deletions(-) diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 1bfe2fd0eec95..4c936ddbcef00 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -670,7 +670,8 @@ pub fn trans_external_path<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ccx.sess().bug("unexpected intrinsic in trans_external_path") } _ => { - let llfn = foreign::register_foreign_item_fn(ccx, fn_ty.abi, t, &name[..]); + let llfn = foreign::register_foreign_item_fn(ccx, fn_ty.abi, + t, &name); let attrs = csearch::get_item_attrs(&ccx.sess().cstore, did); attributes::from_fn_attrs(ccx, &attrs, llfn); llfn diff --git a/src/librustc_trans/trans/declare.rs b/src/librustc_trans/trans/declare.rs index 7849235fdbee5..e0ab5dec98dfe 100644 --- a/src/librustc_trans/trans/declare.rs +++ b/src/librustc_trans/trans/declare.rs @@ -9,14 +9,14 @@ // except according to those terms. //! Declare various LLVM values. //! -//! Prefer using functions and methods from this module rather than calling LLVM functions -//! directly. These functions do some additional work to ensure we do the right thing given -//! the preconceptions of trans. +//! Prefer using functions and methods from this module rather than calling LLVM +//! functions directly. These functions do some additional work to ensure we do +//! the right thing given the preconceptions of trans. //! //! Some useful guidelines: //! -//! * Use declare_* family of methods if you are declaring, but are not interested in defining the -//! ValueRef they return. +//! * Use declare_* family of methods if you are declaring, but are not +//! interested in defining the ValueRef they return. //! * Use define_* family of methods when you might be defining the ValueRef. //! * When in doubt, define. use llvm::{self, ValueRef}; @@ -37,8 +37,8 @@ use libc::c_uint; /// Declare a global value. /// -/// If there’s a value with the same name already declared, the function will return its ValueRef -/// instead. +/// If there’s a value with the same name already declared, the function will +/// return its ValueRef instead. pub fn declare_global(ccx: &CrateContext, name: &str, ty: Type) -> llvm::ValueRef { debug!("declare_global(name={:?})", name); let namebuf = CString::new(name).unwrap_or_else(|_|{ @@ -54,10 +54,10 @@ pub fn declare_global(ccx: &CrateContext, name: &str, ty: Type) -> llvm::ValueRe /// /// For rust functions use `declare_rust_fn` instead. /// -/// If there’s a value with the same name already declared, the function will update the -/// declaration and return existing ValueRef instead. -pub fn declare_fn(ccx: &CrateContext, name: &str, callconv: llvm::CallConv, ty: Type, - output: ty::FnOutput) -> ValueRef { +/// If there’s a value with the same name already declared, the function will +/// update the declaration and return existing ValueRef instead. +pub fn declare_fn(ccx: &CrateContext, name: &str, callconv: llvm::CallConv, + ty: Type, output: ty::FnOutput) -> ValueRef { debug!("declare_fn(name={:?})", name); let namebuf = CString::new(name).unwrap_or_else(|_|{ ccx.sess().bug(&format!("name {:?} contains an interior null byte", name)) @@ -67,7 +67,8 @@ pub fn declare_fn(ccx: &CrateContext, name: &str, callconv: llvm::CallConv, ty: }; llvm::SetFunctionCallConv(llfn, callconv); - // Function addresses in Rust are never significant, allowing functions to be merged. + // Function addresses in Rust are never significant, allowing functions to + // be merged. llvm::SetUnnamedAddr(llfn, true); if output == ty::FnDiverging { @@ -88,23 +89,25 @@ pub fn declare_fn(ccx: &CrateContext, name: &str, callconv: llvm::CallConv, ty: /// Declare a C ABI function. /// -/// Only use this for foreign function ABIs and glue. For Rust functions use `declare_rust_fn` -/// instead. +/// Only use this for foreign function ABIs and glue. For Rust functions use +/// `declare_rust_fn` instead. /// -/// If there’s a value with the same name already declared, the function will update the -/// declaration and return existing ValueRef instead. -pub fn declare_cfn(ccx: &CrateContext, name: &str, fn_type: Type, output: ty::Ty) -> ValueRef { +/// If there’s a value with the same name already declared, the function will +/// update the declaration and return existing ValueRef instead. +pub fn declare_cfn(ccx: &CrateContext, name: &str, fn_type: Type, + output: ty::Ty) -> ValueRef { declare_fn(ccx, name, llvm::CCallConv, fn_type, ty::FnConverging(output)) } /// Declare a Rust function. /// -/// If there’s a value with the same name already declared, the function will update the -/// declaration and return existing ValueRef instead. +/// If there’s a value with the same name already declared, the function will +/// update the declaration and return existing ValueRef instead. pub fn declare_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &str, fn_type: ty::Ty<'tcx>) -> ValueRef { - debug!("declare_rust_fn(name={:?}, fn_type={})", name, fn_type.repr(ccx.tcx())); + debug!("declare_rust_fn(name={:?}, fn_type={})", name, + fn_type.repr(ccx.tcx())); let fn_type = monomorphize::normalize_associated_type(ccx.tcx(), &fn_type); debug!("declare_rust_fn (after normalised associated types) fn_type={}", fn_type.repr(ccx.tcx())); @@ -131,7 +134,8 @@ pub fn declare_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &str, let llfty = type_of::type_of_rust_fn(ccx, env, &sig, abi); debug!("declare_rust_fn llfty={}", ccx.tn().type_to_string(llfty)); - // it is ok to directly access sig.0.output because we erased all late-bound-regions above + // it is ok to directly access sig.0.output because we erased all + // late-bound-regions above let llfn = declare_fn(ccx, name, llvm::CCallConv, llfty, sig.0.output); attributes::from_fn_type(ccx, fn_type).apply_llfn(llfn); llfn @@ -140,8 +144,8 @@ pub fn declare_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &str, /// Declare a Rust function with internal linkage. /// -/// If there’s a value with the same name already declared, the function will update the -/// declaration and return existing ValueRef instead. +/// If there’s a value with the same name already declared, the function will +/// update the declaration and return existing ValueRef instead. pub fn declare_internal_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &str, fn_type: ty::Ty<'tcx>) -> ValueRef { let llfn = declare_rust_fn(ccx, name, fn_type); @@ -152,10 +156,10 @@ pub fn declare_internal_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &s /// Declare a global with an intention to define it. /// -/// Use this function when you intend to define a global. This function will return None if the -/// name already has a definition associated with it. In that case an error should be reported to -/// the user, because it usually happens due to user’s fault (e.g. misuse of #[no_mangle] or -/// #[export_name] attributes). +/// Use this function when you intend to define a global. This function will +/// return None if the name already has a definition associated with it. In that +/// case an error should be reported to the user, because it usually happens due +/// to user’s fault (e.g. misuse of #[no_mangle] or #[export_name] attributes). pub fn define_global(ccx: &CrateContext, name: &str, ty: Type) -> Option { if get_defined_value(ccx, name).is_some() { None @@ -169,10 +173,10 @@ pub fn define_global(ccx: &CrateContext, name: &str, ty: Type) -> Option Option { if get_defined_value(ccx, name).is_some() { @@ -185,13 +189,13 @@ pub fn define_fn(ccx: &CrateContext, name: &str, callconv: llvm::CallConv, fn_ty /// Declare a C ABI function with an intention to define it. /// -/// Use this function when you intend to define a function. This function will return None if the -/// name already has a definition associated with it. In that case an error should be reported to -/// the user, because it usually happens due to user’s fault (e.g. misuse of #[no_mangle] or -/// #[export_name] attributes). +/// Use this function when you intend to define a function. This function will +/// return None if the name already has a definition associated with it. In that +/// case an error should be reported to the user, because it usually happens due +/// to user’s fault (e.g. misuse of #[no_mangle] or #[export_name] attributes). /// -/// Only use this for foreign function ABIs and glue. For Rust functions use `declare_rust_fn` -/// instead. +/// Only use this for foreign function ABIs and glue. For Rust functions use +/// `declare_rust_fn` instead. pub fn define_cfn(ccx: &CrateContext, name: &str, fn_type: Type, output: ty::Ty) -> Option { if get_defined_value(ccx, name).is_some() { @@ -204,10 +208,10 @@ pub fn define_cfn(ccx: &CrateContext, name: &str, fn_type: Type, /// Declare a Rust function with an intention to define it. /// -/// Use this function when you intend to define a function. This function will return None if the -/// name already has a definition associated with it. In that case an error should be reported to -/// the user, because it usually happens due to user’s fault (e.g. misuse of #[no_mangle] or -/// #[export_name] attributes). +/// Use this function when you intend to define a function. This function will +/// return None if the name already has a definition associated with it. In that +/// case an error should be reported to the user, because it usually happens due +/// to user’s fault (e.g. misuse of #[no_mangle] or #[export_name] attributes). pub fn define_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &str, fn_type: ty::Ty<'tcx>) -> Option { if get_defined_value(ccx, name).is_some() { @@ -220,10 +224,10 @@ pub fn define_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &str, /// Declare a Rust function with an intention to define it. /// -/// Use this function when you intend to define a function. This function will return None if the -/// name already has a definition associated with it. In that case an error should be reported to -/// the user, because it usually happens due to user’s fault (e.g. misuse of #[no_mangle] or -/// #[export_name] attributes). +/// Use this function when you intend to define a function. This function will +/// return None if the name already has a definition associated with it. In that +/// case an error should be reported to the user, because it usually happens due +/// to user’s fault (e.g. misuse of #[no_mangle] or #[export_name] attributes). pub fn define_internal_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &str, fn_type: ty::Ty<'tcx>) -> Option { if get_defined_value(ccx, name).is_some() { @@ -250,8 +254,8 @@ fn get_defined_value(ccx: &CrateContext, name: &str) -> Option { (llvm::LLVMIsDeclaration(val) != 0, linkage == llvm::AvailableExternallyLinkage as c_uint) }; - debug!("get_defined_value: found {:?} value (declaration: {}, aext_link: {})", name, - declaration, aext_link); + debug!("get_defined_value: found {:?} value (declaration: {}, \ + aext_link: {})", name, declaration, aext_link); if !declaration || aext_link { Some(val) } else { From b538189ba0abb77658c7b082d2d541daaaa7f80a Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 11 May 2015 15:49:30 -0700 Subject: [PATCH 25/31] mk: Generate a .def file for rustc_llvm on MSVC Windows needs explicit exports of functions from DLLs but LLVM does not mention any of its symbols as being export-able from a DLL. The compiler, however, relies on being able to use LLVM symbols across DLL boundaries so we need to force many of LLVM's symbols to be exported from `rustc_llvm.dll`. This commit adds support for generation of a `rustc_llvm.def` file which is passed along to the linker when generating `rustc_llvm.dll` which should keep all these symbols exportable and usable. --- mk/cfg/x86_64-pc-windows-msvc.mk | 34 ++++++++++++++++++++++++++++++++ mk/target.mk | 4 +++- src/etc/mklldef.py | 25 +++++++++++++++++++++++ 3 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 src/etc/mklldef.py diff --git a/mk/cfg/x86_64-pc-windows-msvc.mk b/mk/cfg/x86_64-pc-windows-msvc.mk index 209c53f045203..1a1f059945a85 100644 --- a/mk/cfg/x86_64-pc-windows-msvc.mk +++ b/mk/cfg/x86_64-pc-windows-msvc.mk @@ -45,3 +45,37 @@ endif # instead of `lib.exe` for assembling archives, so we need to inject this custom # dependency here. NATIVE_TOOL_DEPS_core_T_x86_64-pc-windows-msvc += llvm-ar.exe + +# When working with MSVC on windows, each DLL needs to explicitly declare its +# interface to the outside world through some means. The options for doing so +# include: +# +# 1. A custom attribute on each function itself +# 2. A linker argument saying what to export +# 3. A file which lists all symbols that need to be exported +# +# The Rust compiler takes care (1) for us for all Rust code by annotating all +# public-facing functions with dllexport, but we have a few native dependencies +# which need to cross the DLL boundary. The most important of these dependencies +# is LLVM which is linked into `rustc_llvm.dll` but primarily used from +# `rustc_trans.dll`. This means that many of LLVM's C API functions need to be +# exposed from `rustc_llvm.dll` to be forwarded over the boundary. +# +# Unfortunately, at this time, LLVM does not handle this sort of exportation on +# Windows for us, so we're forced to do it ourselves if we want it (which seems +# like the path of least resistance right now). To do this we generate a `.DEF` +# file [1] which we then custom-pass to the linker when building the rustc_llvm +# crate. This DEF file list all symbols that are exported from +# `src/librustc_llvm/lib.rs` and is generated by a small python script. +# +# Fun times! +# +# [1]: https://msdn.microsoft.com/en-us/library/28d6s79h.aspx +RUSTFLAGS_rustc_llvm_T_x86_64-pc-windows-msvc += \ + -C link-args="-DEF:x86_64-pc-windows-msvc/rt/rustc_llvm.def" +CUSTOM_DEPS_rustc_llvm_T_x86_64-pc-windows-msvc += \ + x86_64-pc-windows-msvc/rt/rustc_llvm.def + +x86_64-pc-windows-msvc/rt/rustc_llvm.def: $(S)src/etc/mklldef.py \ + $(S)src/librustc_llvm/lib.rs + $(CFG_PYTHON) $^ $@ rustc_llvm-$(CFG_FILENAME_EXTRA) diff --git a/mk/target.mk b/mk/target.mk index 4359eec8447b2..c8efb8e86d6d7 100644 --- a/mk/target.mk +++ b/mk/target.mk @@ -39,7 +39,8 @@ CRATE_FULLDEPS_$(1)_T_$(2)_H_$(3)_$(4) := \ $$(foreach dep,$$(NATIVE_DEPS_$(4)_T_$(2)), \ $$(RT_OUTPUT_DIR_$(2))/$$(dep)) \ $$(foreach dep,$$(NATIVE_TOOL_DEPS_$(4)_T_$(2)), \ - $$(TBIN$(1)_T_$(3)_H_$(3))/$$(dep)) + $$(TBIN$(1)_T_$(3)_H_$(3))/$$(dep)) \ + $$(CUSTOM_DEPS_$(4)_T_$(2)) endef $(foreach host,$(CFG_HOST), \ @@ -92,6 +93,7 @@ $$(TLIB$(1)_T_$(2)_H_$(3))/stamp.$(4): \ $$(LLVM_LIBDIR_RUSTFLAGS_$(2)) \ $$(LLVM_STDCPP_RUSTFLAGS_$(2)) \ $$(RUSTFLAGS_$(4)) \ + $$(RUSTFLAGS_$(4)_T_$(2)) \ --out-dir $$(@D) \ -C extra-filename=-$$(CFG_FILENAME_EXTRA) \ $$< diff --git a/src/etc/mklldef.py b/src/etc/mklldef.py new file mode 100644 index 0000000000000..d2f8ee469a4a1 --- /dev/null +++ b/src/etc/mklldef.py @@ -0,0 +1,25 @@ +# 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 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +import sys + +input_file = sys.argv[1] +output_file = sys.argv[2] +name = sys.argv[3] + +with open(input_file, 'r') as f: + with open(output_file, 'w') as g: + print >> g, 'LIBRARY ' + name + print >> g, 'EXPORTS' + for x in f: + x = str(x) + if not x.startswith(' pub fn LLVM'): continue + name = x[11:x.find('(')] + print >> g, ' ' + name From 847c8520b14e3ae9aec26a33f70750a071d3f18d Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 11 May 2015 21:07:38 -0700 Subject: [PATCH 26/31] rustc_llvm: Don't export constants across dlls For imports of constants across DLLs to work on Windows it *requires* that the import be marked with `dllimport` (unlike functions where the marker is optional, but strongly recommended). This currently isn't working for importing FFI constants across boundaries, however, so the one constant exported from `rustc_llvm.dll` is now a function to be called instead. --- src/librustc_llvm/lib.rs | 2 +- src/librustc_trans/trans/debuginfo/mod.rs | 2 +- src/rustllvm/RustWrapper.cpp | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs index 54031e4b04d25..3e575785d2719 100644 --- a/src/librustc_llvm/lib.rs +++ b/src/librustc_llvm/lib.rs @@ -1771,7 +1771,7 @@ extern { Dialect: c_uint) -> ValueRef; - pub static LLVMRustDebugMetadataVersion: u32; + pub fn LLVMRustDebugMetadataVersion() -> u32; pub fn LLVMRustAddModuleFlag(M: ModuleRef, name: *const c_char, diff --git a/src/librustc_trans/trans/debuginfo/mod.rs b/src/librustc_trans/trans/debuginfo/mod.rs index e4312b669ad98..4e5407016ba9b 100644 --- a/src/librustc_trans/trans/debuginfo/mod.rs +++ b/src/librustc_trans/trans/debuginfo/mod.rs @@ -193,7 +193,7 @@ pub fn finalize(cx: &CrateContext) { // Prevent bitcode readers from deleting the debug info. let ptr = "Debug Info Version\0".as_ptr(); llvm::LLVMRustAddModuleFlag(cx.llmod(), ptr as *const _, - llvm::LLVMRustDebugMetadataVersion); + llvm::LLVMRustDebugMetadataVersion()); }; } diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index 5a2d1ec42071f..66db7326d21bf 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -233,7 +233,9 @@ DIT unwrapDI(LLVMMetadataRef ref) { return DIT(ref ? unwrap(ref) : NULL); } -extern "C" const uint32_t LLVMRustDebugMetadataVersion = DEBUG_METADATA_VERSION; +extern "C" const uint32_t LLVMRustDebugMetadataVersion() { + return DEBUG_METADATA_VERSION; +} extern "C" void LLVMRustAddModuleFlag(LLVMModuleRef M, const char *name, From f5222fb892be00f2b3e4dd8b83c5277e94ebbbba Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 11 May 2015 21:09:07 -0700 Subject: [PATCH 27/31] std: Implement aborting stubs for MSVC unwinding At this time unwinding support is not implemented for MSVC as `libgcc_s_seh-1.dll` is not available by default (and this is used on MinGW), but this should be investigated soon. For now this change is just aimed at getting the compiler far enough to bootstrap everything instead of successfully running tests. This commit refactors the `std::rt::unwind` module a bit to prepare for SEH support eventually by moving all GCC-specific functionality to its own submodule and defining the interface needed. --- src/libstd/rt/unwind/gcc.rs | 342 ++++++++++++++++++++ src/libstd/rt/{unwind.rs => unwind/mod.rs} | 345 +-------------------- src/libstd/rt/unwind/seh.rs | 30 ++ 3 files changed, 389 insertions(+), 328 deletions(-) create mode 100644 src/libstd/rt/unwind/gcc.rs rename src/libstd/rt/{unwind.rs => unwind/mod.rs} (50%) create mode 100644 src/libstd/rt/unwind/seh.rs diff --git a/src/libstd/rt/unwind/gcc.rs b/src/libstd/rt/unwind/gcc.rs new file mode 100644 index 0000000000000..39b32a3f08e49 --- /dev/null +++ b/src/libstd/rt/unwind/gcc.rs @@ -0,0 +1,342 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use prelude::v1::*; + +use any::Any; +use boxed; +use libc::c_void; +use rt::libunwind as uw; + +struct Exception { + uwe: uw::_Unwind_Exception, + cause: Option>, +} + +pub unsafe fn panic(data: Box) -> ! { + let exception: Box<_> = box Exception { + uwe: uw::_Unwind_Exception { + exception_class: rust_exception_class(), + exception_cleanup: exception_cleanup, + private: [0; uw::unwinder_private_data_size], + }, + cause: Some(data), + }; + let exception_param = boxed::into_raw(exception) as *mut uw::_Unwind_Exception; + let error = uw::_Unwind_RaiseException(exception_param); + rtabort!("Could not unwind stack, error = {}", error as isize); + + extern fn exception_cleanup(_unwind_code: uw::_Unwind_Reason_Code, + exception: *mut uw::_Unwind_Exception) { + rtdebug!("exception_cleanup()"); + unsafe { + let _: Box = Box::from_raw(exception as *mut Exception); + } + } +} + +pub unsafe fn cleanup(ptr: *mut c_void) -> Box { + let my_ep = ptr as *mut Exception; + rtdebug!("caught {}", (*my_ep).uwe.exception_class); + let cause = (*my_ep).cause.take(); + uw::_Unwind_DeleteException(ptr as *mut _); + cause.unwrap() +} + +// Rust's exception class identifier. This is used by personality routines to +// determine whether the exception was thrown by their own runtime. +fn rust_exception_class() -> uw::_Unwind_Exception_Class { + // M O Z \0 R U S T -- vendor, language + 0x4d4f5a_00_52555354 +} + +// We could implement our personality routine in pure Rust, however exception +// info decoding is tedious. More importantly, personality routines have to +// handle various platform quirks, which are not fun to maintain. For this +// reason, we attempt to reuse personality routine of the C language: +// __gcc_personality_v0. +// +// Since C does not support exception catching, __gcc_personality_v0 simply +// always returns _URC_CONTINUE_UNWIND in search phase, and always returns +// _URC_INSTALL_CONTEXT (i.e. "invoke cleanup code") in cleanup phase. +// +// This is pretty close to Rust's exception handling approach, except that Rust +// does have a single "catch-all" handler at the bottom of each thread's stack. +// So we have two versions of the personality routine: +// - rust_eh_personality, used by all cleanup landing pads, which never catches, +// so the behavior of __gcc_personality_v0 is perfectly adequate there, and +// - rust_eh_personality_catch, used only by rust_try(), which always catches. +// +// Note, however, that for implementation simplicity, rust_eh_personality_catch +// lacks code to install a landing pad, so in order to obtain exception object +// pointer (which it needs to return upstream), rust_try() employs another trick: +// it calls into the nested rust_try_inner(), whose landing pad does not resume +// unwinds. Instead, it extracts the exception pointer and performs a "normal" +// return. +// +// See also: rt/rust_try.ll + +#[cfg(all(not(target_arch = "arm"), + not(all(windows, target_arch = "x86_64")), + not(test)))] +pub mod eabi { + use rt::libunwind as uw; + use libc::c_int; + + extern "C" { + fn __gcc_personality_v0(version: c_int, + actions: uw::_Unwind_Action, + exception_class: uw::_Unwind_Exception_Class, + ue_header: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context) + -> uw::_Unwind_Reason_Code; + } + + #[lang="eh_personality"] + #[no_mangle] // referenced from rust_try.ll + #[allow(private_no_mangle_fns)] + extern fn rust_eh_personality( + version: c_int, + actions: uw::_Unwind_Action, + exception_class: uw::_Unwind_Exception_Class, + ue_header: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context + ) -> uw::_Unwind_Reason_Code + { + unsafe { + __gcc_personality_v0(version, actions, exception_class, ue_header, + context) + } + } + + #[no_mangle] // referenced from rust_try.ll + pub extern "C" fn rust_eh_personality_catch( + _version: c_int, + actions: uw::_Unwind_Action, + _exception_class: uw::_Unwind_Exception_Class, + _ue_header: *mut uw::_Unwind_Exception, + _context: *mut uw::_Unwind_Context + ) -> uw::_Unwind_Reason_Code + { + + if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase + uw::_URC_HANDLER_FOUND // catch! + } + else { // cleanup phase + uw::_URC_INSTALL_CONTEXT + } + } +} + +// iOS on armv7 is using SjLj exceptions and therefore requires to use +// a specialized personality routine: __gcc_personality_sj0 + +#[cfg(all(target_os = "ios", target_arch = "arm", not(test)))] +pub mod eabi { + use rt::libunwind as uw; + use libc::c_int; + + extern "C" { + fn __gcc_personality_sj0(version: c_int, + actions: uw::_Unwind_Action, + exception_class: uw::_Unwind_Exception_Class, + ue_header: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context) + -> uw::_Unwind_Reason_Code; + } + + #[lang="eh_personality"] + #[no_mangle] // referenced from rust_try.ll + pub extern "C" fn rust_eh_personality( + version: c_int, + actions: uw::_Unwind_Action, + exception_class: uw::_Unwind_Exception_Class, + ue_header: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context + ) -> uw::_Unwind_Reason_Code + { + unsafe { + __gcc_personality_sj0(version, actions, exception_class, ue_header, + context) + } + } + + #[no_mangle] // referenced from rust_try.ll + pub extern "C" fn rust_eh_personality_catch( + _version: c_int, + actions: uw::_Unwind_Action, + _exception_class: uw::_Unwind_Exception_Class, + _ue_header: *mut uw::_Unwind_Exception, + _context: *mut uw::_Unwind_Context + ) -> uw::_Unwind_Reason_Code + { + if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase + uw::_URC_HANDLER_FOUND // catch! + } + else { // cleanup phase + unsafe { + __gcc_personality_sj0(_version, actions, _exception_class, _ue_header, + _context) + } + } + } +} + + +// ARM EHABI uses a slightly different personality routine signature, +// but otherwise works the same. +#[cfg(all(target_arch = "arm", not(target_os = "ios"), not(test)))] +pub mod eabi { + use rt::libunwind as uw; + use libc::c_int; + + extern "C" { + fn __gcc_personality_v0(state: uw::_Unwind_State, + ue_header: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context) + -> uw::_Unwind_Reason_Code; + } + + #[lang="eh_personality"] + #[no_mangle] // referenced from rust_try.ll + #[allow(private_no_mangle_fns)] + extern "C" fn rust_eh_personality( + state: uw::_Unwind_State, + ue_header: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context + ) -> uw::_Unwind_Reason_Code + { + unsafe { + __gcc_personality_v0(state, ue_header, context) + } + } + + #[no_mangle] // referenced from rust_try.ll + pub extern "C" fn rust_eh_personality_catch( + state: uw::_Unwind_State, + _ue_header: *mut uw::_Unwind_Exception, + _context: *mut uw::_Unwind_Context + ) -> uw::_Unwind_Reason_Code + { + if (state as c_int & uw::_US_ACTION_MASK as c_int) + == uw::_US_VIRTUAL_UNWIND_FRAME as c_int { // search phase + uw::_URC_HANDLER_FOUND // catch! + } + else { // cleanup phase + uw::_URC_INSTALL_CONTEXT + } + } +} + +// Win64 SEH (see http://msdn.microsoft.com/en-us/library/1eyas8tf.aspx) +// +// This looks a bit convoluted because rather than implementing a native SEH +// handler, GCC reuses the same personality routine as for the other +// architectures by wrapping it with an "API translator" layer +// (_GCC_specific_handler). + +#[cfg(all(windows, target_arch = "x86_64", not(test)))] +#[doc(hidden)] +#[allow(non_camel_case_types, non_snake_case)] +pub mod eabi { + pub use self::EXCEPTION_DISPOSITION::*; + use rt::libunwind as uw; + use libc::{c_void, c_int}; + + #[repr(C)] + pub struct EXCEPTION_RECORD; + #[repr(C)] + pub struct CONTEXT; + #[repr(C)] + pub struct DISPATCHER_CONTEXT; + + #[repr(C)] + #[derive(Copy, Clone)] + pub enum EXCEPTION_DISPOSITION { + ExceptionContinueExecution, + ExceptionContinueSearch, + ExceptionNestedException, + ExceptionCollidedUnwind + } + + type _Unwind_Personality_Fn = + extern "C" fn( + version: c_int, + actions: uw::_Unwind_Action, + exception_class: uw::_Unwind_Exception_Class, + ue_header: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context + ) -> uw::_Unwind_Reason_Code; + + extern "C" { + fn __gcc_personality_seh0( + exceptionRecord: *mut EXCEPTION_RECORD, + establisherFrame: *mut c_void, + contextRecord: *mut CONTEXT, + dispatcherContext: *mut DISPATCHER_CONTEXT + ) -> EXCEPTION_DISPOSITION; + + fn _GCC_specific_handler( + exceptionRecord: *mut EXCEPTION_RECORD, + establisherFrame: *mut c_void, + contextRecord: *mut CONTEXT, + dispatcherContext: *mut DISPATCHER_CONTEXT, + personality: _Unwind_Personality_Fn + ) -> EXCEPTION_DISPOSITION; + } + + #[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 + { + unsafe { + __gcc_personality_seh0(exceptionRecord, establisherFrame, + contextRecord, dispatcherContext) + } + } + + #[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 + { + extern "C" fn inner( + _version: c_int, + actions: uw::_Unwind_Action, + _exception_class: uw::_Unwind_Exception_Class, + _ue_header: *mut uw::_Unwind_Exception, + _context: *mut uw::_Unwind_Context + ) -> uw::_Unwind_Reason_Code + { + if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase + uw::_URC_HANDLER_FOUND // catch! + } + else { // cleanup phase + uw::_URC_INSTALL_CONTEXT + } + } + + unsafe { + _GCC_specific_handler(exceptionRecord, establisherFrame, + contextRecord, dispatcherContext, + inner) + } + } +} + diff --git a/src/libstd/rt/unwind.rs b/src/libstd/rt/unwind/mod.rs similarity index 50% rename from src/libstd/rt/unwind.rs rename to src/libstd/rt/unwind/mod.rs index b24099505ed89..9cbe6c4048324 100644 --- a/src/libstd/rt/unwind.rs +++ b/src/libstd/rt/unwind/mod.rs @@ -57,6 +57,9 @@ //! //! Currently Rust uses unwind runtime provided by libgcc. +#![allow(dead_code)] +#![allow(unused_imports)] + use prelude::v1::*; use any::Any; @@ -71,12 +74,13 @@ use mem; use sync::atomic::{self, Ordering}; use sys_common::mutex::{Mutex, MUTEX_INIT}; -use rt::libunwind as uw; - -struct Exception { - uwe: uw::_Unwind_Exception, - cause: Option>, -} +// The actual unwinding implementation is cfg'd here, and we've got two current +// implementations. One goes through SEH on Windows and the other goes through +// libgcc via the libunwind-like API. +#[cfg(target_env = "msvc")] #[path = "seh.rs"] #[doc(hidden)] +pub mod imp; +#[cfg(not(target_env = "msvc"))] #[path = "gcc.rs"] #[doc(hidden)] +pub mod imp; pub type Callback = fn(msg: &(Any + Send), file: &'static str, line: u32); @@ -97,6 +101,10 @@ static CALLBACK_CNT: atomic::AtomicUsize = atomic::ATOMIC_USIZE_INIT; thread_local! { static PANICKING: Cell = Cell::new(false) } +#[link(name = "rustrt_native", kind = "static")] +#[cfg(not(test))] +extern {} + /// Invoke a closure, capturing the cause of panic if one occurs. /// /// This function will return `Ok(())` if the closure did not panic, and will @@ -127,11 +135,7 @@ pub unsafe fn try(f: F) -> Result<(), Box> { return if ep.is_null() { Ok(()) } else { - let my_ep = ep as *mut Exception; - rtdebug!("caught {}", (*my_ep).uwe.exception_class); - let cause = (*my_ep).cause.take(); - uw::_Unwind_DeleteException(ep); - Err(cause.unwrap()) + Err(imp::cleanup(ep)) }; extern fn try_fn(opt_closure: *mut c_void) { @@ -139,17 +143,13 @@ pub unsafe fn try(f: F) -> Result<(), Box> { unsafe { (*opt_closure).take().unwrap()(); } } - #[link(name = "rustrt_native", kind = "static")] - #[cfg(not(test))] - extern {} - extern { // Rust's try-catch // When f(...) returns normally, the return value is null. // When f(...) throws, the return value is a pointer to the caught // exception object. fn rust_try(f: extern fn(*mut c_void), - data: *mut c_void) -> *mut uw::_Unwind_Exception; + data: *mut c_void) -> *mut c_void; } } @@ -164,319 +164,8 @@ pub fn panicking() -> bool { #[allow(private_no_mangle_fns)] fn rust_panic(cause: Box) -> ! { rtdebug!("begin_unwind()"); - unsafe { - let exception: Box<_> = box Exception { - uwe: uw::_Unwind_Exception { - exception_class: rust_exception_class(), - exception_cleanup: exception_cleanup, - private: [0; uw::unwinder_private_data_size], - }, - cause: Some(cause), - }; - let exception_param = boxed::into_raw(exception) as *mut uw::_Unwind_Exception; - let error = uw::_Unwind_RaiseException(exception_param); - rtabort!("Could not unwind stack, error = {}", error as isize) - } - - extern fn exception_cleanup(_unwind_code: uw::_Unwind_Reason_Code, - exception: *mut uw::_Unwind_Exception) { - rtdebug!("exception_cleanup()"); - unsafe { - let _: Box = Box::from_raw(exception as *mut Exception); - } - } -} - -// Rust's exception class identifier. This is used by personality routines to -// determine whether the exception was thrown by their own runtime. -fn rust_exception_class() -> uw::_Unwind_Exception_Class { - // M O Z \0 R U S T -- vendor, language - 0x4d4f5a_00_52555354 -} - -// We could implement our personality routine in pure Rust, however exception -// info decoding is tedious. More importantly, personality routines have to -// handle various platform quirks, which are not fun to maintain. For this -// reason, we attempt to reuse personality routine of the C language: -// __gcc_personality_v0. -// -// Since C does not support exception catching, __gcc_personality_v0 simply -// always returns _URC_CONTINUE_UNWIND in search phase, and always returns -// _URC_INSTALL_CONTEXT (i.e. "invoke cleanup code") in cleanup phase. -// -// This is pretty close to Rust's exception handling approach, except that Rust -// does have a single "catch-all" handler at the bottom of each thread's stack. -// So we have two versions of the personality routine: -// - rust_eh_personality, used by all cleanup landing pads, which never catches, -// so the behavior of __gcc_personality_v0 is perfectly adequate there, and -// - rust_eh_personality_catch, used only by rust_try(), which always catches. -// -// Note, however, that for implementation simplicity, rust_eh_personality_catch -// lacks code to install a landing pad, so in order to obtain exception object -// pointer (which it needs to return upstream), rust_try() employs another trick: -// it calls into the nested rust_try_inner(), whose landing pad does not resume -// unwinds. Instead, it extracts the exception pointer and performs a "normal" -// return. -// -// See also: rt/rust_try.ll - -#[cfg(all(not(target_arch = "arm"), - not(all(windows, target_arch = "x86_64")), - not(test)))] -#[doc(hidden)] -pub mod eabi { - use rt::libunwind as uw; - use libc::c_int; - - extern "C" { - fn __gcc_personality_v0(version: c_int, - actions: uw::_Unwind_Action, - exception_class: uw::_Unwind_Exception_Class, - ue_header: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context) - -> uw::_Unwind_Reason_Code; - } - - #[lang = "eh_personality"] - #[no_mangle] // referenced from rust_try.ll - #[allow(private_no_mangle_fns)] - extern fn rust_eh_personality( - version: c_int, - actions: uw::_Unwind_Action, - exception_class: uw::_Unwind_Exception_Class, - ue_header: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context - ) -> uw::_Unwind_Reason_Code - { - unsafe { - __gcc_personality_v0(version, actions, exception_class, ue_header, - context) - } - } - - #[no_mangle] // referenced from rust_try.ll - pub extern "C" fn rust_eh_personality_catch( - _version: c_int, - actions: uw::_Unwind_Action, - _exception_class: uw::_Unwind_Exception_Class, - _ue_header: *mut uw::_Unwind_Exception, - _context: *mut uw::_Unwind_Context - ) -> uw::_Unwind_Reason_Code - { - - if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase - uw::_URC_HANDLER_FOUND // catch! - } - else { // cleanup phase - uw::_URC_INSTALL_CONTEXT - } - } -} - -// iOS on armv7 is using SjLj exceptions and therefore requires to use -// a specialized personality routine: __gcc_personality_sj0 - -#[cfg(all(target_os = "ios", target_arch = "arm", not(test)))] -#[doc(hidden)] -pub mod eabi { - use rt::libunwind as uw; - use libc::c_int; - - extern "C" { - fn __gcc_personality_sj0(version: c_int, - actions: uw::_Unwind_Action, - exception_class: uw::_Unwind_Exception_Class, - ue_header: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context) - -> uw::_Unwind_Reason_Code; - } - - #[lang = "eh_personality"] - #[no_mangle] // referenced from rust_try.ll - pub extern "C" fn rust_eh_personality( - version: c_int, - actions: uw::_Unwind_Action, - exception_class: uw::_Unwind_Exception_Class, - ue_header: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context - ) -> uw::_Unwind_Reason_Code - { - unsafe { - __gcc_personality_sj0(version, actions, exception_class, ue_header, - context) - } - } - - #[no_mangle] // referenced from rust_try.ll - pub extern "C" fn rust_eh_personality_catch( - _version: c_int, - actions: uw::_Unwind_Action, - _exception_class: uw::_Unwind_Exception_Class, - _ue_header: *mut uw::_Unwind_Exception, - _context: *mut uw::_Unwind_Context - ) -> uw::_Unwind_Reason_Code - { - if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase - uw::_URC_HANDLER_FOUND // catch! - } - else { // cleanup phase - unsafe { - __gcc_personality_sj0(_version, actions, _exception_class, _ue_header, - _context) - } - } - } -} - - -// ARM EHABI uses a slightly different personality routine signature, -// but otherwise works the same. -#[cfg(all(target_arch = "arm", not(target_os = "ios"), not(test)))] -#[doc(hidden)] -pub mod eabi { - use rt::libunwind as uw; - use libc::c_int; - - extern "C" { - fn __gcc_personality_v0(state: uw::_Unwind_State, - ue_header: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context) - -> uw::_Unwind_Reason_Code; - } - - #[lang = "eh_personality"] - #[no_mangle] // referenced from rust_try.ll - #[allow(private_no_mangle_fns)] - extern "C" fn rust_eh_personality( - state: uw::_Unwind_State, - ue_header: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context - ) -> uw::_Unwind_Reason_Code - { - unsafe { - __gcc_personality_v0(state, ue_header, context) - } - } - - #[no_mangle] // referenced from rust_try.ll - pub extern "C" fn rust_eh_personality_catch( - state: uw::_Unwind_State, - _ue_header: *mut uw::_Unwind_Exception, - _context: *mut uw::_Unwind_Context - ) -> uw::_Unwind_Reason_Code - { - if (state as c_int & uw::_US_ACTION_MASK as c_int) - == uw::_US_VIRTUAL_UNWIND_FRAME as c_int { // search phase - uw::_URC_HANDLER_FOUND // catch! - } - else { // cleanup phase - uw::_URC_INSTALL_CONTEXT - } - } -} - -// Win64 SEH (see http://msdn.microsoft.com/en-us/library/1eyas8tf.aspx) -// -// This looks a bit convoluted because rather than implementing a native SEH handler, -// GCC reuses the same personality routine as for the other architectures by wrapping it -// with an "API translator" layer (_GCC_specific_handler). - -#[cfg(all(windows, target_arch = "x86_64", not(test)))] -#[doc(hidden)] -#[allow(non_camel_case_types, non_snake_case)] -pub mod eabi { - pub use self::EXCEPTION_DISPOSITION::*; - use rt::libunwind as uw; - use libc::{c_void, c_int}; - - #[repr(C)] - pub struct EXCEPTION_RECORD; - #[repr(C)] - pub struct CONTEXT; - #[repr(C)] - pub struct DISPATCHER_CONTEXT; - - #[repr(C)] - #[derive(Copy, Clone)] - pub enum EXCEPTION_DISPOSITION { - ExceptionContinueExecution, - ExceptionContinueSearch, - ExceptionNestedException, - ExceptionCollidedUnwind - } - - type _Unwind_Personality_Fn = - extern "C" fn( - version: c_int, - actions: uw::_Unwind_Action, - exception_class: uw::_Unwind_Exception_Class, - ue_header: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context - ) -> uw::_Unwind_Reason_Code; - - extern "C" { - fn __gcc_personality_seh0( - exceptionRecord: *mut EXCEPTION_RECORD, - establisherFrame: *mut c_void, - contextRecord: *mut CONTEXT, - dispatcherContext: *mut DISPATCHER_CONTEXT - ) -> EXCEPTION_DISPOSITION; - - fn _GCC_specific_handler( - exceptionRecord: *mut EXCEPTION_RECORD, - establisherFrame: *mut c_void, - contextRecord: *mut CONTEXT, - dispatcherContext: *mut DISPATCHER_CONTEXT, - personality: _Unwind_Personality_Fn - ) -> EXCEPTION_DISPOSITION; - } - - #[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 - { - unsafe { - __gcc_personality_seh0(exceptionRecord, establisherFrame, - contextRecord, dispatcherContext) - } - } - - #[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 - { - extern "C" fn inner( - _version: c_int, - actions: uw::_Unwind_Action, - _exception_class: uw::_Unwind_Exception_Class, - _ue_header: *mut uw::_Unwind_Exception, - _context: *mut uw::_Unwind_Context - ) -> uw::_Unwind_Reason_Code - { - if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase - uw::_URC_HANDLER_FOUND // catch! - } - else { // cleanup phase - uw::_URC_INSTALL_CONTEXT - } - } - - unsafe { - _GCC_specific_handler(exceptionRecord, establisherFrame, - contextRecord, dispatcherContext, - inner) - } + imp::panic(cause) } } diff --git a/src/libstd/rt/unwind/seh.rs b/src/libstd/rt/unwind/seh.rs new file mode 100644 index 0000000000000..a72c1debe14e0 --- /dev/null +++ b/src/libstd/rt/unwind/seh.rs @@ -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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use prelude::v1::*; + +use any::Any; +use intrinsics; +use libc::c_void; + +pub unsafe fn panic(_data: Box) -> ! { + intrinsics::abort(); +} + +pub unsafe fn cleanup(_ptr: *mut c_void) -> Box { + intrinsics::abort(); +} + +#[lang = "eh_personality"] +#[no_mangle] +pub extern fn rust_eh_personality() {} + +#[no_mangle] +pub extern fn rust_eh_personality_catch() {} From 9a2415b82284f81d2fa7d6aa5096e6d2e5d83452 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 12 May 2015 13:29:09 -0700 Subject: [PATCH 28/31] std: Mark rust_get_num_cpus as dllexport This function is imported across the DLL boundary by the libtest dynamic library, so it has to be marked as dllexport somehow, and for now this is done with an attribute on the function specifically. --- src/rt/rust_builtin.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/rt/rust_builtin.c b/src/rt/rust_builtin.c index 5939a5a3c90f5..92371e90aba0c 100644 --- a/src/rt/rust_builtin.c +++ b/src/rt/rust_builtin.c @@ -45,6 +45,12 @@ #include "valgrind/valgrind.h" #endif +#if defined(_MSC_VER) +# define RUST_BUILTIN_API __declspec(dllexport) +#else +# define RUST_BUILTIN_API +#endif + #ifndef _WIN32 char* rust_list_dir_val(struct dirent* entry_ptr) { @@ -129,6 +135,7 @@ get_num_cpus() { } #endif +RUST_BUILTIN_API uintptr_t rust_get_num_cpus() { return get_num_cpus(); From 3d32cf5d9afdd8c375935ac875b2a20f24159e52 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 12 May 2015 14:46:19 -0700 Subject: [PATCH 29/31] rustc_trans: Apply dllexport attributes for MSVC This commit modifies the compiler to emit `dllexport` for all reachable functions and data on MSVC targets, regardless of whether a dynamic library is being created or not. More details can be found in the commit itself. --- src/librustc_trans/trans/base.rs | 18 +++++++++- src/librustc_trans/trans/context.rs | 55 +++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 4c936ddbcef00..4a6c73009f1ad 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -237,6 +237,9 @@ pub fn get_extern_const<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, did: ast::DefId, llvm::set_thread_local(c, true); } } + if ccx.use_dll_storage_attrs() { + llvm::SetDLLStorageClass(c, llvm::DLLImportStorageClass); + } ccx.externs().borrow_mut().insert(name.to_string(), c); return c; } @@ -1940,11 +1943,17 @@ pub fn update_linkage(ccx: &CrateContext, match id { Some(id) if ccx.reachable().contains(&id) => { llvm::SetLinkage(llval, llvm::ExternalLinkage); + if ccx.use_dll_storage_attrs() { + llvm::SetDLLStorageClass(llval, llvm::DLLExportStorageClass); + } }, _ => { // `id` does not refer to an item in `ccx.reachable`. if ccx.sess().opts.cg.codegen_units > 1 { llvm::SetLinkage(llval, llvm::ExternalLinkage); + if ccx.use_dll_storage_attrs() { + llvm::SetDLLStorageClass(llval, llvm::DLLExportStorageClass); + } } else { llvm::SetLinkage(llval, llvm::InternalLinkage); } @@ -2103,9 +2112,15 @@ fn finish_register_fn(ccx: &CrateContext, sym: String, node_id: ast::NodeId, if ccx.tcx().lang_items.stack_exhausted() == Some(def) { attributes::split_stack(llfn, false); llvm::SetLinkage(llfn, llvm::ExternalLinkage); + if ccx.use_dll_storage_attrs() { + llvm::SetDLLStorageClass(llfn, llvm::DLLExportStorageClass); + } } if ccx.tcx().lang_items.eh_personality() == Some(def) { llvm::SetLinkage(llfn, llvm::ExternalLinkage); + if ccx.use_dll_storage_attrs() { + llvm::SetDLLStorageClass(llfn, llvm::DLLExportStorageClass); + } } } @@ -2172,7 +2187,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 { @@ -2589,6 +2604,7 @@ fn internalize_symbols(cx: &SharedCrateContext, reachable: &HashSet) { if !declared.contains(&name) && !reachable.contains(str::from_utf8(&name).unwrap()) { llvm::SetLinkage(val, llvm::InternalLinkage); + llvm::SetDLLStorageClass(val, llvm::DefaultStorageClass); } } } diff --git a/src/librustc_trans/trans/context.rs b/src/librustc_trans/trans/context.rs index 41ef566f2fd7f..51db0adf5b775 100644 --- a/src/librustc_trans/trans/context.rs +++ b/src/librustc_trans/trans/context.rs @@ -75,6 +75,7 @@ pub struct SharedCrateContext<'tcx> { available_monomorphizations: RefCell>, available_drop_glues: RefCell, String>>, + use_dll_storage_attrs: bool, } /// The local portion of a `CrateContext`. There is one `LocalCrateContext` @@ -251,6 +252,51 @@ impl<'tcx> SharedCrateContext<'tcx> { create_context_and_module(&tcx.sess, "metadata") }; + // An interesting part of Windows which MSVC forces our hand on (and + // apparently MinGW didn't) is the usage of `dllimport` and `dllexport` + // attributes in LLVM IR as well as native dependencies (in C these + // correspond to `__declspec(dllimport)`). + // + // Whenever a dynamic library is built by MSVC it must have its public + // interface specified by functions tagged with `dllexport` or otherwise + // they're not available to be linked against. This poses a few problems + // for the compiler, some of which are somewhat fundamental, but we use + // the `use_dll_storage_attrs` variable below to attach the `dllexport` + // attribute to all LLVM functions that are reachable (e.g. they're + // already tagged with external linkage). This is suboptimal for a few + // reasons: + // + // * If an object file will never be included in a dynamic library, + // there's no need to attach the dllexport attribute. Most object + // files in Rust are not destined to become part of a dll as binaries + // are statically linked by default. + // * If the compiler is emitting both an rlib and a dylib, the same + // source object file is currently used but with MSVC this may be less + // feasible. The compiler may be able to get around this, but it may + // involve some invasive changes to deal with this. + // + // The flipside of this situation is that whenever you link to a dll and + // you import a function from it, the import should be tagged with + // `dllimport`. At this time, however, the compiler does not emit + // `dllimport` for any declarations other than constants (where it is + // required), which is again suboptimal for even more reasons! + // + // * Calling a function imported from another dll without using + // `dllimport` causes the linker/compiler to have extra overhead (one + // `jmp` instruction on x86) when calling the function. + // * The same object file may be used in different circumstances, so a + // function may be imported from a dll if the object is linked into a + // dll, but it may be just linked against if linked into an rlib. + // * The compiler has no knowledge about whether native functions should + // be tagged dllimport or not. + // + // For now the compiler takes the perf hit (I do not have any numbers to + // this effect) by marking very little as `dllimport` and praying the + // linker will take care of everything. Fixing this problem will likely + // require adding a few attributes to Rust itself (feature gated at the + // start) and then strongly recommending static linkage on MSVC! + let use_dll_storage_attrs = tcx.sess.target.target.options.is_like_msvc; + let mut shared_ccx = SharedCrateContext { local_ccxs: Vec::with_capacity(local_count), metadata_llmod: metadata_llmod, @@ -277,6 +323,7 @@ impl<'tcx> SharedCrateContext<'tcx> { check_drop_flag_for_sanity: check_drop_flag_for_sanity, available_monomorphizations: RefCell::new(FnvHashSet()), available_drop_glues: RefCell::new(FnvHashMap()), + use_dll_storage_attrs: use_dll_storage_attrs, }; for i in 0..local_count { @@ -365,6 +412,10 @@ impl<'tcx> SharedCrateContext<'tcx> { pub fn stats<'a>(&'a self) -> &'a Stats { &self.stats } + + pub fn use_dll_storage_attrs(&self) -> bool { + self.use_dll_storage_attrs + } } impl<'tcx> LocalCrateContext<'tcx> { @@ -733,6 +784,10 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { // values. self.shared.check_drop_flag_for_sanity } + + pub fn use_dll_storage_attrs(&self) -> bool { + self.shared.use_dll_storage_attrs() + } } /// Declare any llvm intrinsics that you might need From 74f4f395aefd1054b8d8974a128007d0671e9d78 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Tue, 12 May 2015 15:52:33 -0700 Subject: [PATCH 30/31] std: Don't require rust_try as an exported symbol This commit adds a small non-generic non-inlineable shim function to `rt::unwind::try` which the compiler can take care of for managing the exported symbol instead of having to edit `src/rt/rust_try.ll` --- src/libstd/rt/unwind/mod.rs | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/src/libstd/rt/unwind/mod.rs b/src/libstd/rt/unwind/mod.rs index 9cbe6c4048324..576035ffe9a02 100644 --- a/src/libstd/rt/unwind/mod.rs +++ b/src/libstd/rt/unwind/mod.rs @@ -127,16 +127,31 @@ extern {} /// run. pub unsafe fn try(f: F) -> Result<(), Box> { let mut f = Some(f); + return inner_try(try_fn::, &mut f as *mut _ as *mut c_void); - let prev = PANICKING.with(|s| s.get()); - PANICKING.with(|s| s.set(false)); - let ep = rust_try(try_fn::, &mut f as *mut _ as *mut c_void); - PANICKING.with(|s| s.set(prev)); - return if ep.is_null() { - Ok(()) - } else { - Err(imp::cleanup(ep)) - }; + // If an inner function were not used here, then this generic function `try` + // uses the native symbol `rust_try`, for which the code is statically + // linked into the standard library. This means that the DLL for the + // standard library must have `rust_try` as an exposed symbol that + // downstream crates can link against (because monomorphizations of `try` in + // downstream crates will have a reference to the `rust_try` symbol). + // + // On MSVC this requires the symbol `rust_try` to be tagged with + // `dllexport`, but it's easier to not have conditional `src/rt/rust_try.ll` + // files and instead just have this non-generic shim the compiler can take + // care of exposing correctly. + unsafe fn inner_try(f: extern fn(*mut c_void), data: *mut c_void) + -> Result<(), Box> { + let prev = PANICKING.with(|s| s.get()); + PANICKING.with(|s| s.set(false)); + let ep = rust_try(f, data); + PANICKING.with(|s| s.set(prev)); + if ep.is_null() { + Ok(()) + } else { + Err(imp::cleanup(ep)) + } + } extern fn try_fn(opt_closure: *mut c_void) { let opt_closure = opt_closure as *mut Option; From cb3071b2736244db03f800102f123f437fabc9c1 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 13 May 2015 10:03:00 -0700 Subject: [PATCH 31/31] mk: Update `make dist` for MSVC targets This commit updates the `dist` target for MSVC to not build the mingw components and to also ensure that the `llvm-ar.exe` binary is ferried along into the right location for installs. --- mk/cfg/x86_64-pc-windows-msvc.mk | 1 + mk/dist.mk | 11 ++++++++++- mk/prepare.mk | 11 ++++++++++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/mk/cfg/x86_64-pc-windows-msvc.mk b/mk/cfg/x86_64-pc-windows-msvc.mk index 1a1f059945a85..bd1088b7cd133 100644 --- a/mk/cfg/x86_64-pc-windows-msvc.mk +++ b/mk/cfg/x86_64-pc-windows-msvc.mk @@ -45,6 +45,7 @@ endif # instead of `lib.exe` for assembling archives, so we need to inject this custom # dependency here. NATIVE_TOOL_DEPS_core_T_x86_64-pc-windows-msvc += llvm-ar.exe +INSTALLED_BINS_x86_64-pc-windows-msvc += llvm-ar.exe # When working with MSVC on windows, each DLL needs to explicitly declare its # interface to the outside world through some means. The options for doing so diff --git a/mk/dist.mk b/mk/dist.mk index 75c6219c5f04f..9aab51daba914 100644 --- a/mk/dist.mk +++ b/mk/dist.mk @@ -144,9 +144,11 @@ dist/$$(PKG_NAME)-$(1).tar.gz: dist-install-dir-$(1) prepare-overlay-$(1) @$(call E, build: $$@) # Copy essential gcc components into installer ifdef CFG_WINDOWSY_$(1) +ifeq ($$(findstring gnu,$(1)),gnu) $$(Q)rm -Rf tmp/dist/win-rust-gcc-$(1) $$(Q)$$(CFG_PYTHON) $$(S)src/etc/make-win-dist.py tmp/dist/$$(PKG_NAME)-$(1)-image tmp/dist/win-rust-gcc-$(1) $(1) $$(Q)cp -r $$(S)src/etc/third-party tmp/dist/$$(PKG_NAME)-$(1)-image/share/doc/ +endif endif $$(Q)$$(S)src/rust-installer/gen-installer.sh \ --product-name=Rust \ @@ -213,7 +215,14 @@ endif dist-install-dirs: $(foreach host,$(CFG_HOST),dist-install-dir-$(host)) ifdef CFG_WINDOWSY_$(CFG_BUILD) -MAYBE_MINGW_TARBALLS=$(foreach host,$(CFG_HOST),dist/$(MINGW_PKG_NAME)-$(host).tar.gz) +define BUILD_MINGW_TARBALL +ifeq ($$(findstring gnu,$(1)),gnu) +MAYBE_MINGW_TARBALLS += dist/$(MINGW_PKG_NAME)-$(1).tar.gz +endif +endef + +$(foreach host,$(CFG_HOST),\ + $(eval $(call BUILD_MINGW_TARBALL,$(host)))) endif ifeq ($(CFG_DISABLE_DOCS),) diff --git a/mk/prepare.mk b/mk/prepare.mk index a7f5a489dc5b4..573b7ac79fd1a 100644 --- a/mk/prepare.mk +++ b/mk/prepare.mk @@ -36,7 +36,10 @@ endef # Copy an executable # $(1) is the filename/libname-glob +# +# Gee, what's up with that $(nop)? See comment below. define PREPARE_BIN + $(nop) @$(call E, prepare: $(PREPARE_DEST_BIN_DIR)/$(1)) $(Q)$(PREPARE_BIN_CMD) $(PREPARE_SOURCE_BIN_DIR)/$(1) $(PREPARE_DEST_BIN_DIR)/$(1) endef @@ -119,6 +122,8 @@ define DEF_PREPARE_TARGET_N # Rebind PREPARE_*_LIB_DIR to point to rustlib, then install the libs for the targets prepare-target-$(2)-host-$(3)-$(1)-$(4): PREPARE_WORKING_SOURCE_LIB_DIR=$$(PREPARE_SOURCE_LIB_DIR)/rustlib/$(2)/lib prepare-target-$(2)-host-$(3)-$(1)-$(4): PREPARE_WORKING_DEST_LIB_DIR=$$(PREPARE_DEST_LIB_DIR)/rustlib/$(2)/lib +prepare-target-$(2)-host-$(3)-$(1)-$(4): PREPARE_SOURCE_BIN_DIR=$$(PREPARE_SOURCE_LIB_DIR)/rustlib/$(3)/bin +prepare-target-$(2)-host-$(3)-$(1)-$(4): PREPARE_DEST_BIN_DIR=$$(PREPARE_DEST_LIB_DIR)/rustlib/$(3)/bin prepare-target-$(2)-host-$(3)-$(1)-$(4): prepare-maybe-clean-$(4) \ $$(foreach crate,$$(TARGET_CRATES), \ $$(TLIB$(1)_T_$(2)_H_$(3))/stamp.$$(crate)) \ @@ -133,6 +138,7 @@ prepare-target-$(2)-host-$(3)-$(1)-$(4): prepare-maybe-clean-$(4) \ $$(if $$(findstring $(2), $$(PREPARE_TARGETS)), \ $$(if $$(findstring $(3), $$(PREPARE_HOST)), \ $$(call PREPARE_DIR,$$(PREPARE_WORKING_DEST_LIB_DIR)) \ + $$(call PREPARE_DIR,$$(PREPARE_DEST_BIN_DIR)) \ $$(foreach crate,$$(TARGET_CRATES), \ $$(if $$(or $$(findstring 1, $$(ONLY_RLIB_$$(crate))),$$(findstring 1,$$(CFG_INSTALL_ONLY_RLIB_$(2)))),, \ $$(call PREPARE_LIB,$$(call CFG_LIB_GLOB_$(2),$$(crate)))) \ @@ -141,7 +147,10 @@ prepare-target-$(2)-host-$(3)-$(1)-$(4): prepare-maybe-clean-$(4) \ $$(foreach crate,$$(HOST_CRATES), \ $$(call PREPARE_LIB,$$(call CFG_LIB_GLOB_$(2),$$(crate)))),) \ $$(foreach object,$$(INSTALLED_OBJECTS_$(2)),\ - $$(call PREPARE_LIB,$$(object))),),),) + $$(call PREPARE_LIB,$$(object))) \ + $$(foreach bin,$$(INSTALLED_BINS_$(3)),\ + $$(call PREPARE_BIN,$$(bin))) \ + ,),),) endef define INSTALL_GDB_DEBUGGER_SCRIPTS_COMMANDS