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