diff --git a/Makefile.in b/Makefile.in index 8fc66a764da61..d3bb5a541a470 100644 --- a/Makefile.in +++ b/Makefile.in @@ -266,8 +266,3 @@ ifneq ($(strip $(findstring TAGS.emacs,$(MAKECMDGOALS)) \ CFG_INFO := $(info cfg: including ctags rules) include $(CFG_SRC_DIR)mk/ctags.mk endif - -# Find all of the .d files and include them to add information about -# header file dependencies. -ALL_DEP_FILES := $(ALL_OBJ_FILES:%.o=%.d) --include $(ALL_DEP_FILES) diff --git a/configure b/configure index 9b9de9da06716..29bddf687b2d2 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 @@ -1083,6 +1084,65 @@ do err "musl libc $CFG_MUSL_ROOT/lib/libc.a not found" fi ;; + + x86_64-*-msvc) + # Currently the build system is not configured to build jemalloc + # with MSVC, so we omit this optional dependency. + step_msg "targeting MSVC, disabling jemalloc" + CFG_DISABLE_JEMALLOC=1 + putvar CFG_DISABLE_JEMALLOC + + # There are some MSYS python builds which will auto-translate + # windows-style paths to MSYS-style paths in Python itself. + # Unfortunately this breaks LLVM's build system as somewhere along + # the line LLVM prints a path into a file from Python and then CMake + # later tries to interpret that path. If Python prints a MSYS path + # and CMake tries to use it as a Windows path, you're gonna have a + # Bad Time. + # + # Consequently here we try to detect when that happens and print an + # error if it does. + if $CFG_PYTHON -c 'import sys; print sys.argv[1]' `pwd` | grep '^/' + then + err "python is silently translating windows paths to MSYS paths \ + and the build will fail if this python is used.\n\n \ + Either an official python install must be used or an \ + alternative python package in MinGW must be used." + fi + + # MSVC requires cmake because that's how we're going to build LLVM + probe_need CFG_CMAKE cmake + + # Use the REG program to figure out where VS is installed + # We need to figure out where cl.exe and link.exe are, so we do some + # munging and some probing here. We also look for the default + # INCLUDE and LIB variables for MSVC so we can set those in the + # build system as well. + install=$(reg QUERY \ + 'HKLM\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\12.0' \ + -v InstallDir) + need_ok "couldn't find visual studio install root" + CFG_MSVC_ROOT=$(echo "$install" | grep InstallDir | sed 's/.*REG_SZ[ ]*//') + CFG_MSVC_ROOT=$(dirname "$CFG_MSVC_ROOT") + CFG_MSVC_ROOT=$(dirname "$CFG_MSVC_ROOT") + CFG_MSVC_CL="${CFG_MSVC_ROOT}/VC/bin/amd64/cl.exe" + CFG_MSVC_LIB="${CFG_MSVC_ROOT}/VC/bin/amd64/lib.exe" + CFG_MSVC_LINK="${CFG_MSVC_ROOT}/VC/bin/amd64/link.exe" + + vcvarsall="${CFG_MSVC_ROOT}/VC/vcvarsall.bat" + CFG_MSVC_INCLUDE_PATH=$(cmd /c "\"$vcvarsall\" amd64 && cmd /c echo %INCLUDE%") + need_ok "failed to learn about MSVC's INCLUDE" + CFG_MSVC_LIB_PATH=$(cmd /c "\"$vcvarsall\" amd64 && cmd /c echo %LIB%") + need_ok "failed to learn about MSVC's LIB" + + putvar CFG_MSVC_ROOT + putvar CFG_MSVC_CL + putvar CFG_MSVC_LIB + putvar CFG_MSVC_LINK + putvar CFG_MSVC_INCLUDE_PATH + putvar CFG_MSVC_LIB_PATH + ;; + *) ;; esac @@ -1124,6 +1184,7 @@ do do make_dir $t/rt/stage$s make_dir $t/rt/jemalloc + make_dir $t/rt/compiler-rt for i in \ isaac sync test \ arch/i386 arch/x86_64 arch/arm arch/aarch64 arch/mips arch/powerpc @@ -1287,7 +1348,39 @@ do done fi - if [ ${do_reconfigure} -ne 0 ] + use_cmake=0 + case "$t" in + (*-msvc) + use_cmake=1 + ;; + esac + + if [ ${do_reconfigure} -ne 0 ] && [ ${use_cmake} -ne 0 ] + then + msg "configuring LLVM for $t with cmake" + + CMAKE_ARGS="-DLLVM_INCLUDE_TESTS=OFF" + if [ ! -z "$CFG_DISABLE_OPTIMIZE_LLVM" ]; then + CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_BUILD_TYPE=Debug" + else + CMAKE_ARGS="$CMAKE_ARGS -DCMAKE_BUILD_TYPE=Release" + fi + if [ -z "$CFG_ENABLE_LLVM_ASSERTIONS" ] + then + CMAKE_ARGS="$CMAKE_ARGS -DLLVM_ENABLE_ASSERTIONS=OFF" + else + CMAKE_ARGS="$CMAKE_ARGS -DLLVM_ENABLE_ASSERTIONS=ON" + fi + + msg "configuring LLVM with:" + msg "$CMAKE_ARGS" + (cd $LLVM_BUILD_DIR && "$CFG_CMAKE" $CFG_LLVM_SRC_DIR \ + -G "Visual Studio 12 2013 Win64" \ + $CMAKE_ARGS) + need_ok "LLVM cmake configure failed" + fi + + if [ ${do_reconfigure} -ne 0 ] && [ ${use_cmake} -eq 0 ] then # LLVM's configure doesn't recognize the new Windows triples yet gnu_t=$(to_gnu_triple $t) @@ -1311,7 +1404,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 @@ -1495,11 +1588,6 @@ do putvar $CFG_LLVM_INST_DIR done -# Munge any paths that appear in config.mk back to posix-y -cp config.tmp config.tmp.bak -sed -e 's@ \([a-zA-Z]\):[/\\]@ /\1/@g;' config.tmp -rm -f config.tmp.bak - msg copy_if_changed ${CFG_SRC_DIR}Makefile.in ./Makefile move_if_changed config.tmp config.mk diff --git a/mk/cfg/aarch64-apple-ios.mk b/mk/cfg/aarch64-apple-ios.mk index 7767129a5e218..8cd09fa9043c8 100644 --- a/mk/cfg/aarch64-apple-ios.mk +++ b/mk/cfg/aarch64-apple-ios.mk @@ -5,6 +5,7 @@ ifneq ($(findstring darwin,$(CFG_OSTYPE)),) CFG_IOS_SDK_aarch64-apple-ios := $(shell xcrun --show-sdk-path -sdk iphoneos 2>/dev/null) CFG_IOS_SDK_FLAGS_aarch64-apple-ios := -target aarch64-apple-darwin -isysroot $(CFG_IOS_SDK_aarch64-apple-ios) -mios-version-min=7.0 -arch arm64 CC_aarch64-apple-ios = $(shell xcrun -find -sdk iphoneos clang) +LINK_aarch64-apple-ios = $(shell xcrun -find -sdk iphoneos clang) CXX_aarch64-apple-ios = $(shell xcrun -find -sdk iphoneos clang++) CPP_aarch64-apple-ios = $(shell xcrun -find -sdk iphoneos clang++) AR_aarch64-apple-ios = $(shell xcrun -find -sdk iphoneos ar) diff --git a/mk/cfg/aarch64-linux-android.mk b/mk/cfg/aarch64-linux-android.mk index d7a1405c3d0a8..9e0245e093d8a 100644 --- a/mk/cfg/aarch64-linux-android.mk +++ b/mk/cfg/aarch64-linux-android.mk @@ -1,6 +1,7 @@ # aarch64-linux-android configuration # CROSS_PREFIX_aarch64-linux-android- CC_aarch64-linux-android=$(CFG_ANDROID_CROSS_PATH)/bin/aarch64-linux-android-gcc +LINK_aarch64-linux-android=$(CFG_ANDROID_CROSS_PATH)/bin/aarch64-linux-android-gcc CXX_aarch64-linux-android=$(CFG_ANDROID_CROSS_PATH)/bin/aarch64-linux-android-g++ CPP_aarch64-linux-android=$(CFG_ANDROID_CROSS_PATH)/bin/aarch64-linux-android-gcc -E AR_aarch64-linux-android=$(CFG_ANDROID_CROSS_PATH)/bin/aarch64-linux-android-ar diff --git a/mk/cfg/aarch64-unknown-linux-gnu.mk b/mk/cfg/aarch64-unknown-linux-gnu.mk index 6637423e4951a..88d7700db820f 100644 --- a/mk/cfg/aarch64-unknown-linux-gnu.mk +++ b/mk/cfg/aarch64-unknown-linux-gnu.mk @@ -1,6 +1,7 @@ # aarch64-unknown-linux-gnu configuration CROSS_PREFIX_aarch64-unknown-linux-gnu=aarch64-linux-gnu- CC_aarch64-unknown-linux-gnu=gcc +LINK_aarch64-unknown-linux-gnu=gcc CXX_aarch64-unknown-linux-gnu=g++ CPP_aarch64-unknown-linux-gnu=gcc -E AR_aarch64-unknown-linux-gnu=ar diff --git a/mk/cfg/arm-linux-androideabi.mk b/mk/cfg/arm-linux-androideabi.mk index fdd38ba75fe58..a66f70f6305ed 100644 --- a/mk/cfg/arm-linux-androideabi.mk +++ b/mk/cfg/arm-linux-androideabi.mk @@ -1,4 +1,5 @@ # arm-linux-androideabi configuration +LINK_arm-linux-androideabi=$(CFG_ANDROID_CROSS_PATH)/bin/arm-linux-androideabi-gcc CC_arm-linux-androideabi=$(CFG_ANDROID_CROSS_PATH)/bin/arm-linux-androideabi-gcc CXX_arm-linux-androideabi=$(CFG_ANDROID_CROSS_PATH)/bin/arm-linux-androideabi-g++ CPP_arm-linux-androideabi=$(CFG_ANDROID_CROSS_PATH)/bin/arm-linux-androideabi-gcc -E diff --git a/mk/cfg/x86_64-pc-windows-gnu.mk b/mk/cfg/x86_64-pc-windows-gnu.mk index 4118ea26c072b..10aaf137e8b3a 100644 --- a/mk/cfg/x86_64-pc-windows-gnu.mk +++ b/mk/cfg/x86_64-pc-windows-gnu.mk @@ -1,6 +1,7 @@ # x86_64-pc-windows-gnu configuration CROSS_PREFIX_x86_64-pc-windows-gnu=x86_64-w64-mingw32- CC_x86_64-pc-windows-gnu=gcc +LINK_x86_64-pc-windows-gnu=gcc CXX_x86_64-pc-windows-gnu=g++ CPP_x86_64-pc-windows-gnu=gcc -E AR_x86_64-pc-windows-gnu=ar diff --git a/mk/cfg/x86_64-pc-windows-msvc.mk b/mk/cfg/x86_64-pc-windows-msvc.mk new file mode 100644 index 0000000000000..bd1088b7cd133 --- /dev/null +++ b/mk/cfg/x86_64-pc-windows-msvc.mk @@ -0,0 +1,82 @@ +# x86_64-pc-windows-msvc configuration +CC_x86_64-pc-windows-msvc="$(CFG_MSVC_CL)" -nologo +LINK_x86_64-pc-windows-msvc="$(CFG_MSVC_LINK)" -nologo +CXX_x86_64-pc-windows-msvc="$(CFG_MSVC_CL)" -nologo +CPP_x86_64-pc-windows-msvc="$(CFG_MSVC_CL)" -nologo +AR_x86_64-pc-windows-msvc="$(CFG_MSVC_LIB)" -nologo +CFG_LIB_NAME_x86_64-pc-windows-msvc=$(1).dll +CFG_STATIC_LIB_NAME_x86_64-pc-windows-msvc=$(1).lib +CFG_LIB_GLOB_x86_64-pc-windows-msvc=$(1)-*.dll +CFG_LIB_DSYM_GLOB_x86_64-pc-windows-msvc=$(1)-*.dylib.dSYM +CFG_JEMALLOC_CFLAGS_x86_64-pc-windows-msvc := +CFG_GCCISH_CFLAGS_x86_64-pc-windows-msvc := +CFG_GCCISH_CXXFLAGS_x86_64-pc-windows-msvc := +CFG_GCCISH_LINK_FLAGS_x86_64-pc-windows-msvc := +CFG_GCCISH_DEF_FLAG_x86_64-pc-windows-msvc := +CFG_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_LDPATH_x86_64-pc-windows-msvc := +CFG_RUN_x86_64-pc-windows-msvc=$(2) +CFG_RUN_TARG_x86_64-pc-windows-msvc=$(call CFG_RUN_x86_64-pc-windows-msvc,,$(2)) +CFG_GNU_TRIPLE_x86_64-pc-windows-msvc := x86_64-pc-win32 + +# These two environment variables are scraped by the `./configure` script and +# are necessary for `cl.exe` to find standard headers (the INCLUDE variable) and +# for `link.exe` to find standard libraries (the LIB variable). +ifdef CFG_MSVC_INCLUDE_PATH +export INCLUDE := $(CFG_MSVC_INCLUDE_PATH) +endif +ifdef CFG_MSVC_LIB_PATH +export LIB := $(CFG_MSVC_LIB_PATH) +endif + +# Unfortunately `link.exe` is also a program in `/usr/bin` on MinGW installs, +# but it's not the one that we want. As a result we make sure that our detected +# `link.exe` shows up in PATH first. +ifdef CFG_MSVC_LINK +export PATH := $(CFG_MSVC_ROOT)/VC/bin/amd64:$(PATH) +endif + +# There are more comments about this available in the target specification for +# Windows MSVC in the compiler, but the gist of it is that we use `llvm-ar.exe` +# instead of `lib.exe` for assembling archives, so we need to inject this custom +# dependency here. +NATIVE_TOOL_DEPS_core_T_x86_64-pc-windows-msvc += llvm-ar.exe +INSTALLED_BINS_x86_64-pc-windows-msvc += llvm-ar.exe + +# When working with MSVC on windows, each DLL needs to explicitly declare its +# interface to the outside world through some means. The options for doing so +# include: +# +# 1. A custom attribute on each function itself +# 2. A linker argument saying what to export +# 3. A file which lists all symbols that need to be exported +# +# The Rust compiler takes care (1) for us for all Rust code by annotating all +# public-facing functions with dllexport, but we have a few native dependencies +# which need to cross the DLL boundary. The most important of these dependencies +# is LLVM which is linked into `rustc_llvm.dll` but primarily used from +# `rustc_trans.dll`. This means that many of LLVM's C API functions need to be +# exposed from `rustc_llvm.dll` to be forwarded over the boundary. +# +# Unfortunately, at this time, LLVM does not handle this sort of exportation on +# Windows for us, so we're forced to do it ourselves if we want it (which seems +# like the path of least resistance right now). To do this we generate a `.DEF` +# file [1] which we then custom-pass to the linker when building the rustc_llvm +# crate. This DEF file list all symbols that are exported from +# `src/librustc_llvm/lib.rs` and is generated by a small python script. +# +# Fun times! +# +# [1]: https://msdn.microsoft.com/en-us/library/28d6s79h.aspx +RUSTFLAGS_rustc_llvm_T_x86_64-pc-windows-msvc += \ + -C link-args="-DEF:x86_64-pc-windows-msvc/rt/rustc_llvm.def" +CUSTOM_DEPS_rustc_llvm_T_x86_64-pc-windows-msvc += \ + x86_64-pc-windows-msvc/rt/rustc_llvm.def + +x86_64-pc-windows-msvc/rt/rustc_llvm.def: $(S)src/etc/mklldef.py \ + $(S)src/librustc_llvm/lib.rs + $(CFG_PYTHON) $^ $@ rustc_llvm-$(CFG_FILENAME_EXTRA) diff --git a/mk/clean.mk b/mk/clean.mk index 5b90d41ceeceb..c04ef89ebc58a 100644 --- a/mk/clean.mk +++ b/mk/clean.mk @@ -118,16 +118,3 @@ $(foreach host, $(CFG_HOST), \ $(eval $(foreach target, $(CFG_TARGET), \ $(eval $(foreach stage, 0 1 2 3, \ $(eval $(call CLEAN_TARGET_STAGE_N,$(stage),$(target),$(host)))))))) - -define DEF_CLEAN_LLVM_HOST -ifeq ($(CFG_LLVM_ROOT),) -clean-llvm$(1): - $$(Q)$$(MAKE) -C $$(CFG_LLVM_BUILD_DIR_$(1)) clean -else -clean-llvm$(1): ; - -endif -endef - -$(foreach host, $(CFG_HOST), \ - $(eval $(call DEF_CLEAN_LLVM_HOST,$(host)))) diff --git a/mk/crates.mk b/mk/crates.mk index 546b16c1b850b..62dc1019066f0 100644 --- a/mk/crates.mk +++ b/mk/crates.mk @@ -125,9 +125,6 @@ ONLY_RLIB_rustc_bitflags := 1 # Documented-by-default crates DOC_CRATES := std alloc collections core libc rustc_unicode -# Installed objects/libraries by default -INSTALLED_OBJECTS := libmorestack.a libcompiler-rt.a - ################################################################################ # You should not need to edit below this line ################################################################################ diff --git a/mk/dist.mk b/mk/dist.mk index 75c6219c5f04f..9aab51daba914 100644 --- a/mk/dist.mk +++ b/mk/dist.mk @@ -144,9 +144,11 @@ dist/$$(PKG_NAME)-$(1).tar.gz: dist-install-dir-$(1) prepare-overlay-$(1) @$(call E, build: $$@) # Copy essential gcc components into installer ifdef CFG_WINDOWSY_$(1) +ifeq ($$(findstring gnu,$(1)),gnu) $$(Q)rm -Rf tmp/dist/win-rust-gcc-$(1) $$(Q)$$(CFG_PYTHON) $$(S)src/etc/make-win-dist.py tmp/dist/$$(PKG_NAME)-$(1)-image tmp/dist/win-rust-gcc-$(1) $(1) $$(Q)cp -r $$(S)src/etc/third-party tmp/dist/$$(PKG_NAME)-$(1)-image/share/doc/ +endif endif $$(Q)$$(S)src/rust-installer/gen-installer.sh \ --product-name=Rust \ @@ -213,7 +215,14 @@ endif dist-install-dirs: $(foreach host,$(CFG_HOST),dist-install-dir-$(host)) ifdef CFG_WINDOWSY_$(CFG_BUILD) -MAYBE_MINGW_TARBALLS=$(foreach host,$(CFG_HOST),dist/$(MINGW_PKG_NAME)-$(host).tar.gz) +define BUILD_MINGW_TARBALL +ifeq ($$(findstring gnu,$(1)),gnu) +MAYBE_MINGW_TARBALLS += dist/$(MINGW_PKG_NAME)-$(1).tar.gz +endif +endef + +$(foreach host,$(CFG_HOST),\ + $(eval $(call BUILD_MINGW_TARBALL,$(host)))) endif ifeq ($(CFG_DISABLE_DOCS),) diff --git a/mk/llvm.mk b/mk/llvm.mk index 1861dd313ce71..d5b608e88daf8 100644 --- a/mk/llvm.mk +++ b/mk/llvm.mk @@ -19,6 +19,12 @@ LLVM_DEPS_INC=$(call rwildcard,$(CFG_LLVM_SRC_DIR)include,*cpp *hpp) LLVM_DEPS=$(LLVM_DEPS_SRC) $(LLVM_DEPS_INC) endif +ifdef CFG_DISABLE_OPTIMIZE_LLVM +LLVM_BUILD_CONFIG_MODE := Debug +else +LLVM_BUILD_CONFIG_MODE := Release +endif + define DEF_LLVM_RULES # If CFG_LLVM_ROOT is defined then we don't build LLVM ourselves @@ -26,12 +32,34 @@ ifeq ($(CFG_LLVM_ROOT),) LLVM_STAMP_$(1) = $$(CFG_LLVM_BUILD_DIR_$(1))/llvm-auto-clean-stamp +ifeq ($$(findstring msvc,$(1)),msvc) + +$$(LLVM_CONFIG_$(1)): $$(LLVM_DEPS) $$(LLVM_STAMP_$(1)) + @$$(call E, cmake: llvm) + $$(Q)$$(CFG_CMAKE) --build $$(CFG_LLVM_BUILD_DIR_$(1)) \ + --config $$(LLVM_BUILD_CONFIG_MODE) + $$(Q)touch $$(LLVM_CONFIG_$(1)) + +clean-llvm$(1): + +else + $$(LLVM_CONFIG_$(1)): $$(LLVM_DEPS) $$(LLVM_STAMP_$(1)) @$$(call E, make: llvm) $$(Q)$$(MAKE) -C $$(CFG_LLVM_BUILD_DIR_$(1)) $$(CFG_LLVM_BUILD_ENV_$(1)) ONLY_TOOLS="$$(LLVM_TOOLS)" $$(Q)touch $$(LLVM_CONFIG_$(1)) + +clean-llvm$(1): + $$(Q)$$(MAKE) -C $$(CFG_LLVM_BUILD_DIR_$(1)) clean + endif +else +clean-llvm$(1): +endif + +$$(LLVM_AR_$(1)): $$(LLVM_CONFIG_$(1)) + # This is used to independently force an LLVM clean rebuild # when we changed something not otherwise captured by builtin # dependencies. In these cases, commit a change that touches @@ -66,7 +94,7 @@ $(foreach host,$(CFG_HOST), \ # This can't be done in target.mk because it's included before this file. define LLVM_LINKAGE_DEPS -$$(TLIB$(1)_T_$(2)_H_$(3))/stamp.rustc_llvm: $$(LLVM_LINKAGE_PATH_$(3)) +$$(TLIB$(1)_T_$(2)_H_$(3))/stamp.rustc_llvm: $$(LLVM_LINKAGE_PATH_$(2)) endef $(foreach source,$(CFG_HOST), \ diff --git a/mk/main.mk b/mk/main.mk index 738580cb5b5e0..bf96d8f660f1c 100644 --- a/mk/main.mk +++ b/mk/main.mk @@ -85,10 +85,6 @@ CFG_INFO := $(info cfg: version $(CFG_VERSION)) # More configuration ###################################################################### -# We track all of the object files we might build so that we can find -# and include all of the .d files in one fell swoop. -ALL_OBJ_FILES := - MKFILE_DEPS := config.stamp $(call rwildcard,$(CFG_SRC_DIR)mk/,*) MKFILES_FOR_TARBALL:=$(MKFILE_DEPS) ifneq ($(NO_MKFILE_DEPS),) @@ -296,6 +292,7 @@ endif # Any rules that depend on LLVM should depend on LLVM_CONFIG LLVM_CONFIG_$(1):=$$(CFG_LLVM_INST_DIR_$(1))/bin/llvm-config$$(X_$(1)) LLVM_MC_$(1):=$$(CFG_LLVM_INST_DIR_$(1))/bin/llvm-mc$$(X_$(1)) +LLVM_AR_$(1):=$$(CFG_LLVM_INST_DIR_$(1))/bin/llvm-ar$$(X_$(1)) LLVM_VERSION_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --version) LLVM_BINDIR_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --bindir) LLVM_INCDIR_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --includedir) @@ -303,9 +300,13 @@ LLVM_LIBDIR_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --libdir) LLVM_LIBDIR_RUSTFLAGS_$(1)=-L "$$(LLVM_LIBDIR_$(1))" LLVM_LIBS_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --libs $$(LLVM_COMPONENTS)) LLVM_LDFLAGS_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --ldflags) +ifeq ($$(findstring freebsd,$(1)),freebsd) # On FreeBSD, it may search wrong headers (that are for pre-installed LLVM), # so we replace -I with -iquote to ensure that it searches bundled LLVM first. LLVM_CXXFLAGS_$(1)=$$(subst -I, -iquote , $$(shell "$$(LLVM_CONFIG_$(1))" --cxxflags)) +else +LLVM_CXXFLAGS_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --cxxflags) +endif LLVM_HOST_TRIPLE_$(1)=$$(shell "$$(LLVM_CONFIG_$(1))" --host-target) LLVM_AS_$(1)=$$(CFG_LLVM_INST_DIR_$(1))/bin/llvm-as$$(X_$(1)) @@ -400,8 +401,6 @@ endif # Prerequisites for using the stageN compiler to build target artifacts TSREQ$(1)_T_$(2)_H_$(3) = \ $$(HSREQ$(1)_H_$(3)) \ - $$(foreach obj,$$(INSTALLED_OBJECTS),\ - $$(TLIB$(1)_T_$(2)_H_$(3))/$$(obj)) \ $$(foreach obj,$$(INSTALLED_OBJECTS_$(2)),\ $$(TLIB$(1)_T_$(2)_H_$(3))/$$(obj)) diff --git a/mk/platform.mk b/mk/platform.mk index 9545a1fb52d9e..26a6db1c5bd1a 100644 --- a/mk/platform.mk +++ b/mk/platform.mk @@ -90,10 +90,6 @@ ifneq ($(findstring linux,$(CFG_OSTYPE)),) endif endif -# These flags will cause the compiler to produce a .d file -# next to the .o file that lists header deps. -CFG_DEPEND_FLAGS = -MMD -MP -MT $(1) -MF $(1:%.o=%.d) - AR := ar define SET_FROM_CFG @@ -116,6 +112,14 @@ CFG_RLIB_GLOB=lib$(1)-*.rlib include $(wildcard $(CFG_SRC_DIR)mk/cfg/*.mk) +define ADD_INSTALLED_OBJECTS + INSTALLED_OBJECTS_$(1) += $$(call CFG_STATIC_LIB_NAME_$(1),morestack) \ + $$(call CFG_STATIC_LIB_NAME_$(1),compiler-rt) +endef + +$(foreach target,$(CFG_TARGET), \ + $(eval $(call ADD_INSTALLED_OBJECTS,$(target)))) + # The -Qunused-arguments sidesteps spurious warnings from clang define FILTER_FLAGS ifeq ($$(CFG_USING_CLANG),1) @@ -129,6 +133,21 @@ endef $(foreach target,$(CFG_TARGET), \ $(eval $(call FILTER_FLAGS,$(target)))) +# Configure various macros to pass gcc or cl.exe style arguments +define CC_MACROS + CFG_CC_INCLUDE_$(1)=-I $$(1) + ifeq ($$(findstring msvc,$(1)),msvc) + CFG_CC_OUTPUT_$(1)=-Fo:$$(1) + CFG_CREATE_ARCHIVE_$(1)=$$(AR_$(1)) -OUT:$$(1) + else + CFG_CC_OUTPUT_$(1)=-o $$(1) + CFG_CREATE_ARCHIVE_$(1)=$$(AR_$(1)) crus $$(1) + endif +endef + +$(foreach target,$(CFG_TARGET), \ + $(eval $(call CC_MACROS,$(target)))) + ifeq ($(CFG_CCACHE_CPP2),1) CCACHE_CPP2=1 @@ -145,21 +164,23 @@ FIND_COMPILER = $(word 1,$(1:ccache=)) define CFG_MAKE_TOOLCHAIN # Prepend the tools with their prefix if cross compiling ifneq ($(CFG_BUILD),$(1)) + ifneq ($$(findstring msvc,$(1)),msvc) CC_$(1)=$(CROSS_PREFIX_$(1))$(CC_$(1)) CXX_$(1)=$(CROSS_PREFIX_$(1))$(CXX_$(1)) CPP_$(1)=$(CROSS_PREFIX_$(1))$(CPP_$(1)) AR_$(1)=$(CROSS_PREFIX_$(1))$(AR_$(1)) - RUSTC_CROSS_FLAGS_$(1)=-C linker=$$(call FIND_COMPILER,$$(CC_$(1))) \ + LINK_$(1)=$(CROSS_PREFIX_$(1))$(LINK_$(1)) + RUSTC_CROSS_FLAGS_$(1)=-C linker=$$(call FIND_COMPILER,$$(LINK_$(1))) \ -C ar=$$(call FIND_COMPILER,$$(AR_$(1))) $(RUSTC_CROSS_FLAGS_$(1)) RUSTC_FLAGS_$(1)=$$(RUSTC_CROSS_FLAGS_$(1)) $(RUSTC_FLAGS_$(1)) + endif endif CFG_COMPILE_C_$(1) = $$(CC_$(1)) \ $$(CFG_GCCISH_CFLAGS) \ $$(CFG_GCCISH_CFLAGS_$(1)) \ - $$(CFG_DEPEND_FLAGS) \ - -c -o $$(1) $$(2) + -c $$(call CFG_CC_OUTPUT_$(1),$$(1)) $$(2) CFG_LINK_C_$(1) = $$(CC_$(1)) \ $$(CFG_GCCISH_LINK_FLAGS) -o $$(1) \ $$(CFG_GCCISH_LINK_FLAGS_$(1)) \ @@ -170,8 +191,7 @@ define CFG_MAKE_TOOLCHAIN $$(CFG_GCCISH_CXXFLAGS) \ $$(CFG_GCCISH_CFLAGS_$(1)) \ $$(CFG_GCCISH_CXXFLAGS_$(1)) \ - $$(CFG_DEPEND_FLAGS) \ - -c -o $$(1) $$(2) + -c $$(call CFG_CC_OUTPUT_$(1),$$(1)) $$(2) CFG_LINK_CXX_$(1) = $$(CXX_$(1)) \ $$(CFG_GCCISH_LINK_FLAGS) -o $$(1) \ $$(CFG_GCCISH_LINK_FLAGS_$(1)) \ @@ -189,7 +209,7 @@ define CFG_MAKE_TOOLCHAIN # We're using llvm-mc as our assembler because it supports # .cfi pseudo-ops on mac - CFG_ASSEMBLE_$(1)=$$(CPP_$(1)) -E $$(CFG_DEPEND_FLAGS) $$(2) | \ + CFG_ASSEMBLE_$(1)=$$(CPP_$(1)) -E $$(2) | \ $$(LLVM_MC_$$(CFG_BUILD)) \ -assemble \ -relocation-model=$$(LLVM_MC_RELOCATION_MODEL) \ @@ -201,7 +221,7 @@ define CFG_MAKE_TOOLCHAIN # For the ARM, AARCH64, MIPS and POWER crosses, use the toolchain assembler # FIXME: We should be able to use the LLVM assembler CFG_ASSEMBLE_$(1)=$$(CC_$(1)) $$(CFG_GCCISH_CFLAGS_$(1)) \ - $$(CFG_DEPEND_FLAGS) $$(2) -c -o $$(1) + $$(2) -c -o $$(1) endif diff --git a/mk/prepare.mk b/mk/prepare.mk index 1ae6a61c95a8c..573b7ac79fd1a 100644 --- a/mk/prepare.mk +++ b/mk/prepare.mk @@ -36,7 +36,10 @@ endef # Copy an executable # $(1) is the filename/libname-glob +# +# Gee, what's up with that $(nop)? See comment below. define PREPARE_BIN + $(nop) @$(call E, prepare: $(PREPARE_DEST_BIN_DIR)/$(1)) $(Q)$(PREPARE_BIN_CMD) $(PREPARE_SOURCE_BIN_DIR)/$(1) $(PREPARE_DEST_BIN_DIR)/$(1) endef @@ -119,6 +122,8 @@ define DEF_PREPARE_TARGET_N # Rebind PREPARE_*_LIB_DIR to point to rustlib, then install the libs for the targets prepare-target-$(2)-host-$(3)-$(1)-$(4): PREPARE_WORKING_SOURCE_LIB_DIR=$$(PREPARE_SOURCE_LIB_DIR)/rustlib/$(2)/lib prepare-target-$(2)-host-$(3)-$(1)-$(4): PREPARE_WORKING_DEST_LIB_DIR=$$(PREPARE_DEST_LIB_DIR)/rustlib/$(2)/lib +prepare-target-$(2)-host-$(3)-$(1)-$(4): PREPARE_SOURCE_BIN_DIR=$$(PREPARE_SOURCE_LIB_DIR)/rustlib/$(3)/bin +prepare-target-$(2)-host-$(3)-$(1)-$(4): PREPARE_DEST_BIN_DIR=$$(PREPARE_DEST_LIB_DIR)/rustlib/$(3)/bin prepare-target-$(2)-host-$(3)-$(1)-$(4): prepare-maybe-clean-$(4) \ $$(foreach crate,$$(TARGET_CRATES), \ $$(TLIB$(1)_T_$(2)_H_$(3))/stamp.$$(crate)) \ @@ -133,6 +138,7 @@ prepare-target-$(2)-host-$(3)-$(1)-$(4): prepare-maybe-clean-$(4) \ $$(if $$(findstring $(2), $$(PREPARE_TARGETS)), \ $$(if $$(findstring $(3), $$(PREPARE_HOST)), \ $$(call PREPARE_DIR,$$(PREPARE_WORKING_DEST_LIB_DIR)) \ + $$(call PREPARE_DIR,$$(PREPARE_DEST_BIN_DIR)) \ $$(foreach crate,$$(TARGET_CRATES), \ $$(if $$(or $$(findstring 1, $$(ONLY_RLIB_$$(crate))),$$(findstring 1,$$(CFG_INSTALL_ONLY_RLIB_$(2)))),, \ $$(call PREPARE_LIB,$$(call CFG_LIB_GLOB_$(2),$$(crate)))) \ @@ -140,8 +146,11 @@ prepare-target-$(2)-host-$(3)-$(1)-$(4): prepare-maybe-clean-$(4) \ $$(if $$(findstring $(2),$$(CFG_HOST)), \ $$(foreach crate,$$(HOST_CRATES), \ $$(call PREPARE_LIB,$$(call CFG_LIB_GLOB_$(2),$$(crate)))),) \ - $$(foreach object,$$(INSTALLED_OBJECTS) $$(INSTALLED_OBJECTS_$(2)),\ - $$(call PREPARE_LIB,$$(object))),),),) + $$(foreach object,$$(INSTALLED_OBJECTS_$(2)),\ + $$(call PREPARE_LIB,$$(object))) \ + $$(foreach bin,$$(INSTALLED_BINS_$(3)),\ + $$(call PREPARE_BIN,$$(bin))) \ + ,),),) endef define INSTALL_GDB_DEBUGGER_SCRIPTS_COMMANDS diff --git a/mk/rt.mk b/mk/rt.mk index bd6578d3b724b..777a2a0fd3b4b 100644 --- a/mk/rt.mk +++ b/mk/rt.mk @@ -81,8 +81,8 @@ $$(RT_OUTPUT_DIR_$(1))/%.o: $(S)src/rt/%.c $$(MKFILE_DEPS) @mkdir -p $$(@D) @$$(call E, compile: $$@) $$(Q)$$(call CFG_COMPILE_C_$(1), $$@, \ - -I $$(S)src/rt/hoedown/src \ - -I $$(S)src/rt \ + $$(call CFG_CC_INCLUDE_$(1),$$(S)src/rt/hoedown/src) \ + $$(call CFG_CC_INCLUDE_$(1),$$(S)src/rt) \ $$(RUNTIME_CFLAGS_$(1))) $$< $$(RT_OUTPUT_DIR_$(1))/%.o: $(S)src/rt/%.S $$(MKFILE_DEPS) \ @@ -109,12 +109,7 @@ OBJS_$(2)_$(1) := $$(OBJS_$(2)_$(1):.S=.o) NATIVE_$(2)_$(1) := $$(call CFG_STATIC_LIB_NAME_$(1),$(2)) $$(RT_OUTPUT_DIR_$(1))/$$(NATIVE_$(2)_$(1)): $$(OBJS_$(2)_$(1)) @$$(call E, link: $$@) - $$(Q)$$(AR_$(1)) rcs $$@ $$^ - -ifeq ($$(findstring windows,$(1)),windows) -$$(RT_OUTPUT_DIR_$(1))/lib$(2).a: $$(RT_OUTPUT_DIR_$(1))/$$(NATIVE_$(2)_$(1)) - $$(Q)cp $$^ $$@ -endif + $$(Q)$$(call CFG_CREATE_ARCHIVE_$(1),$$@) $$^ endef @@ -227,22 +222,36 @@ COMPRT_DEPS := $(wildcard \ $(S)src/compiler-rt/*/*/*/*) endif -COMPRT_NAME_$(1) := libcompiler-rt.a +COMPRT_NAME_$(1) := $$(call CFG_STATIC_LIB_NAME_$(1),compiler-rt) COMPRT_LIB_$(1) := $$(RT_OUTPUT_DIR_$(1))/$$(COMPRT_NAME_$(1)) COMPRT_BUILD_DIR_$(1) := $$(RT_OUTPUT_DIR_$(1))/compiler-rt +# Note that on MSVC-targeting builds we hardwire CC/AR to gcc/ar even though +# we're targeting MSVC. This is because although compiler-rt has a CMake build +# config I can't actually figure out how to use it, so I'm not sure how to use +# cl.exe to build the objects. Additionally, the compiler-rt library when built +# with gcc has the same ABI as cl.exe, so they're largely compatible +COMPRT_CC_$(1) := $$(CC_$(1)) +COMPRT_AR_$(1) := $$(AR_$(1)) +COMPRT_CFLAGS_$(1) := $$(CFG_GCCISH_CFLAGS_$(1)) +ifeq ($$(findstring msvc,$(1)),msvc) +COMPRT_CC_$(1) := gcc +COMPRT_AR_$(1) := ar +COMPRT_CFLAGS_$(1) := $$(CFG_GCCISH_CFLAGS_$(1)) -m64 +endif + $$(COMPRT_LIB_$(1)): $$(COMPRT_DEPS) $$(MKFILE_DEPS) @$$(call E, make: compiler-rt) $$(Q)$$(MAKE) -C "$(S)src/compiler-rt" \ ProjSrcRoot="$(S)src/compiler-rt" \ ProjObjRoot="$$(abspath $$(COMPRT_BUILD_DIR_$(1)))" \ - CC="$$(CC_$(1))" \ - AR="$$(AR_$(1))" \ - RANLIB="$$(AR_$(1)) s" \ - CFLAGS="$$(CFG_GCCISH_CFLAGS_$(1))" \ + CC='$$(COMPRT_CC_$(1))' \ + AR='$$(COMPRT_AR_$(1))' \ + RANLIB='$$(COMPRT_AR_$(1)) s' \ + CFLAGS="$$(COMPRT_CFLAGS_$(1))" \ TargetTriple=$(1) \ triple-builtins - $$(Q)cp $$(COMPRT_BUILD_DIR_$(1))/triple/builtins/libcompiler_rt.a $$(COMPRT_LIB_$(1)) + $$(Q)cp $$(COMPRT_BUILD_DIR_$(1))/triple/builtins/libcompiler_rt.a $$@ ################################################################################ # libbacktrace diff --git a/mk/rustllvm.mk b/mk/rustllvm.mk index 363022e8781d0..50d993701421e 100644 --- a/mk/rustllvm.mk +++ b/mk/rustllvm.mk @@ -18,27 +18,41 @@ define DEF_RUSTLLVM_TARGETS # to find the llvm includes (probably because we're not actually installing # llvm, but using it straight out of the build directory) ifdef CFG_WINDOWSY_$(1) -LLVM_EXTRA_INCDIRS_$(1)= -iquote $(S)src/llvm/include \ - -iquote $$(CFG_LLVM_BUILD_DIR_$(1))/include +LLVM_EXTRA_INCDIRS_$(1)= $$(call CFG_CC_INCLUDE_$(1),$(S)src/llvm/include) \ + $$(call CFG_CC_INCLUDE_$(1),\ + $$(CFG_LLVM_BUILD_DIR_$(1))/include) endif RUSTLLVM_OBJS_CS_$(1) := $$(addprefix rustllvm/, \ ExecutionEngineWrapper.cpp RustWrapper.cpp PassWrapper.cpp) RUSTLLVM_INCS_$(1) = $$(LLVM_EXTRA_INCDIRS_$(1)) \ - -iquote $$(LLVM_INCDIR_$(1)) \ - -iquote $$(S)src/rustllvm/include + $$(call CFG_CC_INCLUDE_$(1),$$(LLVM_INCDIR_$(1))) \ + $$(call CFG_CC_INCLUDE_$(1),$$(S)src/rustllvm/include) RUSTLLVM_OBJS_OBJS_$(1) := $$(RUSTLLVM_OBJS_CS_$(1):rustllvm/%.cpp=$(1)/rustllvm/%.o) -ALL_OBJ_FILES += $$(RUSTLLVM_OBJS_OBJS_$(1)) + +# Note that we appease `cl.exe` and its need for some sort of exception +# handling flag with the `EHsc` argument here as well. +ifeq ($$(findstring msvc,$(1)),msvc) +EXTRA_RUSTLLVM_CXXFLAGS_$(1) := //EHsc +endif $$(RT_OUTPUT_DIR_$(1))/$$(call CFG_STATIC_LIB_NAME_$(1),rustllvm): \ $$(RUSTLLVM_OBJS_OBJS_$(1)) @$$(call E, link: $$@) - $$(Q)$$(AR_$(1)) rcs $$@ $$(RUSTLLVM_OBJS_OBJS_$(1)) + $$(Q)$$(call CFG_CREATE_ARCHIVE_$(1),$$@) $$^ +# On MSVC we need to double-escape arguments that llvm-config printed which +# start with a '/'. The shell we're running in will auto-translate the argument +# `/foo` to `C:/msys64/foo` but we really want it to be passed through as `/foo` +# so the argument passed to our shell must be `//foo`. $(1)/rustllvm/%.o: $(S)src/rustllvm/%.cpp $$(MKFILE_DEPS) $$(LLVM_CONFIG_$(1)) @$$(call E, compile: $$@) - $$(Q)$$(call CFG_COMPILE_CXX_$(1), $$@, $$(LLVM_CXXFLAGS_$(1)) $$(RUSTLLVM_INCS_$(1))) $$< + $$(Q)$$(call CFG_COMPILE_CXX_$(1), $$@,) \ + $$(subst /,//,$$(LLVM_CXXFLAGS_$(1))) \ + $$(EXTRA_RUSTLLVM_CXXFLAGS_$(1)) \ + $$(RUSTLLVM_INCS_$(1)) \ + $$< endef # Instantiate template for all stages diff --git a/mk/target.mk b/mk/target.mk index 319f44fd35b77..c8efb8e86d6d7 100644 --- a/mk/target.mk +++ b/mk/target.mk @@ -37,7 +37,10 @@ CRATE_FULLDEPS_$(1)_T_$(2)_H_$(3)_$(4) := \ $$(foreach dep,$$(NATIVE_DEPS_$(4)), \ $$(RT_OUTPUT_DIR_$(2))/$$(call CFG_STATIC_LIB_NAME_$(2),$$(dep))) \ $$(foreach dep,$$(NATIVE_DEPS_$(4)_T_$(2)), \ - $$(RT_OUTPUT_DIR_$(2))/$$(dep)) + $$(RT_OUTPUT_DIR_$(2))/$$(dep)) \ + $$(foreach dep,$$(NATIVE_TOOL_DEPS_$(4)_T_$(2)), \ + $$(TBIN$(1)_T_$(3)_H_$(3))/$$(dep)) \ + $$(CUSTOM_DEPS_$(4)_T_$(2)) endef $(foreach host,$(CFG_HOST), \ @@ -83,13 +86,14 @@ $$(TLIB$(1)_T_$(2)_H_$(3))/stamp.$(4): \ $$(dir $$@)$$(call CFG_LIB_GLOB_$(2),$(4))) $$(call REMOVE_ALL_OLD_GLOB_MATCHES, \ $$(dir $$@)$$(call CFG_RLIB_GLOB,$(4))) - $(Q)CFG_LLVM_LINKAGE_FILE=$$(LLVM_LINKAGE_PATH_$(3)) \ + $(Q)CFG_LLVM_LINKAGE_FILE=$$(LLVM_LINKAGE_PATH_$(2)) \ $$(subst @,,$$(STAGE$(1)_T_$(2)_H_$(3))) \ $$(RUST_LIB_FLAGS_ST$(1)) \ -L "$$(RT_OUTPUT_DIR_$(2))" \ $$(LLVM_LIBDIR_RUSTFLAGS_$(2)) \ $$(LLVM_STDCPP_RUSTFLAGS_$(2)) \ $$(RUSTFLAGS_$(4)) \ + $$(RUSTFLAGS_$(4)_T_$(2)) \ --out-dir $$(@D) \ -C extra-filename=-$$(CFG_FILENAME_EXTRA) \ $$< @@ -149,6 +153,11 @@ $$(TLIB$(1)_T_$(2)_H_$(3))/%: $$(RT_OUTPUT_DIR_$(2))/% \ | $$(TLIB$(1)_T_$(2)_H_$(3))/ $$(SNAPSHOT_RUSTC_POST_CLEANUP) @$$(call E, cp: $$@) $$(Q)cp $$< $$@ + +$$(TBIN$(1)_T_$(2)_H_$(3))/%: $$(CFG_LLVM_INST_DIR_$(2))/bin/% \ + | $$(TBIN$(1)_T_$(2)_H_$(3))/ $$(SNAPSHOT_RUSTC_POST_CLEANUP) + @$$(call E, cp: $$@) + $$(Q)cp $$< $$@ endef $(foreach source,$(CFG_HOST), \ diff --git a/mk/tests.mk b/mk/tests.mk index f391d8555fc2b..44c661c4e20ea 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -382,7 +382,7 @@ $(3)/stage$(1)/test/$(4)test-$(2)$$(X_$(2)): \ $$(CRATEFILE_$(4)) \ $$(TESTDEP_$(1)_$(2)_$(3)_$(4)) @$$(call E, rustc: $$@) - $(Q)CFG_LLVM_LINKAGE_FILE=$$(LLVM_LINKAGE_PATH_$(3)) \ + $(Q)CFG_LLVM_LINKAGE_FILE=$$(LLVM_LINKAGE_PATH_$(2)) \ $$(subst @,,$$(STAGE$(1)_T_$(2)_H_$(3))) -o $$@ $$< --test \ -L "$$(RT_OUTPUT_DIR_$(2))" \ $$(LLVM_LIBDIR_RUSTFLAGS_$(2)) \ @@ -894,7 +894,7 @@ ifeq ($(2),$$(CFG_BUILD)) $$(call TEST_OK_FILE,$(1),$(2),$(3),doc-crate-$(4)): $$(CRATEDOCTESTDEP_$(1)_$(2)_$(3)_$(4)) @$$(call E, run doc-crate-$(4) [$(2)]) $$(Q)touch $$@.start_time - $$(Q)CFG_LLVM_LINKAGE_FILE=$$(LLVM_LINKAGE_PATH_$(3)) \ + $$(Q)CFG_LLVM_LINKAGE_FILE=$$(LLVM_LINKAGE_PATH_$(2)) \ $$(RUSTDOC_$(1)_T_$(2)_H_$(3)) --test --cfg dox \ $$(CRATEFILE_$(4)) --test-args "$$(TESTARGS)" && \ touch -r $$@.start_time $$@ && rm $$@.start_time diff --git a/src/etc/mklldef.py b/src/etc/mklldef.py new file mode 100644 index 0000000000000..d2f8ee469a4a1 --- /dev/null +++ b/src/etc/mklldef.py @@ -0,0 +1,25 @@ +# Copyright 2015 The Rust Project Developers. See the COPYRIGHT +# file at the top-level directory of this distribution and at +# http://rust-lang.org/COPYRIGHT. +# +# Licensed under the Apache License, Version 2.0 or the MIT license +# , at your +# option. This file may not be copied, modified, or distributed +# except according to those terms. + +import sys + +input_file = sys.argv[1] +output_file = sys.argv[2] +name = sys.argv[3] + +with open(input_file, 'r') as f: + with open(output_file, 'w') as g: + print >> g, 'LIBRARY ' + name + print >> g, 'EXPORTS' + for x in f: + x = str(x) + if not x.startswith(' pub fn LLVM'): continue + name = x[11:x.find('(')] + print >> g, ' ' + name diff --git a/src/etc/mklldeps.py b/src/etc/mklldeps.py index fe9feb3538d15..7a925fa3f3367 100644 --- a/src/etc/mklldeps.py +++ b/src/etc/mklldeps.py @@ -80,10 +80,13 @@ def run(args): assert('stdlib=libc++' not in out) f.write("#[link(name = \"stdc++\", kind = \"static\")]\n") else: + # Note that we use `cfg_attr` here because on MSVC the C++ standard library + # is not c++ or stdc++, but rather the linker takes care of linking the + # right standard library. if 'stdlib=libc++' in out: - f.write("#[link(name = \"c++\")]\n") + f.write("#[cfg_attr(not(target_env = \"msvc\"), link(name = \"c++\"))]\n") else: - f.write("#[link(name = \"stdc++\")]\n") + f.write("#[cfg_attr(not(target_env = \"msvc\"), link(name = \"stdc++\"))]\n") # Attach everything to an extern block f.write("extern {}\n") diff --git a/src/liblibc/lib.rs b/src/liblibc/lib.rs index 55934da00a37c..42e9bd9c9a0d3 100644 --- a/src/liblibc/lib.rs +++ b/src/liblibc/lib.rs @@ -150,6 +150,12 @@ extern {} #[link(name = "c", kind = "static")] extern {} +#[cfg(all(windows, target_env = "msvc"))] +#[link(name = "kernel32")] +#[link(name = "shell32")] +#[link(name = "msvcrt")] +extern {} + // libnacl provides functions that require a trip through the IRT to work. // ie: _exit, mmap, nanosleep, etc. Anything that would otherwise require a trip // to the kernel. diff --git a/src/librustc/metadata/loader.rs b/src/librustc/metadata/loader.rs index 062a156637a35..e8dcd493074bc 100644 --- a/src/librustc/metadata/loader.rs +++ b/src/librustc/metadata/loader.rs @@ -526,8 +526,7 @@ impl<'a> Context<'a> { for (lib, kind) in m { info!("{} reading metadata from: {}", flavor, lib.display()); - let metadata = match get_metadata_section(self.target.options.is_like_osx, - &lib) { + let metadata = match get_metadata_section(self.target, &lib) { Ok(blob) => { if self.crate_matches(blob.as_slice(), &lib) { blob @@ -715,17 +714,19 @@ impl ArchiveMetadata { } // Just a small wrapper to time how long reading metadata takes. -fn get_metadata_section(is_osx: bool, filename: &Path) -> Result { +fn get_metadata_section(target: &Target, filename: &Path) + -> Result { let mut ret = None; let dur = Duration::span(|| { - ret = Some(get_metadata_section_imp(is_osx, filename)); + ret = Some(get_metadata_section_imp(target, filename)); }); info!("reading {:?} => {}ms", filename.file_name().unwrap(), dur.num_milliseconds()); return ret.unwrap();; } -fn get_metadata_section_imp(is_osx: bool, filename: &Path) -> Result { +fn get_metadata_section_imp(target: &Target, filename: &Path) + -> Result { if !filename.exists() { return Err(format!("no such file: '{}'", filename.display())); } @@ -769,7 +770,7 @@ fn get_metadata_section_imp(is_osx: bool, filename: &Path) -> Result Result &'static str { - if is_osx { +pub fn meta_section_name(target: &Target) -> &'static str { + if target.options.is_like_osx { "__DATA,__note.rustc" + } else if target.options.is_like_msvc { + // When using link.exe it was seen that the section name `.note.rustc` + // was getting shortened to `.note.ru`, and according to the PE and COFF + // specification: + // + // > Executable images do not use a string table and do not support + // > section names longer than 8 characters + // + // https://msdn.microsoft.com/en-us/library/windows/hardware/gg463119.aspx + // + // As a result, we choose a slightly shorter name! As to why + // `.note.rustc` works on MinGW, that's another good question... + ".rustc" } else { ".note.rustc" } } -pub fn read_meta_section_name(is_osx: bool) -> &'static str { - if is_osx { +pub fn read_meta_section_name(target: &Target) -> &'static str { + if target.options.is_like_osx { "__note.rustc" + } else if target.options.is_like_msvc { + ".rustc" } else { ".note.rustc" } } // A diagnostic function for dumping crate metadata to an output stream -pub fn list_file_metadata(is_osx: bool, path: &Path, +pub fn list_file_metadata(target: &Target, path: &Path, out: &mut io::Write) -> io::Result<()> { - match get_metadata_section(is_osx, path) { + match get_metadata_section(target, path) { Ok(bytes) => decoder::list_crate_metadata(bytes.as_slice(), out), Err(msg) => { write!(out, "{}\n", msg) diff --git a/src/librustc_back/archive.rs b/src/librustc_back/archive.rs index 37d784692fd0e..cd2e27cf58d60 100644 --- a/src/librustc_back/archive.rs +++ b/src/librustc_back/archive.rs @@ -30,16 +30,11 @@ pub struct ArchiveConfig<'a> { pub lib_search_paths: Vec, pub slib_prefix: String, pub slib_suffix: String, - pub maybe_ar_prog: Option + pub ar_prog: String } pub struct Archive<'a> { - handler: &'a ErrorHandler, - dst: PathBuf, - lib_search_paths: Vec, - slib_prefix: String, - slib_suffix: String, - maybe_ar_prog: Option + config: ArchiveConfig<'a>, } /// Helper for adding many files to an archive with a single invocation of @@ -53,47 +48,10 @@ pub struct ArchiveBuilder<'a> { should_update_symbols: bool, } -fn run_ar(handler: &ErrorHandler, maybe_ar_prog: &Option, - args: &str, cwd: Option<&Path>, - paths: &[&Path]) -> Output { - let ar = match *maybe_ar_prog { - Some(ref ar) => &ar[..], - None => "ar" - }; - let mut cmd = Command::new(ar); - - cmd.arg(args).args(paths).stdout(Stdio::piped()).stderr(Stdio::piped()); - debug!("{:?}", cmd); - - match cwd { - Some(p) => { - cmd.current_dir(p); - debug!("inside {:?}", p.display()); - } - None => {} - } - - match cmd.spawn() { - Ok(prog) => { - let o = prog.wait_with_output().unwrap(); - if !o.status.success() { - handler.err(&format!("{:?} failed with: {}", cmd, o.status)); - handler.note(&format!("stdout ---\n{}", - str::from_utf8(&o.stdout).unwrap())); - handler.note(&format!("stderr ---\n{}", - str::from_utf8(&o.stderr).unwrap()) - ); - handler.abort_if_errors(); - } - o - }, - Err(e) => { - handler.err(&format!("could not exec `{}`: {}", &ar[..], - e)); - handler.abort_if_errors(); - panic!("rustc::back::archive::run_ar() should not reach this point"); - } - } +enum Action<'a> { + Remove(&'a Path), + AddObjects(&'a [&'a PathBuf], bool), + UpdateSymbols, } pub fn find_library(name: &str, osprefix: &str, ossuffix: &str, @@ -120,43 +78,89 @@ pub fn find_library(name: &str, osprefix: &str, ossuffix: &str, impl<'a> Archive<'a> { fn new(config: ArchiveConfig<'a>) -> Archive<'a> { - let ArchiveConfig { handler, dst, lib_search_paths, slib_prefix, slib_suffix, - maybe_ar_prog } = config; - Archive { - handler: handler, - dst: dst, - lib_search_paths: lib_search_paths, - slib_prefix: slib_prefix, - slib_suffix: slib_suffix, - maybe_ar_prog: maybe_ar_prog - } + Archive { config: config } } /// Opens an existing static archive pub fn open(config: ArchiveConfig<'a>) -> Archive<'a> { let archive = Archive::new(config); - assert!(archive.dst.exists()); + assert!(archive.config.dst.exists()); archive } /// Removes a file from this archive pub fn remove_file(&mut self, file: &str) { - run_ar(self.handler, &self.maybe_ar_prog, "d", None, &[&self.dst, &Path::new(file)]); + self.run(None, Action::Remove(Path::new(file))); } /// Lists all files in an archive pub fn files(&self) -> Vec { - let output = run_ar(self.handler, &self.maybe_ar_prog, "t", None, &[&self.dst]); - let output = str::from_utf8(&output.stdout).unwrap(); - // use lines_any because windows delimits output with `\r\n` instead of - // just `\n` - output.lines_any().map(|s| s.to_string()).collect() + let archive = match ArchiveRO::open(&self.config.dst) { + Some(ar) => ar, + None => return Vec::new(), + }; + let ret = archive.iter().filter_map(|child| child.name()) + .map(|name| name.to_string()) + .collect(); + return ret; } /// Creates an `ArchiveBuilder` for adding files to this archive. pub fn extend(self) -> ArchiveBuilder<'a> { ArchiveBuilder::new(self) } + + fn run(&self, cwd: Option<&Path>, action: Action) -> Output { + let abs_dst = env::current_dir().unwrap().join(&self.config.dst); + let ar = &self.config.ar_prog; + let mut cmd = Command::new(ar); + cmd.stdout(Stdio::piped()).stderr(Stdio::piped()); + self.prepare_ar_action(&mut cmd, &abs_dst, action); + info!("{:?}", cmd); + + if let Some(p) = cwd { + cmd.current_dir(p); + info!("inside {:?}", p.display()); + } + + let handler = &self.config.handler; + match cmd.spawn() { + Ok(prog) => { + let o = prog.wait_with_output().unwrap(); + if !o.status.success() { + handler.err(&format!("{:?} failed with: {}", cmd, o.status)); + handler.note(&format!("stdout ---\n{}", + str::from_utf8(&o.stdout).unwrap())); + handler.note(&format!("stderr ---\n{}", + str::from_utf8(&o.stderr).unwrap())); + handler.abort_if_errors(); + } + o + }, + Err(e) => { + handler.err(&format!("could not exec `{}`: {}", + self.config.ar_prog, e)); + handler.abort_if_errors(); + panic!("rustc::back::archive::run() should not reach this point"); + } + } + } + + fn prepare_ar_action(&self, cmd: &mut Command, dst: &Path, action: Action) { + match action { + Action::Remove(file) => { + cmd.arg("d").arg(dst).arg(file); + } + Action::AddObjects(objs, update_symbols) => { + cmd.arg(if update_symbols {"crus"} else {"cruS"}) + .arg(dst) + .args(objs); + } + Action::UpdateSymbols => { + cmd.arg("s").arg(dst); + } + } + } } impl<'a> ArchiveBuilder<'a> { @@ -179,10 +183,10 @@ impl<'a> ArchiveBuilder<'a> { /// search in the relevant locations for a library named `name`. pub fn add_native_library(&mut self, name: &str) -> io::Result<()> { let location = find_library(name, - &self.archive.slib_prefix, - &self.archive.slib_suffix, - &self.archive.lib_search_paths, - self.archive.handler); + &self.archive.config.slib_prefix, + &self.archive.config.slib_suffix, + &self.archive.config.lib_search_paths, + self.archive.config.handler); self.add_archive(&location, name, |_| false) } @@ -229,17 +233,13 @@ impl<'a> ArchiveBuilder<'a> { pub fn build(self) -> Archive<'a> { // Get an absolute path to the destination, so `ar` will work even // though we run it from `self.work_dir`. - let abs_dst = env::current_dir().unwrap().join(&self.archive.dst); - assert!(!abs_dst.is_relative()); - let mut args = vec![&*abs_dst]; - let mut total_len = abs_dst.to_string_lossy().len(); + let mut objects = Vec::new(); + let mut total_len = self.archive.config.dst.to_string_lossy().len(); if self.members.is_empty() { - // OSX `ar` does not allow using `r` with no members, but it does - // allow running `ar s file.a` to update symbols only. if self.should_update_symbols { - run_ar(self.archive.handler, &self.archive.maybe_ar_prog, - "s", Some(self.work_dir.path()), &args[..]); + self.archive.run(Some(self.work_dir.path()), + Action::UpdateSymbols); } return self.archive; } @@ -257,24 +257,22 @@ impl<'a> ArchiveBuilder<'a> { // string, not an array of strings.) if total_len + len + 1 > ARG_LENGTH_LIMIT { // Add the archive members seen so far, without updating the - // symbol table (`S`). - run_ar(self.archive.handler, &self.archive.maybe_ar_prog, - "cruS", Some(self.work_dir.path()), &args[..]); + // symbol table. + self.archive.run(Some(self.work_dir.path()), + Action::AddObjects(&objects, false)); - args.clear(); - args.push(&abs_dst); - total_len = abs_dst.to_string_lossy().len(); + objects.clear(); + total_len = self.archive.config.dst.to_string_lossy().len(); } - args.push(member_name); + objects.push(member_name); total_len += len + 1; } // Add the remaining archive members, and update the symbol table if // necessary. - let flags = if self.should_update_symbols { "crus" } else { "cruS" }; - run_ar(self.archive.handler, &self.archive.maybe_ar_prog, - flags, Some(self.work_dir.path()), &args[..]); + self.archive.run(Some(self.work_dir.path()), + Action::AddObjects(&objects, self.should_update_symbols)); self.archive } @@ -305,6 +303,8 @@ impl<'a> ArchiveBuilder<'a> { }; if filename.contains(".SYMDEF") { continue } if skip(filename) { continue } + let filename = Path::new(filename).file_name().unwrap() + .to_str().unwrap(); // An archive can contain files of the same name multiple times, so // we need to be sure to not have them overwrite one another when we diff --git a/src/librustc_back/target/linux_base.rs b/src/librustc_back/target/linux_base.rs index 823a4a81fa4c1..3ae70ca854bdb 100644 --- a/src/librustc_back/target/linux_base.rs +++ b/src/librustc_back/target/linux_base.rs @@ -13,7 +13,6 @@ use std::default::Default; pub fn opts() -> TargetOptions { TargetOptions { - linker: "cc".to_string(), dynamic_linking: true, executables: true, morestack: true, diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs index c5f1882fa1dd7..ceac37513ee8c 100644 --- a/src/librustc_back/target/mod.rs +++ b/src/librustc_back/target/mod.rs @@ -59,6 +59,7 @@ mod freebsd_base; mod linux_base; mod openbsd_base; mod windows_base; +mod windows_msvc_base; /// Everything `rustc` knows about how to compile for a specific target. /// @@ -92,6 +93,8 @@ pub struct Target { pub struct TargetOptions { /// Linker to invoke. Defaults to "cc". pub linker: String, + /// Archive utility to use when managing archives. Defaults to "ar". + pub ar: String, /// Linker arguments that are unconditionally passed *before* any /// user-defined libraries. pub pre_link_args: Vec, @@ -145,6 +148,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, @@ -152,22 +156,24 @@ pub struct TargetOptions { pub linker_is_gnu: bool, /// Whether the linker support rpaths or not. Defaults to false. pub has_rpath: bool, - /// Whether to disable linking to compiler-rt. Defaults to false, as LLVM will emit references - /// to the functions that compiler-rt provides. + /// Whether to disable linking to compiler-rt. Defaults to false, as LLVM + /// will emit references to the functions that compiler-rt provides. pub no_compiler_rt: bool, - /// Dynamically linked executables can be compiled as position independent if the default - /// relocation model of position independent code is not changed. This is a requirement to take - /// advantage of ASLR, as otherwise the functions in the executable are not randomized and can - /// be used during an exploit of a vulnerability in any code. + /// Dynamically linked executables can be compiled as position independent + /// if the default relocation model of position independent code is not + /// changed. This is a requirement to take advantage of ASLR, as otherwise + /// the functions in the executable are not randomized and can be used + /// during an exploit of a vulnerability in any code. pub position_independent_executables: bool, } impl Default for TargetOptions { - /// Create a set of "sane defaults" for any target. This is still incomplete, and if used for - /// compilation, will certainly not work. + /// Create a set of "sane defaults" for any target. This is still + /// incomplete, and if used for compilation, will certainly not work. fn default() -> TargetOptions { TargetOptions { linker: "cc".to_string(), + ar: "ar".to_string(), pre_link_args: Vec::new(), post_link_args: Vec::new(), cpu: "generic".to_string(), @@ -188,6 +194,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 +378,9 @@ impl Target { armv7s_apple_ios, x86_64_pc_windows_gnu, - i686_pc_windows_gnu + i686_pc_windows_gnu, + + x86_64_pc_windows_msvc ); diff --git a/src/librustc_back/target/windows_msvc_base.rs b/src/librustc_back/target/windows_msvc_base.rs new file mode 100644 index 0000000000000..30d74c807352c --- /dev/null +++ b/src/librustc_back/target/windows_msvc_base.rs @@ -0,0 +1,66 @@ +// 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 { + function_sections: true, + linker: "link.exe".to_string(), + // When taking a look at the value of this `ar` field, one might expect + // `lib.exe` to be the value here! The `lib.exe` program is the default + // tool for managing `.lib` archives on Windows, but unfortunately the + // compiler cannot use it. + // + // To recap, we use `ar` here to manage rlibs (which are just archives). + // LLVM does not expose bindings for modifying archives so we have to + // invoke this utility for write operations (e.g. deleting files, adding + // files, etc). Normally archives only have object files within them, + // but the compiler also uses archives for storing metadata and + // compressed bytecode, so we don't exactly fall within "normal use + // cases". + // + // MSVC's `lib.exe` tool by default will choke when adding a non-object + // file to an archive, which we do on a regular basis, making it + // inoperable for us. Luckily, however, LLVM has already rewritten `ar` + // in the form of `llvm-ar` which is built by default when we build + // LLVM. This tool, unlike `lib.exe`, works just fine with non-object + // files, so we use it instead. + // + // Note that there's a few caveats associated with this: + // + // * This still requires that the *linker* (the consumer of rlibs) will + // ignore non-object files. Thankfully `link.exe` on Windows does + // indeed ignore non-object files in archives. + // * This requires `llvm-ar.exe` to be distributed with the compiler + // itself, but we already make sure of this elsewhere. + // + // Perhaps one day we won't even need this tool at all and we'll just be + // able to make library calls into LLVM! + ar: "llvm-ar.exe".to_string(), + dynamic_linking: true, + executables: true, + dll_prefix: "".to_string(), + 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![ + "/NOLOGO".to_string(), + "/NXCOMPAT".to_string(), + ], + + .. Default::default() + } +} diff --git a/src/librustc_back/target/x86_64_pc_windows_msvc.rs b/src/librustc_back/target/x86_64_pc_windows_msvc.rs new file mode 100644 index 0000000000000..f7c3ca4b3f6ed --- /dev/null +++ b/src/librustc_back/target/x86_64_pc_windows_msvc.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::Target; + +pub fn target() -> Target { + let mut base = super::windows_msvc_base::opts(); + base.cpu = "x86-64".to_string(); + + Target { + // This is currently in sync with the specification for + // x86_64-pc-windows-gnu but there's a comment in that file questioning + // whether this is valid or not. Sounds like the two should stay in sync + // at least for now. + data_layout: "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-\ + f32:32:32-f64:64:64-v64:64:64-v128:128:128-a:0:64-\ + s0:64:64-f80:128:128-n8:16:32:64-S128".to_string(), + 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_env: "msvc".to_string(), + options: base, + } +} diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index a618f4b6ef675..6e0d68ec3e173 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -402,7 +402,7 @@ impl RustcDefaultCalls { &Input::File(ref ifile) => { let path = &(*ifile); let mut v = Vec::new(); - metadata::loader::list_file_metadata(sess.target.target.options.is_like_osx, + metadata::loader::list_file_metadata(&sess.target.target, path, &mut v).unwrap(); println!("{}", String::from_utf8(v).unwrap()); diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs index 2a2aa2bf4cf93..3e575785d2719 100644 --- a/src/librustc_llvm/lib.rs +++ b/src/librustc_llvm/lib.rs @@ -55,6 +55,7 @@ pub use self::CallConv::*; pub use self::Visibility::*; pub use self::DiagnosticSeverity::*; pub use self::Linkage::*; +pub use self::DLLStorageClassTypes::*; use std::ffi::CString; use std::cell::RefCell; @@ -123,6 +124,15 @@ pub enum DiagnosticSeverity { Note, } + +#[repr(C)] +#[derive(Copy, Clone)] +pub enum DLLStorageClassTypes { + DefaultStorageClass = 0, + DLLImportStorageClass = 1, + DLLExportStorageClass = 2, +} + bitflags! { flags Attribute : u32 { const ZExt = 1 << 0, @@ -1761,7 +1771,7 @@ extern { Dialect: c_uint) -> ValueRef; - pub static LLVMRustDebugMetadataVersion: u32; + pub fn LLVMRustDebugMetadataVersion() -> u32; pub fn LLVMRustAddModuleFlag(M: ModuleRef, name: *const c_char, @@ -2075,7 +2085,8 @@ extern { pub fn LLVMRustArchiveIteratorFree(AIR: ArchiveIteratorRef); pub fn LLVMRustDestroyArchive(AR: ArchiveRef); - pub fn LLVMRustSetDLLExportStorageClass(V: ValueRef); + pub fn LLVMRustSetDLLStorageClass(V: ValueRef, + C: DLLStorageClassTypes); pub fn LLVMRustGetSectionName(SI: SectionIteratorRef, data: *mut *const c_char) -> c_int; @@ -2125,6 +2136,12 @@ pub fn SetLinkage(global: ValueRef, link: Linkage) { } } +pub fn SetDLLStorageClass(global: ValueRef, class: DLLStorageClassTypes) { + unsafe { + LLVMRustSetDLLStorageClass(global, class); + } +} + pub fn SetUnnamedAddr(global: ValueRef, unnamed: bool) { unsafe { LLVMSetUnnamedAddr(global, unnamed as Bool); diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index 38ad909dd012e..844a0a698677f 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -9,9 +9,9 @@ // except according to those terms. use super::archive::{Archive, ArchiveBuilder, ArchiveConfig, METADATA_FILENAME}; -use super::archive; -use super::rpath; +use super::linker::{Linker, GnuLinker, MsvcLinker}; use super::rpath::RPathConfig; +use super::rpath; use super::svh::Svh; use session::config; use session::config::NoDebugInfo; @@ -29,7 +29,6 @@ use util::sha2::{Digest, Sha256}; use util::fs::fix_windows_verbatim_for_gcc; use rustc_back::tempdir::TempDir; -use std::ffi::OsString; use std::fs::{self, PathExt}; use std::io::{self, Read, Write}; use std::mem; @@ -366,6 +365,12 @@ pub fn get_cc_prog(sess: &Session) -> String { } } +pub fn get_ar_prog(sess: &Session) -> String { + sess.opts.cg.ar.clone().unwrap_or_else(|| { + sess.target.target.options.ar.clone() + }) +} + pub fn remove(sess: &Session, path: &Path) { match fs::remove_file(path) { Ok(..) => {} @@ -541,6 +546,7 @@ fn link_rlib<'a>(sess: &'a Session, trans: Option<&CrateTranslation>, // None == no metadata/bytecode obj_filename: &Path, out_filename: &Path) -> ArchiveBuilder<'a> { + info!("preparing rlib from {:?} to {:?}", obj_filename, out_filename); let handler = &sess.diagnostic().handler; let config = ArchiveConfig { handler: handler, @@ -548,16 +554,14 @@ fn link_rlib<'a>(sess: &'a Session, lib_search_paths: archive_search_paths(sess), slib_prefix: sess.target.target.options.staticlib_prefix.clone(), slib_suffix: sess.target.target.options.staticlib_suffix.clone(), - maybe_ar_prog: sess.opts.cg.ar.clone() + ar_prog: get_ar_prog(sess), }; let mut ab = ArchiveBuilder::create(config); ab.add_file(obj_filename).unwrap(); for &(ref l, kind) in &*sess.cstore.get_used_libraries().borrow() { match kind { - cstore::NativeStatic => { - ab.add_native_library(&l[..]).unwrap(); - } + cstore::NativeStatic => ab.add_native_library(&l).unwrap(), cstore::NativeFramework | cstore::NativeUnknown => {} } } @@ -608,10 +612,8 @@ fn link_rlib<'a>(sess: &'a Session, }) { Ok(..) => {} Err(e) => { - sess.err(&format!("failed to write {}: {}", - metadata.display(), - e)); - sess.abort_if_errors(); + sess.fatal(&format!("failed to write {}: {}", + metadata.display(), e)); } } ab.add_file(&metadata).unwrap(); @@ -653,9 +655,8 @@ fn link_rlib<'a>(sess: &'a Session, &bc_data_deflated) { Ok(()) => {} Err(e) => { - sess.err(&format!("failed to write compressed bytecode: \ - {}", e)); - sess.abort_if_errors() + sess.fatal(&format!("failed to write compressed \ + bytecode: {}", e)); } }; @@ -789,6 +790,8 @@ fn link_staticlib(sess: &Session, obj_filename: &Path, out_filename: &Path) { // links to all upstream files as well. fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool, obj_filename: &Path, out_filename: &Path) { + info!("preparing dylib? ({}) from {:?} to {:?}", dylib, obj_filename, + out_filename); let tmpdir = TempDir::new("rustc").ok().expect("needs a temp dir"); // The invocations of cc share some flags across platforms @@ -801,10 +804,17 @@ fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool, cmd.arg(root.join(obj)); } - link_args(&mut cmd, sess, dylib, tmpdir.path(), - trans, obj_filename, out_filename); - if !sess.target.target.options.no_compiler_rt { - cmd.arg("-lcompiler-rt"); + { + let mut linker = if sess.target.target.options.is_like_msvc { + Box::new(MsvcLinker { cmd: &mut cmd, sess: &sess }) as Box + } else { + Box::new(GnuLinker { cmd: &mut cmd, sess: &sess }) as Box + }; + link_args(&mut *linker, sess, dylib, tmpdir.path(), + trans, obj_filename, out_filename); + if !sess.target.target.options.no_compiler_rt { + linker.link_staticlib("compiler-rt"); + } } for obj in &sess.target.target.options.post_link_objects { cmd.arg(root.join(obj)); @@ -819,7 +829,7 @@ fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool, sess.abort_if_errors(); // Invoke the system linker - debug!("{:?}", &cmd); + info!("{:?}", &cmd); let prog = time(sess.time_passes(), "running linker", (), |()| cmd.output()); match prog { Ok(prog) => { @@ -833,14 +843,11 @@ fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool, sess.note(str::from_utf8(&output[..]).unwrap()); sess.abort_if_errors(); } - debug!("linker stderr:\n{}", String::from_utf8(prog.stderr).unwrap()); - debug!("linker stdout:\n{}", String::from_utf8(prog.stdout).unwrap()); + info!("linker stderr:\n{}", String::from_utf8(prog.stderr).unwrap()); + info!("linker stdout:\n{}", String::from_utf8(prog.stdout).unwrap()); }, Err(e) => { - sess.err(&format!("could not exec the linker `{}`: {}", - pname, - e)); - sess.abort_if_errors(); + sess.fatal(&format!("could not exec the linker `{}`: {}", pname, e)); } } @@ -850,15 +857,12 @@ fn link_natively(sess: &Session, trans: &CrateTranslation, dylib: bool, if sess.target.target.options.is_like_osx && sess.opts.debuginfo != NoDebugInfo { match Command::new("dsymutil").arg(out_filename).output() { Ok(..) => {} - Err(e) => { - sess.err(&format!("failed to run dsymutil: {}", e)); - sess.abort_if_errors(); - } + Err(e) => sess.fatal(&format!("failed to run dsymutil: {}", e)), } } } -fn link_args(cmd: &mut Command, +fn link_args(cmd: &mut Linker, sess: &Session, dylib: bool, tmpdir: &Path, @@ -873,10 +877,9 @@ fn link_args(cmd: &mut Command, // target descriptor let t = &sess.target.target; - cmd.arg("-L").arg(&fix_windows_verbatim_for_gcc(&lib_path)); - - cmd.arg("-o").arg(out_filename).arg(obj_filename); - + cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path)); + cmd.add_object(obj_filename); + cmd.output_filename(out_filename); // Stack growth requires statically linking a __morestack function. Note // that this is listed *before* all other libraries. Due to the usage of the @@ -895,89 +898,44 @@ fn link_args(cmd: &mut Command, // will include the __morestack symbol 100% of the time, always resolving // references to it even if the object above didn't use it. if t.options.morestack { - if t.options.is_like_osx { - let morestack = lib_path.join("libmorestack.a"); - - let mut v = OsString::from("-Wl,-force_load,"); - v.push(&morestack); - cmd.arg(&v); - } else { - cmd.args(&["-Wl,--whole-archive", "-lmorestack", "-Wl,--no-whole-archive"]); - } + cmd.link_whole_staticlib("morestack", &[lib_path]); } // When linking a dynamic library, we put the metadata into a section of the // executable. This metadata is in a separate object file from the main // object file, so we link that in here. if dylib { - cmd.arg(&obj_filename.with_extension("metadata.o")); + cmd.add_object(&obj_filename.with_extension("metadata.o")); } - if t.options.is_like_osx { - // The dead_strip option to the linker specifies that functions and data - // unreachable by the entry point will be removed. This is quite useful - // with Rust's compilation model of compiling libraries at a time into - // one object file. For example, this brings hello world from 1.7MB to - // 458K. - // - // Note that this is done for both executables and dynamic libraries. We - // won't get much benefit from dylibs because LLVM will have already - // stripped away as much as it could. This has not been seen to impact - // link times negatively. - // - // -dead_strip can't be part of the pre_link_args because it's also used - // for partial linking when using multiple codegen units (-r). So we - // insert it here. - cmd.arg("-Wl,-dead_strip"); - } - - // If we're building a dylib, we don't use --gc-sections because LLVM has - // already done the best it can do, and we also don't want to eliminate the - // metadata. If we're building an executable, however, --gc-sections drops - // the size of hello world from 1.8MB to 597K, a 67% reduction. - if !dylib && !t.options.is_like_osx { - cmd.arg("-Wl,--gc-sections"); - } + // Try to strip as much out of the generated object by removing unused + // sections if possible. See more comments in linker.rs + cmd.gc_sections(dylib); let used_link_args = sess.cstore.get_used_link_args().borrow(); - if t.options.position_independent_executables { + if !dylib && t.options.position_independent_executables { let empty_vec = Vec::new(); let empty_str = String::new(); let args = sess.opts.cg.link_args.as_ref().unwrap_or(&empty_vec); let mut args = args.iter().chain(used_link_args.iter()); - if !dylib - && (t.options.relocation_model == "pic" - || *sess.opts.cg.relocation_model.as_ref() - .unwrap_or(&empty_str) == "pic") + let relocation_model = sess.opts.cg.relocation_model.as_ref() + .unwrap_or(&empty_str); + if (t.options.relocation_model == "pic" || *relocation_model == "pic") && !args.any(|x| *x == "-static") { - cmd.arg("-pie"); + cmd.position_independent_executable(); } } - if t.options.linker_is_gnu { - // GNU-style linkers support optimization with -O. GNU ld doesn't need a - // numeric argument, but other linkers do. - if sess.opts.optimize == config::Default || - sess.opts.optimize == config::Aggressive { - cmd.arg("-Wl,-O1"); - } - } + // Pass optimization flags down to the linker. + cmd.optimize(); // We want to prevent the compiler from accidentally leaking in any system // libraries, so we explicitly ask gcc to not link to any libraries by // default. Note that this does not happen for windows because windows pulls // in some large number of libraries and I couldn't quite figure out which // subset we wanted. - if !t.options.is_like_windows { - cmd.arg("-nodefaultlibs"); - } - - // Mark all dynamic libraries and executables as compatible with ASLR - // FIXME #17098: ASLR breaks gdb - if t.options.is_like_windows && sess.opts.debuginfo == NoDebugInfo { - // cmd.arg("-Wl,--dynamicbase"); - } + cmd.no_default_libraries(); // Take careful note of the ordering of the arguments we pass to the linker // here. Linkers will assume that things on the left depend on things to the @@ -1019,18 +977,7 @@ fn link_args(cmd: &mut Command, // # Telling the linker what we're doing if dylib { - // On mac we need to tell the linker to let this library be rpathed - if sess.target.target.options.is_like_osx { - cmd.args(&["-dynamiclib", "-Wl,-dylib"]); - - if sess.opts.cg.rpath { - let mut v = OsString::from("-Wl,-install_name,@rpath/"); - v.push(out_filename.file_name().unwrap()); - cmd.arg(&v); - } - } else { - cmd.arg("-shared"); - } + cmd.build_dylib(out_filename); } // FIXME (#2397): At some point we want to rpath our guesses as to @@ -1059,9 +1006,10 @@ fn link_args(cmd: &mut Command, // Finally add all the linker arguments provided on the command line along // with any #[link_args] attributes found inside the crate - let empty = Vec::new(); - cmd.args(&sess.opts.cg.link_args.as_ref().unwrap_or(&empty)); - cmd.args(&used_link_args[..]); + if let Some(ref args) = sess.opts.cg.link_args { + cmd.args(args); + } + cmd.args(&used_link_args); } // # Native library linking @@ -1075,21 +1023,15 @@ fn link_args(cmd: &mut Command, // Also note that the native libraries linked here are only the ones located // in the current crate. Upstream crates with native library dependencies // may have their native library pulled in above. -fn add_local_native_libraries(cmd: &mut Command, sess: &Session) { +fn add_local_native_libraries(cmd: &mut Linker, sess: &Session) { sess.target_filesearch(PathKind::All).for_each_lib_search_path(|path, k| { match k { - PathKind::Framework => { cmd.arg("-F").arg(path); } - _ => { cmd.arg("-L").arg(&fix_windows_verbatim_for_gcc(path)); } + PathKind::Framework => { cmd.framework_path(path); } + _ => { cmd.include_path(&fix_windows_verbatim_for_gcc(path)); } } FileDoesntMatch }); - // Some platforms take hints about whether a library is static or dynamic. - // For those that support this, we ensure we pass the option if the library - // was flagged "static" (most defaults are dynamic) to ensure that if - // libfoo.a and libfoo.so both exist that the right one is chosen. - let takes_hints = !sess.target.target.options.is_like_osx; - let libs = sess.cstore.get_used_libraries(); let libs = libs.borrow(); @@ -1100,46 +1042,29 @@ fn add_local_native_libraries(cmd: &mut Command, sess: &Session) { kind != cstore::NativeStatic }); - // Platforms that take hints generally also support the --whole-archive - // flag. We need to pass this flag when linking static native libraries to - // ensure the entire library is included. - // - // For more details see #15460, but the gist is that the linker will strip - // away any unused objects in the archive if we don't otherwise explicitly - // reference them. This can occur for libraries which are just providing - // bindings, libraries with generic functions, etc. - if takes_hints { - cmd.arg("-Wl,--whole-archive").arg("-Wl,-Bstatic"); - } + // Some platforms take hints about whether a library is static or dynamic. + // For those that support this, we ensure we pass the option if the library + // was flagged "static" (most defaults are dynamic) to ensure that if + // libfoo.a and libfoo.so both exist that the right one is chosen. + cmd.hint_static(); + let search_path = archive_search_paths(sess); for l in staticlibs { - if takes_hints { - cmd.arg(&format!("-l{}", l)); - } else { - // -force_load is the OSX equivalent of --whole-archive, but it - // involves passing the full path to the library to link. - let lib = archive::find_library(&l[..], - &sess.target.target.options.staticlib_prefix, - &sess.target.target.options.staticlib_suffix, - &search_path[..], - &sess.diagnostic().handler); - let mut v = OsString::from("-Wl,-force_load,"); - v.push(&lib); - cmd.arg(&v); - } - } - if takes_hints { - cmd.arg("-Wl,--no-whole-archive").arg("-Wl,-Bdynamic"); + // Here we explicitly ask that the entire archive is included into the + // result artifact. For more details see #15460, but the gist is that + // the linker will strip away any unused objects in the archive if we + // don't otherwise explicitly reference them. This can occur for + // libraries which are just providing bindings, libraries with generic + // functions, etc. + cmd.link_whole_staticlib(l, &search_path); } + cmd.hint_dynamic(); + for &(ref l, kind) in others { match kind { - cstore::NativeUnknown => { - cmd.arg(&format!("-l{}", l)); - } - cstore::NativeFramework => { - cmd.arg("-framework").arg(&l[..]); - } + cstore::NativeUnknown => cmd.link_dylib(l), + cstore::NativeFramework => cmd.link_framework(l), cstore::NativeStatic => unreachable!(), } } @@ -1150,7 +1075,7 @@ fn add_local_native_libraries(cmd: &mut Command, sess: &Session) { // Rust crates are not considered at all when creating an rlib output. All // dependencies will be linked when producing the final output (instead of // the intermediate rlib version) -fn add_upstream_rust_crates(cmd: &mut Command, sess: &Session, +fn add_upstream_rust_crates(cmd: &mut Linker, sess: &Session, dylib: bool, tmpdir: &Path, trans: &CrateTranslation) { // All of the heavy lifting has previously been accomplished by the @@ -1201,7 +1126,7 @@ fn add_upstream_rust_crates(cmd: &mut Command, sess: &Session, } // Adds the static "rlib" versions of all crates to the command line. - fn add_static_crate(cmd: &mut Command, sess: &Session, tmpdir: &Path, + fn add_static_crate(cmd: &mut Linker, sess: &Session, tmpdir: &Path, cratepath: &Path) { // When performing LTO on an executable output, all of the // bytecode from the upstream libraries has already been @@ -1227,11 +1152,9 @@ fn add_upstream_rust_crates(cmd: &mut Command, sess: &Session, match fs::copy(&cratepath, &dst) { Ok(..) => {} Err(e) => { - sess.err(&format!("failed to copy {} to {}: {}", - cratepath.display(), - dst.display(), - e)); - sess.abort_if_errors(); + sess.fatal(&format!("failed to copy {} to {}: {}", + cratepath.display(), + dst.display(), e)); } } // Fix up permissions of the copy, as fs::copy() preserves @@ -1244,10 +1167,8 @@ fn add_upstream_rust_crates(cmd: &mut Command, sess: &Session, }) { Ok(..) => {} Err(e) => { - sess.err(&format!("failed to chmod {} when preparing \ - for LTO: {}", dst.display(), - e)); - sess.abort_if_errors(); + sess.fatal(&format!("failed to chmod {} when preparing \ + for LTO: {}", dst.display(), e)); } } let handler = &sess.diagnostic().handler; @@ -1257,22 +1178,22 @@ fn add_upstream_rust_crates(cmd: &mut Command, sess: &Session, lib_search_paths: archive_search_paths(sess), slib_prefix: sess.target.target.options.staticlib_prefix.clone(), slib_suffix: sess.target.target.options.staticlib_suffix.clone(), - maybe_ar_prog: sess.opts.cg.ar.clone() + ar_prog: get_ar_prog(sess), }; let mut archive = Archive::open(config); archive.remove_file(&format!("{}.o", name)); let files = archive.files(); if files.iter().any(|s| s.ends_with(".o")) { - cmd.arg(&dst); + cmd.link_rlib(&dst); } }); } else { - cmd.arg(&fix_windows_verbatim_for_gcc(cratepath)); + cmd.link_rlib(&fix_windows_verbatim_for_gcc(cratepath)); } } // Same thing as above, but for dynamic crates instead of static crates. - fn add_dynamic_crate(cmd: &mut Command, sess: &Session, cratepath: &Path) { + fn add_dynamic_crate(cmd: &mut Linker, sess: &Session, cratepath: &Path) { // If we're performing LTO, then it should have been previously required // that all upstream rust dependencies were available in an rlib format. assert!(!sess.lto()); @@ -1280,10 +1201,10 @@ fn add_upstream_rust_crates(cmd: &mut Command, sess: &Session, // Just need to tell the linker about where the library lives and // what its name is if let Some(dir) = cratepath.parent() { - cmd.arg("-L").arg(&fix_windows_verbatim_for_gcc(dir)); + cmd.include_path(&fix_windows_verbatim_for_gcc(dir)); } let filestem = cratepath.file_stem().unwrap().to_str().unwrap(); - cmd.arg(&format!("-l{}", unlib(&sess.target, filestem))); + cmd.link_dylib(&unlib(&sess.target, filestem)); } } @@ -1305,7 +1226,7 @@ fn add_upstream_rust_crates(cmd: &mut Command, sess: &Session, // generic function calls a native function, then the generic function must // be instantiated in the target crate, meaning that the native symbol must // also be resolved in the target crate. -fn add_upstream_native_libraries(cmd: &mut Command, sess: &Session) { +fn add_upstream_native_libraries(cmd: &mut Linker, sess: &Session) { // Be sure to use a topological sorting of crates because there may be // interdependencies between native libraries. When passing -nodefaultlibs, // for example, almost all native libraries depend on libc, so we have to @@ -1320,13 +1241,8 @@ fn add_upstream_native_libraries(cmd: &mut Command, sess: &Session) { let libs = csearch::get_native_libraries(&sess.cstore, cnum); for &(kind, ref lib) in &libs { match kind { - cstore::NativeUnknown => { - cmd.arg(&format!("-l{}", *lib)); - } - cstore::NativeFramework => { - cmd.arg("-framework"); - cmd.arg(&lib[..]); - } + cstore::NativeUnknown => cmd.link_dylib(lib), + cstore::NativeFramework => cmd.link_framework(lib), cstore::NativeStatic => { sess.bug("statics shouldn't be propagated"); } diff --git a/src/librustc_trans/back/linker.rs b/src/librustc_trans/back/linker.rs new file mode 100644 index 0000000000000..1eacec46c87bb --- /dev/null +++ b/src/librustc_trans/back/linker.rs @@ -0,0 +1,253 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::ffi::OsString; +use std::path::{Path, PathBuf}; +use std::process::Command; + +use rustc_back::archive; +use session::Session; +use session::config; + +/// Linker abstraction used by back::link to build up the command to invoke a +/// linker. +/// +/// This trait is the total list of requirements needed by `back::link` and +/// represents the meaning of each option being passed down. This trait is then +/// used to dispatch on whether a GNU-like linker (generally `ld.exe`) or an +/// MSVC linker (e.g. `link.exe`) is being used. +pub trait Linker { + fn link_dylib(&mut self, lib: &str); + fn link_framework(&mut self, framework: &str); + fn link_staticlib(&mut self, lib: &str); + fn link_rlib(&mut self, lib: &Path); + fn link_whole_staticlib(&mut self, lib: &str, search_path: &[PathBuf]); + fn include_path(&mut self, path: &Path); + fn framework_path(&mut self, path: &Path); + fn output_filename(&mut self, path: &Path); + fn add_object(&mut self, path: &Path); + fn gc_sections(&mut self, is_dylib: bool); + fn position_independent_executable(&mut self); + fn optimize(&mut self); + fn no_default_libraries(&mut self); + fn build_dylib(&mut self, out_filename: &Path); + fn args(&mut self, args: &[String]); + fn hint_static(&mut self); + fn hint_dynamic(&mut self); + fn whole_archives(&mut self); + fn no_whole_archives(&mut self); +} + +pub struct GnuLinker<'a> { + pub cmd: &'a mut Command, + pub sess: &'a Session, +} + +impl<'a> GnuLinker<'a> { + fn takes_hints(&self) -> bool { + !self.sess.target.target.options.is_like_osx + } +} + +impl<'a> Linker for GnuLinker<'a> { + fn link_dylib(&mut self, lib: &str) { self.cmd.arg("-l").arg(lib); } + fn link_staticlib(&mut self, lib: &str) { self.cmd.arg("-l").arg(lib); } + fn link_rlib(&mut self, lib: &Path) { self.cmd.arg(lib); } + fn include_path(&mut self, path: &Path) { self.cmd.arg("-L").arg(path); } + fn framework_path(&mut self, path: &Path) { self.cmd.arg("-F").arg(path); } + fn output_filename(&mut self, path: &Path) { self.cmd.arg("-o").arg(path); } + fn add_object(&mut self, path: &Path) { self.cmd.arg(path); } + fn position_independent_executable(&mut self) { self.cmd.arg("-pie"); } + fn args(&mut self, args: &[String]) { self.cmd.args(args); } + + fn link_framework(&mut self, framework: &str) { + self.cmd.arg("-framework").arg(framework); + } + + fn link_whole_staticlib(&mut self, lib: &str, search_path: &[PathBuf]) { + let target = &self.sess.target.target; + if !target.options.is_like_osx { + self.cmd.arg("-Wl,--whole-archive") + .arg("-l").arg(lib) + .arg("-Wl,--no-whole-archive"); + } else { + // -force_load is the OSX equivalent of --whole-archive, but it + // involves passing the full path to the library to link. + let mut v = OsString::from("-Wl,-force_load,"); + v.push(&archive::find_library(lib, + &target.options.staticlib_prefix, + &target.options.staticlib_suffix, + search_path, + &self.sess.diagnostic().handler)); + self.cmd.arg(&v); + } + } + + fn gc_sections(&mut self, is_dylib: bool) { + // The dead_strip option to the linker specifies that functions and data + // unreachable by the entry point will be removed. This is quite useful + // with Rust's compilation model of compiling libraries at a time into + // one object file. For example, this brings hello world from 1.7MB to + // 458K. + // + // Note that this is done for both executables and dynamic libraries. We + // won't get much benefit from dylibs because LLVM will have already + // stripped away as much as it could. This has not been seen to impact + // link times negatively. + // + // -dead_strip can't be part of the pre_link_args because it's also used + // for partial linking when using multiple codegen units (-r). So we + // insert it here. + if self.sess.target.target.options.is_like_osx { + self.cmd.arg("-Wl,-dead_strip"); + + // If we're building a dylib, we don't use --gc-sections because LLVM + // has already done the best it can do, and we also don't want to + // eliminate the metadata. If we're building an executable, however, + // --gc-sections drops the size of hello world from 1.8MB to 597K, a 67% + // reduction. + } else if !is_dylib { + self.cmd.arg("-Wl,--gc-sections"); + } + } + + fn optimize(&mut self) { + if !self.sess.target.target.options.linker_is_gnu { return } + + // GNU-style linkers support optimization with -O. GNU ld doesn't + // need a numeric argument, but other linkers do. + if self.sess.opts.optimize == config::Default || + self.sess.opts.optimize == config::Aggressive { + self.cmd.arg("-Wl,-O1"); + } + } + + fn no_default_libraries(&mut self) { + // Unfortunately right now passing -nodefaultlibs to gcc on windows + // doesn't work so hot (in terms of native dependencies). This if + // statement should hopefully be removed one day though! + if !self.sess.target.target.options.is_like_windows { + self.cmd.arg("-nodefaultlibs"); + } + } + + fn build_dylib(&mut self, out_filename: &Path) { + // On mac we need to tell the linker to let this library be rpathed + if self.sess.target.target.options.is_like_osx { + self.cmd.args(&["-dynamiclib", "-Wl,-dylib"]); + + if self.sess.opts.cg.rpath { + let mut v = OsString::from("-Wl,-install_name,@rpath/"); + v.push(out_filename.file_name().unwrap()); + self.cmd.arg(&v); + } + } else { + self.cmd.arg("-shared"); + } + } + + fn whole_archives(&mut self) { + if !self.takes_hints() { return } + self.cmd.arg("-Wl,--whole-archive"); + } + + fn no_whole_archives(&mut self) { + if !self.takes_hints() { return } + self.cmd.arg("-Wl,--no-whole-archive"); + } + + fn hint_static(&mut self) { + if !self.takes_hints() { return } + self.cmd.arg("-Wl,-Bstatic"); + } + + fn hint_dynamic(&mut self) { + if !self.takes_hints() { return } + self.cmd.arg("-Wl,-Bdynamic"); + } +} + +pub struct MsvcLinker<'a> { + pub cmd: &'a mut Command, + pub sess: &'a Session, +} + +impl<'a> Linker for MsvcLinker<'a> { + fn link_rlib(&mut self, lib: &Path) { self.cmd.arg(lib); } + fn add_object(&mut self, path: &Path) { self.cmd.arg(path); } + fn args(&mut self, args: &[String]) { self.cmd.args(args); } + fn build_dylib(&mut self, _out_filename: &Path) { self.cmd.arg("/DLL"); } + fn gc_sections(&mut self, _is_dylib: bool) { self.cmd.arg("/OPT:REF,ICF"); } + + fn link_dylib(&mut self, lib: &str) { + self.cmd.arg(&format!("{}.lib", lib)); + } + fn link_staticlib(&mut self, lib: &str) { + self.cmd.arg(&format!("{}.lib", lib)); + } + + fn position_independent_executable(&mut self) { + // noop + } + + fn no_default_libraries(&mut self) { + // Currently we don't pass the /NODEFAULTLIB flag to the linker on MSVC + // as there's been trouble in the past of linking the C++ standard + // library required by LLVM. This likely needs to happen one day, but + // in general Windows is also a more controlled environment than + // Unix, so it's not necessarily as critical that this be implemented. + // + // Note that there are also some licensing worries about statically + // linking some libraries which require a specific agreement, so it may + // not ever be possible for us to pass this flag. + } + + fn include_path(&mut self, path: &Path) { + let mut arg = OsString::from("/LIBPATH:"); + arg.push(path); + self.cmd.arg(&arg); + } + + fn output_filename(&mut self, path: &Path) { + let mut arg = OsString::from("/OUT:"); + arg.push(path); + self.cmd.arg(&arg); + } + + fn framework_path(&mut self, _path: &Path) { + panic!("frameworks are not supported on windows") + } + fn link_framework(&mut self, _framework: &str) { + panic!("frameworks are not supported on windows") + } + + fn link_whole_staticlib(&mut self, lib: &str, _search_path: &[PathBuf]) { + // not supported? + self.link_staticlib(lib); + } + fn optimize(&mut self) { + // Needs more investigation of `/OPT` arguments + } + fn whole_archives(&mut self) { + // hints not supported? + } + fn no_whole_archives(&mut self) { + // hints not supported? + } + + // On windows static libraries are of the form `foo.lib` and dynamic + // libraries are not linked against directly, but rather through their + // import libraries also called `foo.lib`. As a result there's no + // possibility for a native library to appear both dynamically and + // statically in the same folder so we don't have to worry about hints like + // we do on Unix platforms. + fn hint_static(&mut self) {} + fn hint_dynamic(&mut self) {} +} diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index 3e2db80a9c556..2a823c69276f6 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -74,6 +74,7 @@ pub mod back { pub use rustc_back::x86; pub use rustc_back::x86_64; + pub mod linker; pub mod link; pub mod lto; pub mod write; diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 4879975dde695..4a6c73009f1ad 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -237,6 +237,9 @@ pub fn get_extern_const<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, did: ast::DefId, llvm::set_thread_local(c, true); } } + if ccx.use_dll_storage_attrs() { + llvm::SetDLLStorageClass(c, llvm::DLLImportStorageClass); + } ccx.externs().borrow_mut().insert(name.to_string(), c); return c; } @@ -670,7 +673,8 @@ pub fn trans_external_path<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, ccx.sess().bug("unexpected intrinsic in trans_external_path") } _ => { - let llfn = foreign::register_foreign_item_fn(ccx, fn_ty.abi, t, &name[..]); + let llfn = foreign::register_foreign_item_fn(ccx, fn_ty.abi, + t, &name); let attrs = csearch::get_item_attrs(&ccx.sess().cstore, did); attributes::from_fn_attrs(ccx, &attrs, llfn); llfn @@ -1939,11 +1943,17 @@ pub fn update_linkage(ccx: &CrateContext, match id { Some(id) if ccx.reachable().contains(&id) => { llvm::SetLinkage(llval, llvm::ExternalLinkage); + if ccx.use_dll_storage_attrs() { + llvm::SetDLLStorageClass(llval, llvm::DLLExportStorageClass); + } }, _ => { // `id` does not refer to an item in `ccx.reachable`. if ccx.sess().opts.cg.codegen_units > 1 { llvm::SetLinkage(llval, llvm::ExternalLinkage); + if ccx.use_dll_storage_attrs() { + llvm::SetDLLStorageClass(llval, llvm::DLLExportStorageClass); + } } else { llvm::SetLinkage(llval, llvm::InternalLinkage); } @@ -2102,9 +2112,15 @@ fn finish_register_fn(ccx: &CrateContext, sym: String, node_id: ast::NodeId, if ccx.tcx().lang_items.stack_exhausted() == Some(def) { attributes::split_stack(llfn, false); llvm::SetLinkage(llfn, llvm::ExternalLinkage); + if ccx.use_dll_storage_attrs() { + llvm::SetDLLStorageClass(llfn, llvm::DLLExportStorageClass); + } } if ccx.tcx().lang_items.eh_personality() == Some(def) { llvm::SetLinkage(llfn, llvm::ExternalLinkage); + if ccx.use_dll_storage_attrs() { + llvm::SetDLLStorageClass(llfn, llvm::DLLExportStorageClass); + } } } @@ -2171,7 +2187,7 @@ pub fn create_entry_wrapper(ccx: &CrateContext, // FIXME: #16581: Marking a symbol in the executable with `dllexport` // linkage forces MinGW's linker to output a `.reloc` section for ASLR if ccx.sess().target.target.options.is_like_windows { - unsafe { llvm::LLVMRustSetDLLExportStorageClass(llfn) } + llvm::SetDLLStorageClass(llfn, llvm::DLLExportStorageClass); } let llbb = unsafe { @@ -2527,7 +2543,7 @@ pub fn write_metadata(cx: &SharedCrateContext, krate: &ast::Crate) -> Vec { }; unsafe { llvm::LLVMSetInitializer(llglobal, llconst); - let name = loader::meta_section_name(cx.sess().target.target.options.is_like_osx); + let name = loader::meta_section_name(&cx.sess().target.target); let name = CString::new(name).unwrap(); llvm::LLVMSetSection(llglobal, name.as_ptr()) } @@ -2588,6 +2604,7 @@ fn internalize_symbols(cx: &SharedCrateContext, reachable: &HashSet) { if !declared.contains(&name) && !reachable.contains(str::from_utf8(&name).unwrap()) { llvm::SetLinkage(val, llvm::InternalLinkage); + llvm::SetDLLStorageClass(val, llvm::DefaultStorageClass); } } } diff --git a/src/librustc_trans/trans/context.rs b/src/librustc_trans/trans/context.rs index 41ef566f2fd7f..51db0adf5b775 100644 --- a/src/librustc_trans/trans/context.rs +++ b/src/librustc_trans/trans/context.rs @@ -75,6 +75,7 @@ pub struct SharedCrateContext<'tcx> { available_monomorphizations: RefCell>, available_drop_glues: RefCell, String>>, + use_dll_storage_attrs: bool, } /// The local portion of a `CrateContext`. There is one `LocalCrateContext` @@ -251,6 +252,51 @@ impl<'tcx> SharedCrateContext<'tcx> { create_context_and_module(&tcx.sess, "metadata") }; + // An interesting part of Windows which MSVC forces our hand on (and + // apparently MinGW didn't) is the usage of `dllimport` and `dllexport` + // attributes in LLVM IR as well as native dependencies (in C these + // correspond to `__declspec(dllimport)`). + // + // Whenever a dynamic library is built by MSVC it must have its public + // interface specified by functions tagged with `dllexport` or otherwise + // they're not available to be linked against. This poses a few problems + // for the compiler, some of which are somewhat fundamental, but we use + // the `use_dll_storage_attrs` variable below to attach the `dllexport` + // attribute to all LLVM functions that are reachable (e.g. they're + // already tagged with external linkage). This is suboptimal for a few + // reasons: + // + // * If an object file will never be included in a dynamic library, + // there's no need to attach the dllexport attribute. Most object + // files in Rust are not destined to become part of a dll as binaries + // are statically linked by default. + // * If the compiler is emitting both an rlib and a dylib, the same + // source object file is currently used but with MSVC this may be less + // feasible. The compiler may be able to get around this, but it may + // involve some invasive changes to deal with this. + // + // The flipside of this situation is that whenever you link to a dll and + // you import a function from it, the import should be tagged with + // `dllimport`. At this time, however, the compiler does not emit + // `dllimport` for any declarations other than constants (where it is + // required), which is again suboptimal for even more reasons! + // + // * Calling a function imported from another dll without using + // `dllimport` causes the linker/compiler to have extra overhead (one + // `jmp` instruction on x86) when calling the function. + // * The same object file may be used in different circumstances, so a + // function may be imported from a dll if the object is linked into a + // dll, but it may be just linked against if linked into an rlib. + // * The compiler has no knowledge about whether native functions should + // be tagged dllimport or not. + // + // For now the compiler takes the perf hit (I do not have any numbers to + // this effect) by marking very little as `dllimport` and praying the + // linker will take care of everything. Fixing this problem will likely + // require adding a few attributes to Rust itself (feature gated at the + // start) and then strongly recommending static linkage on MSVC! + let use_dll_storage_attrs = tcx.sess.target.target.options.is_like_msvc; + let mut shared_ccx = SharedCrateContext { local_ccxs: Vec::with_capacity(local_count), metadata_llmod: metadata_llmod, @@ -277,6 +323,7 @@ impl<'tcx> SharedCrateContext<'tcx> { check_drop_flag_for_sanity: check_drop_flag_for_sanity, available_monomorphizations: RefCell::new(FnvHashSet()), available_drop_glues: RefCell::new(FnvHashMap()), + use_dll_storage_attrs: use_dll_storage_attrs, }; for i in 0..local_count { @@ -365,6 +412,10 @@ impl<'tcx> SharedCrateContext<'tcx> { pub fn stats<'a>(&'a self) -> &'a Stats { &self.stats } + + pub fn use_dll_storage_attrs(&self) -> bool { + self.use_dll_storage_attrs + } } impl<'tcx> LocalCrateContext<'tcx> { @@ -733,6 +784,10 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> { // values. self.shared.check_drop_flag_for_sanity } + + pub fn use_dll_storage_attrs(&self) -> bool { + self.shared.use_dll_storage_attrs() + } } /// Declare any llvm intrinsics that you might need diff --git a/src/librustc_trans/trans/debuginfo/mod.rs b/src/librustc_trans/trans/debuginfo/mod.rs index e4312b669ad98..4e5407016ba9b 100644 --- a/src/librustc_trans/trans/debuginfo/mod.rs +++ b/src/librustc_trans/trans/debuginfo/mod.rs @@ -193,7 +193,7 @@ pub fn finalize(cx: &CrateContext) { // Prevent bitcode readers from deleting the debug info. let ptr = "Debug Info Version\0".as_ptr(); llvm::LLVMRustAddModuleFlag(cx.llmod(), ptr as *const _, - llvm::LLVMRustDebugMetadataVersion); + llvm::LLVMRustDebugMetadataVersion()); }; } diff --git a/src/librustc_trans/trans/declare.rs b/src/librustc_trans/trans/declare.rs index 7849235fdbee5..e0ab5dec98dfe 100644 --- a/src/librustc_trans/trans/declare.rs +++ b/src/librustc_trans/trans/declare.rs @@ -9,14 +9,14 @@ // except according to those terms. //! Declare various LLVM values. //! -//! Prefer using functions and methods from this module rather than calling LLVM functions -//! directly. These functions do some additional work to ensure we do the right thing given -//! the preconceptions of trans. +//! Prefer using functions and methods from this module rather than calling LLVM +//! functions directly. These functions do some additional work to ensure we do +//! the right thing given the preconceptions of trans. //! //! Some useful guidelines: //! -//! * Use declare_* family of methods if you are declaring, but are not interested in defining the -//! ValueRef they return. +//! * Use declare_* family of methods if you are declaring, but are not +//! interested in defining the ValueRef they return. //! * Use define_* family of methods when you might be defining the ValueRef. //! * When in doubt, define. use llvm::{self, ValueRef}; @@ -37,8 +37,8 @@ use libc::c_uint; /// Declare a global value. /// -/// If there’s a value with the same name already declared, the function will return its ValueRef -/// instead. +/// If there’s a value with the same name already declared, the function will +/// return its ValueRef instead. pub fn declare_global(ccx: &CrateContext, name: &str, ty: Type) -> llvm::ValueRef { debug!("declare_global(name={:?})", name); let namebuf = CString::new(name).unwrap_or_else(|_|{ @@ -54,10 +54,10 @@ pub fn declare_global(ccx: &CrateContext, name: &str, ty: Type) -> llvm::ValueRe /// /// For rust functions use `declare_rust_fn` instead. /// -/// If there’s a value with the same name already declared, the function will update the -/// declaration and return existing ValueRef instead. -pub fn declare_fn(ccx: &CrateContext, name: &str, callconv: llvm::CallConv, ty: Type, - output: ty::FnOutput) -> ValueRef { +/// If there’s a value with the same name already declared, the function will +/// update the declaration and return existing ValueRef instead. +pub fn declare_fn(ccx: &CrateContext, name: &str, callconv: llvm::CallConv, + ty: Type, output: ty::FnOutput) -> ValueRef { debug!("declare_fn(name={:?})", name); let namebuf = CString::new(name).unwrap_or_else(|_|{ ccx.sess().bug(&format!("name {:?} contains an interior null byte", name)) @@ -67,7 +67,8 @@ pub fn declare_fn(ccx: &CrateContext, name: &str, callconv: llvm::CallConv, ty: }; llvm::SetFunctionCallConv(llfn, callconv); - // Function addresses in Rust are never significant, allowing functions to be merged. + // Function addresses in Rust are never significant, allowing functions to + // be merged. llvm::SetUnnamedAddr(llfn, true); if output == ty::FnDiverging { @@ -88,23 +89,25 @@ pub fn declare_fn(ccx: &CrateContext, name: &str, callconv: llvm::CallConv, ty: /// Declare a C ABI function. /// -/// Only use this for foreign function ABIs and glue. For Rust functions use `declare_rust_fn` -/// instead. +/// Only use this for foreign function ABIs and glue. For Rust functions use +/// `declare_rust_fn` instead. /// -/// If there’s a value with the same name already declared, the function will update the -/// declaration and return existing ValueRef instead. -pub fn declare_cfn(ccx: &CrateContext, name: &str, fn_type: Type, output: ty::Ty) -> ValueRef { +/// If there’s a value with the same name already declared, the function will +/// update the declaration and return existing ValueRef instead. +pub fn declare_cfn(ccx: &CrateContext, name: &str, fn_type: Type, + output: ty::Ty) -> ValueRef { declare_fn(ccx, name, llvm::CCallConv, fn_type, ty::FnConverging(output)) } /// Declare a Rust function. /// -/// If there’s a value with the same name already declared, the function will update the -/// declaration and return existing ValueRef instead. +/// If there’s a value with the same name already declared, the function will +/// update the declaration and return existing ValueRef instead. pub fn declare_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &str, fn_type: ty::Ty<'tcx>) -> ValueRef { - debug!("declare_rust_fn(name={:?}, fn_type={})", name, fn_type.repr(ccx.tcx())); + debug!("declare_rust_fn(name={:?}, fn_type={})", name, + fn_type.repr(ccx.tcx())); let fn_type = monomorphize::normalize_associated_type(ccx.tcx(), &fn_type); debug!("declare_rust_fn (after normalised associated types) fn_type={}", fn_type.repr(ccx.tcx())); @@ -131,7 +134,8 @@ pub fn declare_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &str, let llfty = type_of::type_of_rust_fn(ccx, env, &sig, abi); debug!("declare_rust_fn llfty={}", ccx.tn().type_to_string(llfty)); - // it is ok to directly access sig.0.output because we erased all late-bound-regions above + // it is ok to directly access sig.0.output because we erased all + // late-bound-regions above let llfn = declare_fn(ccx, name, llvm::CCallConv, llfty, sig.0.output); attributes::from_fn_type(ccx, fn_type).apply_llfn(llfn); llfn @@ -140,8 +144,8 @@ pub fn declare_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &str, /// Declare a Rust function with internal linkage. /// -/// If there’s a value with the same name already declared, the function will update the -/// declaration and return existing ValueRef instead. +/// If there’s a value with the same name already declared, the function will +/// update the declaration and return existing ValueRef instead. pub fn declare_internal_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &str, fn_type: ty::Ty<'tcx>) -> ValueRef { let llfn = declare_rust_fn(ccx, name, fn_type); @@ -152,10 +156,10 @@ pub fn declare_internal_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &s /// Declare a global with an intention to define it. /// -/// Use this function when you intend to define a global. This function will return None if the -/// name already has a definition associated with it. In that case an error should be reported to -/// the user, because it usually happens due to user’s fault (e.g. misuse of #[no_mangle] or -/// #[export_name] attributes). +/// Use this function when you intend to define a global. This function will +/// return None if the name already has a definition associated with it. In that +/// case an error should be reported to the user, because it usually happens due +/// to user’s fault (e.g. misuse of #[no_mangle] or #[export_name] attributes). pub fn define_global(ccx: &CrateContext, name: &str, ty: Type) -> Option { if get_defined_value(ccx, name).is_some() { None @@ -169,10 +173,10 @@ pub fn define_global(ccx: &CrateContext, name: &str, ty: Type) -> Option Option { if get_defined_value(ccx, name).is_some() { @@ -185,13 +189,13 @@ pub fn define_fn(ccx: &CrateContext, name: &str, callconv: llvm::CallConv, fn_ty /// Declare a C ABI function with an intention to define it. /// -/// Use this function when you intend to define a function. This function will return None if the -/// name already has a definition associated with it. In that case an error should be reported to -/// the user, because it usually happens due to user’s fault (e.g. misuse of #[no_mangle] or -/// #[export_name] attributes). +/// Use this function when you intend to define a function. This function will +/// return None if the name already has a definition associated with it. In that +/// case an error should be reported to the user, because it usually happens due +/// to user’s fault (e.g. misuse of #[no_mangle] or #[export_name] attributes). /// -/// Only use this for foreign function ABIs and glue. For Rust functions use `declare_rust_fn` -/// instead. +/// Only use this for foreign function ABIs and glue. For Rust functions use +/// `declare_rust_fn` instead. pub fn define_cfn(ccx: &CrateContext, name: &str, fn_type: Type, output: ty::Ty) -> Option { if get_defined_value(ccx, name).is_some() { @@ -204,10 +208,10 @@ pub fn define_cfn(ccx: &CrateContext, name: &str, fn_type: Type, /// Declare a Rust function with an intention to define it. /// -/// Use this function when you intend to define a function. This function will return None if the -/// name already has a definition associated with it. In that case an error should be reported to -/// the user, because it usually happens due to user’s fault (e.g. misuse of #[no_mangle] or -/// #[export_name] attributes). +/// Use this function when you intend to define a function. This function will +/// return None if the name already has a definition associated with it. In that +/// case an error should be reported to the user, because it usually happens due +/// to user’s fault (e.g. misuse of #[no_mangle] or #[export_name] attributes). pub fn define_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &str, fn_type: ty::Ty<'tcx>) -> Option { if get_defined_value(ccx, name).is_some() { @@ -220,10 +224,10 @@ pub fn define_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &str, /// Declare a Rust function with an intention to define it. /// -/// Use this function when you intend to define a function. This function will return None if the -/// name already has a definition associated with it. In that case an error should be reported to -/// the user, because it usually happens due to user’s fault (e.g. misuse of #[no_mangle] or -/// #[export_name] attributes). +/// Use this function when you intend to define a function. This function will +/// return None if the name already has a definition associated with it. In that +/// case an error should be reported to the user, because it usually happens due +/// to user’s fault (e.g. misuse of #[no_mangle] or #[export_name] attributes). pub fn define_internal_rust_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, name: &str, fn_type: ty::Ty<'tcx>) -> Option { if get_defined_value(ccx, name).is_some() { @@ -250,8 +254,8 @@ fn get_defined_value(ccx: &CrateContext, name: &str) -> Option { (llvm::LLVMIsDeclaration(val) != 0, linkage == llvm::AvailableExternallyLinkage as c_uint) }; - debug!("get_defined_value: found {:?} value (declaration: {}, aext_link: {})", name, - declaration, aext_link); + debug!("get_defined_value: found {:?} value (declaration: {}, \ + aext_link: {})", name, declaration, aext_link); if !declaration || aext_link { Some(val) } else { diff --git a/src/libstd/num/f32.rs b/src/libstd/num/f32.rs index 1ee3aab2727a8..e31d97b324038 100644 --- a/src/libstd/num/f32.rs +++ b/src/libstd/num/f32.rs @@ -32,7 +32,6 @@ pub use core::f32::consts; mod cmath { use libc::{c_float, c_int}; - #[link_name = "m"] extern { pub fn acosf(n: c_float) -> c_float; pub fn asinf(n: c_float) -> c_float; @@ -44,13 +43,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; @@ -60,12 +56,27 @@ mod cmath { pub fn tanhf(n: c_float) -> c_float; pub fn tgammaf(n: c_float) -> c_float; - #[cfg(unix)] + #[cfg_attr(all(windows, target_env = "msvc"), link_name = "__lgammaf_r")] pub fn lgammaf_r(n: c_float, sign: &mut c_int) -> c_float; + #[cfg_attr(all(windows, target_env = "msvc"), link_name = "_hypotf")] + pub fn hypotf(x: c_float, y: c_float) -> c_float; - #[cfg(windows)] - #[link_name="__lgammaf_r"] - pub fn lgammaf_r(n: c_float, sign: &mut c_int) -> c_float; + #[cfg(any(unix, all(windows, not(target_env = "msvc"))))] + pub fn frexpf(n: c_float, value: &mut c_int) -> c_float; + #[cfg(any(unix, all(windows, not(target_env = "msvc"))))] + pub fn ldexpf(x: c_float, n: c_int) -> c_float; + } + + #[cfg(all(windows, target_env = "msvc"))] + pub unsafe fn ldexpf(x: c_float, n: c_int) -> c_float { + f64::ldexp(x as f64, n as isize) as c_float + } + + #[cfg(all(windows, target_env = "msvc"))] + pub unsafe fn frexpf(x: c_float, value: &mut c_int) -> c_float { + let (a, b) = f64::frexp(x as f64); + *value = b as c_int; + a as c_float } } diff --git a/src/libstd/num/f64.rs b/src/libstd/num/f64.rs index 398afcb553c1c..e87855ffd4eed 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; @@ -69,11 +68,11 @@ mod cmath { pub fn y1(n: c_double) -> c_double; pub fn yn(i: c_int, n: c_double) -> c_double; - #[cfg(unix)] - pub fn lgamma_r(n: c_double, sign: &mut c_int) -> c_double; - #[cfg(windows)] - #[link_name="__lgamma_r"] + #[cfg_attr(all(windows, target_env = "msvc"), link_name = "__lgamma_r")] pub fn lgamma_r(n: c_double, sign: &mut c_int) -> c_double; + + #[cfg_attr(all(windows, target_env = "msvc"), link_name = "_hypot")] + pub fn hypot(x: c_double, y: c_double) -> c_double; } } diff --git a/src/libstd/rand/os.rs b/src/libstd/rand/os.rs index 3c36f0f1d490c..885adf19ca1fc 100644 --- a/src/libstd/rand/os.rs +++ b/src/libstd/rand/os.rs @@ -279,6 +279,7 @@ mod imp { const CRYPT_VERIFYCONTEXT: DWORD = 0xF0000000; #[allow(non_snake_case)] + #[link(name = "advapi32")] extern "system" { fn CryptAcquireContextA(phProv: *mut HCRYPTPROV, pszContainer: LPCSTR, diff --git a/src/libstd/rt/unwind.rs b/src/libstd/rt/unwind.rs deleted file mode 100644 index b24099505ed89..0000000000000 --- a/src/libstd/rt/unwind.rs +++ /dev/null @@ -1,615 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Implementation of Rust stack unwinding -//! -//! For background on exception handling and stack unwinding please see -//! "Exception Handling in LLVM" (llvm.org/docs/ExceptionHandling.html) and -//! documents linked from it. -//! These are also good reads: -//! http://theofilos.cs.columbia.edu/blog/2013/09/22/base_abi/ -//! http://monoinfinito.wordpress.com/series/exception-handling-in-c/ -//! http://www.airs.com/blog/index.php?s=exception+frames -//! -//! ## A brief summary -//! -//! Exception handling happens in two phases: a search phase and a cleanup phase. -//! -//! In both phases the unwinder walks stack frames from top to bottom using -//! information from the stack frame unwind sections of the current process's -//! modules ("module" here refers to an OS module, i.e. an executable or a -//! dynamic library). -//! -//! For each stack frame, it invokes the associated "personality routine", whose -//! address is also stored in the unwind info section. -//! -//! In the search phase, the job of a personality routine is to examine exception -//! object being thrown, and to decide whether it should be caught at that stack -//! frame. Once the handler frame has been identified, cleanup phase begins. -//! -//! In the cleanup phase, personality routines invoke cleanup code associated -//! with their stack frames (i.e. destructors). Once stack has been unwound down -//! to the handler frame level, unwinding stops and the last personality routine -//! transfers control to its catch block. -//! -//! ## Frame unwind info registration -//! -//! Each module has its own frame unwind info section (usually ".eh_frame"), and -//! unwinder needs to know about all of them in order for unwinding to be able to -//! cross module boundaries. -//! -//! On some platforms, like Linux, this is achieved by dynamically enumerating -//! currently loaded modules via the dl_iterate_phdr() API and finding all -//! .eh_frame sections. -//! -//! Others, like Windows, require modules to actively register their unwind info -//! sections by calling __register_frame_info() API at startup. In the latter -//! case it is essential that there is only one copy of the unwinder runtime in -//! the process. This is usually achieved by linking to the dynamic version of -//! the unwind runtime. -//! -//! Currently Rust uses unwind runtime provided by libgcc. - -use prelude::v1::*; - -use any::Any; -use boxed; -use cell::Cell; -use cmp; -use panicking; -use fmt; -use intrinsics; -use libc::c_void; -use mem; -use sync::atomic::{self, Ordering}; -use sys_common::mutex::{Mutex, MUTEX_INIT}; - -use rt::libunwind as uw; - -struct Exception { - uwe: uw::_Unwind_Exception, - cause: Option>, -} - -pub type Callback = fn(msg: &(Any + Send), file: &'static str, line: u32); - -// Variables used for invoking callbacks when a thread starts to unwind. -// -// For more information, see below. -const MAX_CALLBACKS: usize = 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> { - let mut f = Some(f); - - let prev = PANICKING.with(|s| s.get()); - PANICKING.with(|s| s.set(false)); - let ep = rust_try(try_fn::, &mut f as *mut _ as *mut c_void); - PANICKING.with(|s| s.set(prev)); - return if ep.is_null() { - Ok(()) - } else { - let my_ep = ep as *mut Exception; - rtdebug!("caught {}", (*my_ep).uwe.exception_class); - let cause = (*my_ep).cause.take(); - uw::_Unwind_DeleteException(ep); - Err(cause.unwrap()) - }; - - extern fn try_fn(opt_closure: *mut c_void) { - let opt_closure = opt_closure as *mut Option; - unsafe { (*opt_closure).take().unwrap()(); } - } - - #[link(name = "rustrt_native", kind = "static")] - #[cfg(not(test))] - extern {} - - extern { - // Rust's try-catch - // When f(...) returns normally, the return value is null. - // When f(...) throws, the return value is a pointer to the caught - // exception object. - fn rust_try(f: extern fn(*mut c_void), - data: *mut c_void) -> *mut uw::_Unwind_Exception; - } -} - -/// 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) -> ! { - rtdebug!("begin_unwind()"); - - unsafe { - let exception: Box<_> = box Exception { - uwe: uw::_Unwind_Exception { - exception_class: rust_exception_class(), - exception_cleanup: exception_cleanup, - private: [0; uw::unwinder_private_data_size], - }, - cause: Some(cause), - }; - let exception_param = boxed::into_raw(exception) as *mut uw::_Unwind_Exception; - let error = uw::_Unwind_RaiseException(exception_param); - rtabort!("Could not unwind stack, error = {}", error as isize) - } - - extern fn exception_cleanup(_unwind_code: uw::_Unwind_Reason_Code, - exception: *mut uw::_Unwind_Exception) { - rtdebug!("exception_cleanup()"); - unsafe { - let _: Box = Box::from_raw(exception as *mut Exception); - } - } -} - -// Rust's exception class identifier. This is used by personality routines to -// determine whether the exception was thrown by their own runtime. -fn rust_exception_class() -> uw::_Unwind_Exception_Class { - // M O Z \0 R U S T -- vendor, language - 0x4d4f5a_00_52555354 -} - -// We could implement our personality routine in pure Rust, however exception -// info decoding is tedious. More importantly, personality routines have to -// handle various platform quirks, which are not fun to maintain. For this -// reason, we attempt to reuse personality routine of the C language: -// __gcc_personality_v0. -// -// Since C does not support exception catching, __gcc_personality_v0 simply -// always returns _URC_CONTINUE_UNWIND in search phase, and always returns -// _URC_INSTALL_CONTEXT (i.e. "invoke cleanup code") in cleanup phase. -// -// This is pretty close to Rust's exception handling approach, except that Rust -// does have a single "catch-all" handler at the bottom of each thread's stack. -// So we have two versions of the personality routine: -// - rust_eh_personality, used by all cleanup landing pads, which never catches, -// so the behavior of __gcc_personality_v0 is perfectly adequate there, and -// - rust_eh_personality_catch, used only by rust_try(), which always catches. -// -// Note, however, that for implementation simplicity, rust_eh_personality_catch -// lacks code to install a landing pad, so in order to obtain exception object -// pointer (which it needs to return upstream), rust_try() employs another trick: -// it calls into the nested rust_try_inner(), whose landing pad does not resume -// unwinds. Instead, it extracts the exception pointer and performs a "normal" -// return. -// -// See also: rt/rust_try.ll - -#[cfg(all(not(target_arch = "arm"), - not(all(windows, target_arch = "x86_64")), - not(test)))] -#[doc(hidden)] -pub mod eabi { - use rt::libunwind as uw; - use libc::c_int; - - extern "C" { - fn __gcc_personality_v0(version: c_int, - actions: uw::_Unwind_Action, - exception_class: uw::_Unwind_Exception_Class, - ue_header: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context) - -> uw::_Unwind_Reason_Code; - } - - #[lang = "eh_personality"] - #[no_mangle] // referenced from rust_try.ll - #[allow(private_no_mangle_fns)] - extern fn rust_eh_personality( - version: c_int, - actions: uw::_Unwind_Action, - exception_class: uw::_Unwind_Exception_Class, - ue_header: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context - ) -> uw::_Unwind_Reason_Code - { - unsafe { - __gcc_personality_v0(version, actions, exception_class, ue_header, - context) - } - } - - #[no_mangle] // referenced from rust_try.ll - pub extern "C" fn rust_eh_personality_catch( - _version: c_int, - actions: uw::_Unwind_Action, - _exception_class: uw::_Unwind_Exception_Class, - _ue_header: *mut uw::_Unwind_Exception, - _context: *mut uw::_Unwind_Context - ) -> uw::_Unwind_Reason_Code - { - - if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase - uw::_URC_HANDLER_FOUND // catch! - } - else { // cleanup phase - uw::_URC_INSTALL_CONTEXT - } - } -} - -// iOS on armv7 is using SjLj exceptions and therefore requires to use -// a specialized personality routine: __gcc_personality_sj0 - -#[cfg(all(target_os = "ios", target_arch = "arm", not(test)))] -#[doc(hidden)] -pub mod eabi { - use rt::libunwind as uw; - use libc::c_int; - - extern "C" { - fn __gcc_personality_sj0(version: c_int, - actions: uw::_Unwind_Action, - exception_class: uw::_Unwind_Exception_Class, - ue_header: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context) - -> uw::_Unwind_Reason_Code; - } - - #[lang = "eh_personality"] - #[no_mangle] // referenced from rust_try.ll - pub extern "C" fn rust_eh_personality( - version: c_int, - actions: uw::_Unwind_Action, - exception_class: uw::_Unwind_Exception_Class, - ue_header: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context - ) -> uw::_Unwind_Reason_Code - { - unsafe { - __gcc_personality_sj0(version, actions, exception_class, ue_header, - context) - } - } - - #[no_mangle] // referenced from rust_try.ll - pub extern "C" fn rust_eh_personality_catch( - _version: c_int, - actions: uw::_Unwind_Action, - _exception_class: uw::_Unwind_Exception_Class, - _ue_header: *mut uw::_Unwind_Exception, - _context: *mut uw::_Unwind_Context - ) -> uw::_Unwind_Reason_Code - { - if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase - uw::_URC_HANDLER_FOUND // catch! - } - else { // cleanup phase - unsafe { - __gcc_personality_sj0(_version, actions, _exception_class, _ue_header, - _context) - } - } - } -} - - -// ARM EHABI uses a slightly different personality routine signature, -// but otherwise works the same. -#[cfg(all(target_arch = "arm", not(target_os = "ios"), not(test)))] -#[doc(hidden)] -pub mod eabi { - use rt::libunwind as uw; - use libc::c_int; - - extern "C" { - fn __gcc_personality_v0(state: uw::_Unwind_State, - ue_header: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context) - -> uw::_Unwind_Reason_Code; - } - - #[lang = "eh_personality"] - #[no_mangle] // referenced from rust_try.ll - #[allow(private_no_mangle_fns)] - extern "C" fn rust_eh_personality( - state: uw::_Unwind_State, - ue_header: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context - ) -> uw::_Unwind_Reason_Code - { - unsafe { - __gcc_personality_v0(state, ue_header, context) - } - } - - #[no_mangle] // referenced from rust_try.ll - pub extern "C" fn rust_eh_personality_catch( - state: uw::_Unwind_State, - _ue_header: *mut uw::_Unwind_Exception, - _context: *mut uw::_Unwind_Context - ) -> uw::_Unwind_Reason_Code - { - if (state as c_int & uw::_US_ACTION_MASK as c_int) - == uw::_US_VIRTUAL_UNWIND_FRAME as c_int { // search phase - uw::_URC_HANDLER_FOUND // catch! - } - else { // cleanup phase - uw::_URC_INSTALL_CONTEXT - } - } -} - -// Win64 SEH (see http://msdn.microsoft.com/en-us/library/1eyas8tf.aspx) -// -// This looks a bit convoluted because rather than implementing a native SEH handler, -// GCC reuses the same personality routine as for the other architectures by wrapping it -// with an "API translator" layer (_GCC_specific_handler). - -#[cfg(all(windows, target_arch = "x86_64", not(test)))] -#[doc(hidden)] -#[allow(non_camel_case_types, non_snake_case)] -pub mod eabi { - pub use self::EXCEPTION_DISPOSITION::*; - use rt::libunwind as uw; - use libc::{c_void, c_int}; - - #[repr(C)] - pub struct EXCEPTION_RECORD; - #[repr(C)] - pub struct CONTEXT; - #[repr(C)] - pub struct DISPATCHER_CONTEXT; - - #[repr(C)] - #[derive(Copy, Clone)] - pub enum EXCEPTION_DISPOSITION { - ExceptionContinueExecution, - ExceptionContinueSearch, - ExceptionNestedException, - ExceptionCollidedUnwind - } - - type _Unwind_Personality_Fn = - extern "C" fn( - version: c_int, - actions: uw::_Unwind_Action, - exception_class: uw::_Unwind_Exception_Class, - ue_header: *mut uw::_Unwind_Exception, - context: *mut uw::_Unwind_Context - ) -> uw::_Unwind_Reason_Code; - - extern "C" { - fn __gcc_personality_seh0( - exceptionRecord: *mut EXCEPTION_RECORD, - establisherFrame: *mut c_void, - contextRecord: *mut CONTEXT, - dispatcherContext: *mut DISPATCHER_CONTEXT - ) -> EXCEPTION_DISPOSITION; - - fn _GCC_specific_handler( - exceptionRecord: *mut EXCEPTION_RECORD, - establisherFrame: *mut c_void, - contextRecord: *mut CONTEXT, - dispatcherContext: *mut DISPATCHER_CONTEXT, - personality: _Unwind_Personality_Fn - ) -> EXCEPTION_DISPOSITION; - } - - #[lang = "eh_personality"] - #[no_mangle] // referenced from rust_try.ll - #[allow(private_no_mangle_fns)] - extern "C" fn rust_eh_personality( - exceptionRecord: *mut EXCEPTION_RECORD, - establisherFrame: *mut c_void, - contextRecord: *mut CONTEXT, - dispatcherContext: *mut DISPATCHER_CONTEXT - ) -> EXCEPTION_DISPOSITION - { - unsafe { - __gcc_personality_seh0(exceptionRecord, establisherFrame, - contextRecord, dispatcherContext) - } - } - - #[no_mangle] // referenced from rust_try.ll - pub extern "C" fn rust_eh_personality_catch( - exceptionRecord: *mut EXCEPTION_RECORD, - establisherFrame: *mut c_void, - contextRecord: *mut CONTEXT, - dispatcherContext: *mut DISPATCHER_CONTEXT - ) -> EXCEPTION_DISPOSITION - { - extern "C" fn inner( - _version: c_int, - actions: uw::_Unwind_Action, - _exception_class: uw::_Unwind_Exception_Class, - _ue_header: *mut uw::_Unwind_Exception, - _context: *mut uw::_Unwind_Context - ) -> uw::_Unwind_Reason_Code - { - if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase - uw::_URC_HANDLER_FOUND // catch! - } - else { // cleanup phase - uw::_URC_INSTALL_CONTEXT - } - } - - unsafe { - _GCC_specific_handler(exceptionRecord, establisherFrame, - contextRecord, dispatcherContext, - inner) - } - } -} - -#[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: u32) -> ! { - 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] -pub fn begin_unwind_fmt(msg: fmt::Arguments, file_line: &(&'static str, u32)) -> ! { - 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 _ = s.write_fmt(msg); - begin_unwind_inner(Box::new(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 -pub fn begin_unwind(msg: M, file_line: &(&'static str, u32)) -> ! { - // 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::new(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, u32)) -> ! { - // Make sure the default failure handler is registered before we look at the - // callbacks. We also use a raw sys-based mutex here instead of a - // `std::sync` one as accessing TLS can cause weird recursive problems (and - // we don't need poison checking). - unsafe { - static LOCK: Mutex = MUTEX_INIT; - static mut INIT: bool = false; - LOCK.lock(); - if !INIT { - register(panicking::on_panic); - INIT = true; - } - LOCK.unlock(); - } - - // 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 `Thread` 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. -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/libstd/rt/unwind/gcc.rs b/src/libstd/rt/unwind/gcc.rs new file mode 100644 index 0000000000000..39b32a3f08e49 --- /dev/null +++ b/src/libstd/rt/unwind/gcc.rs @@ -0,0 +1,342 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use prelude::v1::*; + +use any::Any; +use boxed; +use libc::c_void; +use rt::libunwind as uw; + +struct Exception { + uwe: uw::_Unwind_Exception, + cause: Option>, +} + +pub unsafe fn panic(data: Box) -> ! { + let exception: Box<_> = box Exception { + uwe: uw::_Unwind_Exception { + exception_class: rust_exception_class(), + exception_cleanup: exception_cleanup, + private: [0; uw::unwinder_private_data_size], + }, + cause: Some(data), + }; + let exception_param = boxed::into_raw(exception) as *mut uw::_Unwind_Exception; + let error = uw::_Unwind_RaiseException(exception_param); + rtabort!("Could not unwind stack, error = {}", error as isize); + + extern fn exception_cleanup(_unwind_code: uw::_Unwind_Reason_Code, + exception: *mut uw::_Unwind_Exception) { + rtdebug!("exception_cleanup()"); + unsafe { + let _: Box = Box::from_raw(exception as *mut Exception); + } + } +} + +pub unsafe fn cleanup(ptr: *mut c_void) -> Box { + let my_ep = ptr as *mut Exception; + rtdebug!("caught {}", (*my_ep).uwe.exception_class); + let cause = (*my_ep).cause.take(); + uw::_Unwind_DeleteException(ptr as *mut _); + cause.unwrap() +} + +// Rust's exception class identifier. This is used by personality routines to +// determine whether the exception was thrown by their own runtime. +fn rust_exception_class() -> uw::_Unwind_Exception_Class { + // M O Z \0 R U S T -- vendor, language + 0x4d4f5a_00_52555354 +} + +// We could implement our personality routine in pure Rust, however exception +// info decoding is tedious. More importantly, personality routines have to +// handle various platform quirks, which are not fun to maintain. For this +// reason, we attempt to reuse personality routine of the C language: +// __gcc_personality_v0. +// +// Since C does not support exception catching, __gcc_personality_v0 simply +// always returns _URC_CONTINUE_UNWIND in search phase, and always returns +// _URC_INSTALL_CONTEXT (i.e. "invoke cleanup code") in cleanup phase. +// +// This is pretty close to Rust's exception handling approach, except that Rust +// does have a single "catch-all" handler at the bottom of each thread's stack. +// So we have two versions of the personality routine: +// - rust_eh_personality, used by all cleanup landing pads, which never catches, +// so the behavior of __gcc_personality_v0 is perfectly adequate there, and +// - rust_eh_personality_catch, used only by rust_try(), which always catches. +// +// Note, however, that for implementation simplicity, rust_eh_personality_catch +// lacks code to install a landing pad, so in order to obtain exception object +// pointer (which it needs to return upstream), rust_try() employs another trick: +// it calls into the nested rust_try_inner(), whose landing pad does not resume +// unwinds. Instead, it extracts the exception pointer and performs a "normal" +// return. +// +// See also: rt/rust_try.ll + +#[cfg(all(not(target_arch = "arm"), + not(all(windows, target_arch = "x86_64")), + not(test)))] +pub mod eabi { + use rt::libunwind as uw; + use libc::c_int; + + extern "C" { + fn __gcc_personality_v0(version: c_int, + actions: uw::_Unwind_Action, + exception_class: uw::_Unwind_Exception_Class, + ue_header: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context) + -> uw::_Unwind_Reason_Code; + } + + #[lang="eh_personality"] + #[no_mangle] // referenced from rust_try.ll + #[allow(private_no_mangle_fns)] + extern fn rust_eh_personality( + version: c_int, + actions: uw::_Unwind_Action, + exception_class: uw::_Unwind_Exception_Class, + ue_header: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context + ) -> uw::_Unwind_Reason_Code + { + unsafe { + __gcc_personality_v0(version, actions, exception_class, ue_header, + context) + } + } + + #[no_mangle] // referenced from rust_try.ll + pub extern "C" fn rust_eh_personality_catch( + _version: c_int, + actions: uw::_Unwind_Action, + _exception_class: uw::_Unwind_Exception_Class, + _ue_header: *mut uw::_Unwind_Exception, + _context: *mut uw::_Unwind_Context + ) -> uw::_Unwind_Reason_Code + { + + if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase + uw::_URC_HANDLER_FOUND // catch! + } + else { // cleanup phase + uw::_URC_INSTALL_CONTEXT + } + } +} + +// iOS on armv7 is using SjLj exceptions and therefore requires to use +// a specialized personality routine: __gcc_personality_sj0 + +#[cfg(all(target_os = "ios", target_arch = "arm", not(test)))] +pub mod eabi { + use rt::libunwind as uw; + use libc::c_int; + + extern "C" { + fn __gcc_personality_sj0(version: c_int, + actions: uw::_Unwind_Action, + exception_class: uw::_Unwind_Exception_Class, + ue_header: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context) + -> uw::_Unwind_Reason_Code; + } + + #[lang="eh_personality"] + #[no_mangle] // referenced from rust_try.ll + pub extern "C" fn rust_eh_personality( + version: c_int, + actions: uw::_Unwind_Action, + exception_class: uw::_Unwind_Exception_Class, + ue_header: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context + ) -> uw::_Unwind_Reason_Code + { + unsafe { + __gcc_personality_sj0(version, actions, exception_class, ue_header, + context) + } + } + + #[no_mangle] // referenced from rust_try.ll + pub extern "C" fn rust_eh_personality_catch( + _version: c_int, + actions: uw::_Unwind_Action, + _exception_class: uw::_Unwind_Exception_Class, + _ue_header: *mut uw::_Unwind_Exception, + _context: *mut uw::_Unwind_Context + ) -> uw::_Unwind_Reason_Code + { + if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase + uw::_URC_HANDLER_FOUND // catch! + } + else { // cleanup phase + unsafe { + __gcc_personality_sj0(_version, actions, _exception_class, _ue_header, + _context) + } + } + } +} + + +// ARM EHABI uses a slightly different personality routine signature, +// but otherwise works the same. +#[cfg(all(target_arch = "arm", not(target_os = "ios"), not(test)))] +pub mod eabi { + use rt::libunwind as uw; + use libc::c_int; + + extern "C" { + fn __gcc_personality_v0(state: uw::_Unwind_State, + ue_header: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context) + -> uw::_Unwind_Reason_Code; + } + + #[lang="eh_personality"] + #[no_mangle] // referenced from rust_try.ll + #[allow(private_no_mangle_fns)] + extern "C" fn rust_eh_personality( + state: uw::_Unwind_State, + ue_header: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context + ) -> uw::_Unwind_Reason_Code + { + unsafe { + __gcc_personality_v0(state, ue_header, context) + } + } + + #[no_mangle] // referenced from rust_try.ll + pub extern "C" fn rust_eh_personality_catch( + state: uw::_Unwind_State, + _ue_header: *mut uw::_Unwind_Exception, + _context: *mut uw::_Unwind_Context + ) -> uw::_Unwind_Reason_Code + { + if (state as c_int & uw::_US_ACTION_MASK as c_int) + == uw::_US_VIRTUAL_UNWIND_FRAME as c_int { // search phase + uw::_URC_HANDLER_FOUND // catch! + } + else { // cleanup phase + uw::_URC_INSTALL_CONTEXT + } + } +} + +// Win64 SEH (see http://msdn.microsoft.com/en-us/library/1eyas8tf.aspx) +// +// This looks a bit convoluted because rather than implementing a native SEH +// handler, GCC reuses the same personality routine as for the other +// architectures by wrapping it with an "API translator" layer +// (_GCC_specific_handler). + +#[cfg(all(windows, target_arch = "x86_64", not(test)))] +#[doc(hidden)] +#[allow(non_camel_case_types, non_snake_case)] +pub mod eabi { + pub use self::EXCEPTION_DISPOSITION::*; + use rt::libunwind as uw; + use libc::{c_void, c_int}; + + #[repr(C)] + pub struct EXCEPTION_RECORD; + #[repr(C)] + pub struct CONTEXT; + #[repr(C)] + pub struct DISPATCHER_CONTEXT; + + #[repr(C)] + #[derive(Copy, Clone)] + pub enum EXCEPTION_DISPOSITION { + ExceptionContinueExecution, + ExceptionContinueSearch, + ExceptionNestedException, + ExceptionCollidedUnwind + } + + type _Unwind_Personality_Fn = + extern "C" fn( + version: c_int, + actions: uw::_Unwind_Action, + exception_class: uw::_Unwind_Exception_Class, + ue_header: *mut uw::_Unwind_Exception, + context: *mut uw::_Unwind_Context + ) -> uw::_Unwind_Reason_Code; + + extern "C" { + fn __gcc_personality_seh0( + exceptionRecord: *mut EXCEPTION_RECORD, + establisherFrame: *mut c_void, + contextRecord: *mut CONTEXT, + dispatcherContext: *mut DISPATCHER_CONTEXT + ) -> EXCEPTION_DISPOSITION; + + fn _GCC_specific_handler( + exceptionRecord: *mut EXCEPTION_RECORD, + establisherFrame: *mut c_void, + contextRecord: *mut CONTEXT, + dispatcherContext: *mut DISPATCHER_CONTEXT, + personality: _Unwind_Personality_Fn + ) -> EXCEPTION_DISPOSITION; + } + + #[lang="eh_personality"] + #[no_mangle] // referenced from rust_try.ll + #[allow(private_no_mangle_fns)] + extern "C" fn rust_eh_personality( + exceptionRecord: *mut EXCEPTION_RECORD, + establisherFrame: *mut c_void, + contextRecord: *mut CONTEXT, + dispatcherContext: *mut DISPATCHER_CONTEXT + ) -> EXCEPTION_DISPOSITION + { + unsafe { + __gcc_personality_seh0(exceptionRecord, establisherFrame, + contextRecord, dispatcherContext) + } + } + + #[no_mangle] // referenced from rust_try.ll + pub extern "C" fn rust_eh_personality_catch( + exceptionRecord: *mut EXCEPTION_RECORD, + establisherFrame: *mut c_void, + contextRecord: *mut CONTEXT, + dispatcherContext: *mut DISPATCHER_CONTEXT + ) -> EXCEPTION_DISPOSITION + { + extern "C" fn inner( + _version: c_int, + actions: uw::_Unwind_Action, + _exception_class: uw::_Unwind_Exception_Class, + _ue_header: *mut uw::_Unwind_Exception, + _context: *mut uw::_Unwind_Context + ) -> uw::_Unwind_Reason_Code + { + if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 { // search phase + uw::_URC_HANDLER_FOUND // catch! + } + else { // cleanup phase + uw::_URC_INSTALL_CONTEXT + } + } + + unsafe { + _GCC_specific_handler(exceptionRecord, establisherFrame, + contextRecord, dispatcherContext, + inner) + } + } +} + diff --git a/src/libstd/rt/unwind/mod.rs b/src/libstd/rt/unwind/mod.rs new file mode 100644 index 0000000000000..576035ffe9a02 --- /dev/null +++ b/src/libstd/rt/unwind/mod.rs @@ -0,0 +1,319 @@ +// 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. + +#![allow(dead_code)] +#![allow(unused_imports)] + +use prelude::v1::*; + +use any::Any; +use boxed; +use cell::Cell; +use cmp; +use panicking; +use fmt; +use intrinsics; +use libc::c_void; +use mem; +use sync::atomic::{self, Ordering}; +use sys_common::mutex::{Mutex, MUTEX_INIT}; + +// The actual unwinding implementation is cfg'd here, and we've got two current +// implementations. One goes through SEH on Windows and the other goes through +// libgcc via the libunwind-like API. +#[cfg(target_env = "msvc")] #[path = "seh.rs"] #[doc(hidden)] +pub mod imp; +#[cfg(not(target_env = "msvc"))] #[path = "gcc.rs"] #[doc(hidden)] +pub mod imp; + +pub type Callback = fn(msg: &(Any + Send), file: &'static str, line: u32); + +// Variables used for invoking callbacks when a thread starts to unwind. +// +// For more information, see below. +const MAX_CALLBACKS: usize = 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) } + +#[link(name = "rustrt_native", kind = "static")] +#[cfg(not(test))] +extern {} + +/// Invoke a closure, capturing the cause of panic if one occurs. +/// +/// This function will return `Ok(())` if the closure did not panic, and will +/// 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> { + let mut f = Some(f); + return inner_try(try_fn::, &mut f as *mut _ as *mut c_void); + + // If an inner function were not used here, then this generic function `try` + // uses the native symbol `rust_try`, for which the code is statically + // linked into the standard library. This means that the DLL for the + // standard library must have `rust_try` as an exposed symbol that + // downstream crates can link against (because monomorphizations of `try` in + // downstream crates will have a reference to the `rust_try` symbol). + // + // On MSVC this requires the symbol `rust_try` to be tagged with + // `dllexport`, but it's easier to not have conditional `src/rt/rust_try.ll` + // files and instead just have this non-generic shim the compiler can take + // care of exposing correctly. + unsafe fn inner_try(f: extern fn(*mut c_void), data: *mut c_void) + -> Result<(), Box> { + let prev = PANICKING.with(|s| s.get()); + PANICKING.with(|s| s.set(false)); + let ep = rust_try(f, data); + PANICKING.with(|s| s.set(prev)); + if ep.is_null() { + Ok(()) + } else { + Err(imp::cleanup(ep)) + } + } + + extern fn try_fn(opt_closure: *mut c_void) { + let opt_closure = opt_closure as *mut Option; + unsafe { (*opt_closure).take().unwrap()(); } + } + + extern { + // Rust's try-catch + // When f(...) returns normally, the return value is null. + // When f(...) throws, the return value is a pointer to the caught + // exception object. + fn rust_try(f: extern fn(*mut c_void), + data: *mut c_void) -> *mut c_void; + } +} + +/// 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) -> ! { + rtdebug!("begin_unwind()"); + unsafe { + imp::panic(cause) + } +} + +#[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: u32) -> ! { + 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] +pub fn begin_unwind_fmt(msg: fmt::Arguments, file_line: &(&'static str, u32)) -> ! { + 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 _ = s.write_fmt(msg); + begin_unwind_inner(Box::new(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 +pub fn begin_unwind(msg: M, file_line: &(&'static str, u32)) -> ! { + // 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::new(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, u32)) -> ! { + // Make sure the default failure handler is registered before we look at the + // callbacks. We also use a raw sys-based mutex here instead of a + // `std::sync` one as accessing TLS can cause weird recursive problems (and + // we don't need poison checking). + unsafe { + static LOCK: Mutex = MUTEX_INIT; + static mut INIT: bool = false; + LOCK.lock(); + if !INIT { + register(panicking::on_panic); + INIT = true; + } + LOCK.unlock(); + } + + // 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 `Thread` 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. +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/libstd/rt/unwind/seh.rs b/src/libstd/rt/unwind/seh.rs new file mode 100644 index 0000000000000..a72c1debe14e0 --- /dev/null +++ b/src/libstd/rt/unwind/seh.rs @@ -0,0 +1,30 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use prelude::v1::*; + +use any::Any; +use intrinsics; +use libc::c_void; + +pub unsafe fn panic(_data: Box) -> ! { + intrinsics::abort(); +} + +pub unsafe fn cleanup(_ptr: *mut c_void) -> Box { + intrinsics::abort(); +} + +#[lang = "eh_personality"] +#[no_mangle] +pub extern fn rust_eh_personality() {} + +#[no_mangle] +pub extern fn rust_eh_personality_catch() {} diff --git a/src/rt/rust_builtin.c b/src/rt/rust_builtin.c index 362439c146912..92371e90aba0c 100644 --- a/src/rt/rust_builtin.c +++ b/src/rt/rust_builtin.c @@ -14,7 +14,8 @@ #include #include -#if !defined(__WIN32__) + +#if !defined(_WIN32) #include #include #include @@ -40,7 +41,15 @@ /* Foreign builtins. */ //include valgrind.h after stdint.h so that uintptr_t is defined for msys2 w64 +#ifndef _WIN32 #include "valgrind/valgrind.h" +#endif + +#if defined(_MSC_VER) +# define RUST_BUILTIN_API __declspec(dllexport) +#else +# define RUST_BUILTIN_API +#endif #ifndef _WIN32 char* @@ -84,12 +93,7 @@ rust_dirent_t_size() { } #endif -uintptr_t -rust_running_on_valgrind() { - return RUNNING_ON_VALGRIND; -} - -#if defined(__WIN32__) +#if defined(_WIN32) int get_num_cpus() { SYSTEM_INFO sysinfo; @@ -131,19 +135,19 @@ get_num_cpus() { } #endif +RUST_BUILTIN_API uintptr_t rust_get_num_cpus() { return get_num_cpus(); } -unsigned int -rust_valgrind_stack_register(void *start, void *end) { - return VALGRIND_STACK_REGISTER(start, end); -} - -void -rust_valgrind_stack_deregister(unsigned int id) { - VALGRIND_STACK_DEREGISTER(id); +uintptr_t +rust_running_on_valgrind() { +#ifdef _WIN32 + return 0; +#else + return RUNNING_ON_VALGRIND; +#endif } #if defined(__DragonFly__) diff --git a/src/rt/rust_test_helpers.c b/src/rt/rust_test_helpers.c index c755cf67caa9f..8824cef2a816c 100644 --- a/src/rt/rust_test_helpers.c +++ b/src/rt/rust_test_helpers.c @@ -135,6 +135,8 @@ struct ManyInts { struct TwoU8s arg6; }; +// MSVC doesn't allow empty structs or unions +#ifndef _MSC_VER struct Empty { }; @@ -148,6 +150,7 @@ rust_dbg_extern_empty_struct(struct ManyInts v1, struct Empty e, struct ManyInts assert(v1.arg6.one == v2.arg6.one + 1); assert(v1.arg6.two == v2.arg6.two + 1); } +#endif intptr_t rust_get_test_int() { @@ -191,9 +194,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() { diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp index 375c5fc746c5b..66db7326d21bf 100644 --- a/src/rustllvm/RustWrapper.cpp +++ b/src/rustllvm/RustWrapper.cpp @@ -233,7 +233,9 @@ DIT unwrapDI(LLVMMetadataRef ref) { return DIT(ref ? unwrap(ref) : NULL); } -extern "C" const uint32_t LLVMRustDebugMetadataVersion = DEBUG_METADATA_VERSION; +extern "C" const uint32_t LLVMRustDebugMetadataVersion() { + return DEBUG_METADATA_VERSION; +} extern "C" void LLVMRustAddModuleFlag(LLVMModuleRef M, const char *name, @@ -837,9 +839,10 @@ LLVMRustArchiveChildData(Archive::Child *child, size_t *size) { } extern "C" void -LLVMRustSetDLLExportStorageClass(LLVMValueRef Value) { +LLVMRustSetDLLStorageClass(LLVMValueRef Value, + GlobalValue::DLLStorageClassTypes Class) { GlobalValue *V = unwrap(Value); - V->setDLLStorageClass(GlobalValue::DLLExportStorageClass); + V->setDLLStorageClass(Class); } // Note that the two following functions look quite similar to the