From 315750ac92a8114a96b35352ec88f82d21d5fbec Mon Sep 17 00:00:00 2001 From: Ricky Taylor Date: Wed, 4 Mar 2015 22:58:59 +0000 Subject: [PATCH] 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 | 15 +- 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, 1534 insertions(+), 28 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 9b9de9da0671..4e150488224b 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 7767129a5e21..8cd09fa9043c 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 d7a1405c3d0a..9e0245e093d8 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 6637423e4951..88d7700db820 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 fdd38ba75fe5..a66f70f6305e 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 4118ea26c072..10aaf137e8b3 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 000000000000..3235dcece6d4 --- /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 9545a1fb52d9..01865319b3fa 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)) + 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)) + 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 b999929c4af9..4154ee29388c 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 18e67d066d03..796aa3b08ca4 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 21094ad905e9..425077e97388 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 c5f1882fa1dd..de8086bec4f6 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 000000000000..f53f0df0e639 --- /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 000000000000..1cbf7aabeefe --- /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 2a2aa2bf4cf9..74d7dc95b24a 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 38ad909dd012..aa51afac1a4a 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 000000000000..b1582a1a3c59 --- /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 000000000000..0237ac1231cd --- /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 3e2db80a9c55..97c672df148f 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 4879975dde69..413e5e1f49ff 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 503bdf8dadb9..989ef8d8bf45 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 1ee3aab2727a..934cf056ec19 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 398afcb553c1..a09a82b85522 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 3c36f0f1d490..885adf19ca1f 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 0d26206f26bc..d7eede6e953b 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 000000000000..b71db510c00b --- /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 c755cf67caa9..ac925e4260a5 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() {