about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Makefile.in5
-rwxr-xr-xconfigure108
-rw-r--r--mk/cfg/aarch64-apple-ios.mk1
-rw-r--r--mk/cfg/aarch64-linux-android.mk1
-rw-r--r--mk/cfg/aarch64-unknown-linux-gnu.mk1
-rw-r--r--mk/cfg/arm-linux-androideabi.mk1
-rw-r--r--mk/cfg/x86_64-pc-windows-gnu.mk1
-rw-r--r--mk/cfg/x86_64-pc-windows-msvc.mk82
-rw-r--r--mk/clean.mk13
-rw-r--r--mk/crates.mk3
-rw-r--r--mk/dist.mk11
-rw-r--r--mk/llvm.mk30
-rw-r--r--mk/main.mk11
-rw-r--r--mk/platform.mk42
-rw-r--r--mk/prepare.mk13
-rw-r--r--mk/rt.mk37
-rw-r--r--mk/rustllvm.mk28
-rw-r--r--mk/target.mk13
-rw-r--r--mk/tests.mk4
-rw-r--r--src/etc/mklldef.py25
-rw-r--r--src/etc/mklldeps.py7
-rw-r--r--src/liblibc/lib.rs6
-rw-r--r--src/librustc/metadata/loader.rs40
-rw-r--r--src/librustc_back/archive.rs174
-rw-r--r--src/librustc_back/target/linux_base.rs1
-rw-r--r--src/librustc_back/target/mod.rs27
-rw-r--r--src/librustc_back/target/windows_msvc_base.rs66
-rw-r--r--src/librustc_back/target/x86_64_pc_windows_msvc.rs33
-rw-r--r--src/librustc_driver/lib.rs2
-rw-r--r--src/librustc_llvm/lib.rs21
-rw-r--r--src/librustc_trans/back/link.rs266
-rw-r--r--src/librustc_trans/back/linker.rs253
-rw-r--r--src/librustc_trans/lib.rs1
-rw-r--r--src/librustc_trans/trans/base.rs23
-rw-r--r--src/librustc_trans/trans/context.rs55
-rw-r--r--src/librustc_trans/trans/debuginfo/mod.rs2
-rw-r--r--src/librustc_trans/trans/declare.rs98
-rw-r--r--src/libstd/num/f32.rs27
-rw-r--r--src/libstd/num/f64.rs9
-rw-r--r--src/libstd/rand/os.rs1
-rw-r--r--src/libstd/rt/unwind.rs615
-rw-r--r--src/libstd/rt/unwind/gcc.rs342
-rw-r--r--src/libstd/rt/unwind/mod.rs319
-rw-r--r--src/libstd/rt/unwind/seh.rs30
-rw-r--r--src/rt/rust_builtin.c34
-rw-r--r--src/rt/rust_test_helpers.c7
-rw-r--r--src/rustllvm/RustWrapper.cpp9
47 files changed, 1832 insertions, 1066 deletions
diff --git a/Makefile.in b/Makefile.in
index 8fc66a764da..d3bb5a541a4 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 4e763f76139..4f3375b8668 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
 
@@ -817,7 +818,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
@@ -1097,6 +1098,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
@@ -1138,6 +1198,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
@@ -1301,7 +1362,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)
@@ -1325,7 +1418,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
@@ -1509,11 +1602,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.bak >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 7767129a5e2..8cd09fa9043 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 d7a1405c3d0..9e0245e093d 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 6637423e495..88d7700db82 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 fdd38ba75fe..a66f70f6305 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 4118ea26c07..10aaf137e8b 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 00000000000..bd1088b7cd1
--- /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 5b90d41ceec..c04ef89ebc5 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 546b16c1b85..62dc1019066 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 75c6219c5f0..9aab51daba9 100644
--- a/mk/dist.mk
+++ b/mk/dist.mk
@@ -144,10 +144,12 @@ 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 \
 		--rel-manifest-dir=rustlib \
@@ -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 1861dd313ce..d5b608e88da 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 b3c02841340..24d9ed2c68e 100644
--- a/mk/main.mk
+++ b/mk/main.mk
@@ -87,10 +87,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),)
@@ -298,6 +294,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)
@@ -305,9 +302,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))
@@ -402,8 +403,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 9545a1fb52d..26a6db1c5bd 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 1ae6a61c95a..573b7ac79fd 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 bd6578d3b72..777a2a0fd3b 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 363022e8781..50d99370142 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 319f44fd35b..c8efb8e86d6 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 f391d8555fc..44c661c4e20 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 00000000000..d2f8ee469a4
--- /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 <LICENSE-APACHE or
+# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+# option. This file may not be copied, modified, or distributed
+# except according to those terms.
+
+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 fe9feb3538d..7a925fa3f33 100644
--- a/src/etc/mklldeps.py
+++ b/src/etc/mklldeps.py
@@ -80,10 +80,13 @@ if enable_static == '1':
     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 6e6d16f4e0f..3d19e95211e 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 9a95120ee68..f96094d3d09 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,16 +714,18 @@ impl ArchiveMetadata {
 }
 
 // Just a small wrapper to time how long reading metadata takes.
-fn get_metadata_section(is_osx: bool, filename: &Path) -> Result<MetadataBlob, String> {
+fn get_metadata_section(target: &Target, filename: &Path)
+                        -> Result<MetadataBlob, String> {
     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 {:?} => {}", filename.file_name().unwrap(), dur);
     return ret.unwrap();;
 }
 
-fn get_metadata_section_imp(is_osx: bool, filename: &Path) -> Result<MetadataBlob, String> {
+fn get_metadata_section_imp(target: &Target, filename: &Path)
+                            -> Result<MetadataBlob, String> {
     if !filename.exists() {
         return Err(format!("no such file: '{}'", filename.display()));
     }
@@ -768,7 +769,7 @@ fn get_metadata_section_imp(is_osx: bool, filename: &Path) -> Result<MetadataBlo
                                              name_len as usize).to_vec();
             let name = String::from_utf8(name).unwrap();
             debug!("get_metadata_section: name {}", name);
-            if read_meta_section_name(is_osx) == name {
+            if read_meta_section_name(target) == name {
                 let cbuf = llvm::LLVMGetSectionContents(si.llsi);
                 let csz = llvm::LLVMGetSectionSize(si.llsi) as usize;
                 let cvbuf: *const u8 = cbuf as *const u8;
@@ -798,26 +799,41 @@ fn get_metadata_section_imp(is_osx: bool, filename: &Path) -> Result<MetadataBlo
     }
 }
 
-pub fn meta_section_name(is_osx: bool) -> &'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 cad1522ee13..39d1f5ba205 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<PathBuf>,
     pub slib_prefix: String,
     pub slib_suffix: String,
-    pub maybe_ar_prog: Option<String>
+    pub ar_prog: String
 }
 
 pub struct Archive<'a> {
-    handler: &'a ErrorHandler,
-    dst: PathBuf,
-    lib_search_paths: Vec<PathBuf>,
-    slib_prefix: String,
-    slib_suffix: String,
-    maybe_ar_prog: Option<String>
+    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<String>,
-          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<String> {
-        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();
 
             // Archives on unix systems typically do not have slashes in
             // filenames as the `ar` utility generally only uses the last
diff --git a/src/librustc_back/target/linux_base.rs b/src/librustc_back/target/linux_base.rs
index 823a4a81fa4..3ae70ca854b 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 811d59d6748..22d966014da 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<String>,
@@ -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 00000000000..30d74c80735
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use target::TargetOptions;
+use std::default::Default;
+
+pub fn opts() -> TargetOptions {
+    TargetOptions {
+        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 00000000000..f7c3ca4b3f6
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use target::Target;
+
+pub fn target() -> Target {
+    let mut base = super::windows_msvc_base::opts();
+    base.cpu = "x86-64".to_string();
+
+    Target {
+        // 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 5d3af83c898..2c0e1a05c3d 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 4ba4399ae2d..a3b9a0e8467 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 38ad909dd01..844a0a69867 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<Linker>
+        } else {
+            Box::new(GnuLinker { cmd: &mut cmd, sess: &sess }) as Box<Linker>
+        };
+        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 00000000000..1eacec46c87
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use 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 e28681f1d6a..a1220ab6ba8 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 06841501124..e44aae76c19 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
@@ -1938,11 +1942,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);
             }
@@ -2101,9 +2111,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);
+        }
     }
 }
 
@@ -2170,7 +2186,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 {
@@ -2526,7 +2542,7 @@ pub fn write_metadata(cx: &SharedCrateContext, krate: &ast::Crate) -> Vec<u8> {
     };
     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())
     }
@@ -2587,6 +2603,7 @@ fn internalize_symbols(cx: &SharedCrateContext, reachable: &HashSet<String>) {
                 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 41ef566f2fd..51db0adf5b7 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<FnvHashSet<String>>,
     available_drop_glues: RefCell<FnvHashMap<DropGlueKind<'tcx>, 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 e4312b669ad..4e5407016ba 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 7849235fdbe..e0ab5dec98d 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<ValueRef> {
     if get_defined_value(ccx, name).is_some() {
         None
@@ -169,10 +173,10 @@ pub fn define_global(ccx: &CrateContext, name: &str, ty: Type) -> Option<ValueRe
 ///
 /// For rust functions use `define_rust_fn` instead.
 ///
-/// 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_fn(ccx: &CrateContext, name: &str, callconv: llvm::CallConv, fn_type: Type,
                  output: ty::FnOutput) -> Option<ValueRef> {
     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<ValueRef> {
     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<ValueRef> {
     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<ValueRef> {
     if get_defined_value(ccx, name).is_some() {
@@ -250,8 +254,8 @@ fn get_defined_value(ccx: &CrateContext, name: &str) -> Option<ValueRef> {
             (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 1ee3aab2727..e31d97b3240 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 398afcb553c..e87855ffd4e 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 3c36f0f1d49..885adf19ca1 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 b24099505ed..00000000000
--- 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 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! Implementation of Rust stack unwinding
-//!
-//! For background on exception handling and stack unwinding please see
-//! "Exception Handling in LLVM" (llvm.org/docs/ExceptionHandling.html) and
-//! documents linked from it.
-//! These are also good reads:
-//!     http://theofilos.cs.columbia.edu/blog/2013/09/22/base_abi/
-//!     http://monoinfinito.wordpress.com/series/exception-handling-in-c/
-//!     http://www.airs.com/blog/index.php?s=exception+frames
-//!
-//! ## A brief summary
-//!
-//! Exception handling happens in two phases: a search phase and a cleanup phase.
-//!
-//! In both phases the unwinder walks stack frames from top to bottom using
-//! information from the stack frame unwind sections of the current process's
-//! modules ("module" here refers to an OS module, i.e. an executable or a
-//! dynamic library).
-//!
-//! For each stack frame, it invokes the associated "personality routine", whose
-//! address is also stored in the unwind info section.
-//!
-//! In the search phase, the job of a personality routine is to examine exception
-//! object being thrown, and to decide whether it should be caught at that stack
-//! frame.  Once the handler frame has been identified, cleanup phase begins.
-//!
-//! In the cleanup phase, personality routines invoke cleanup code associated
-//! with their stack frames (i.e. destructors).  Once stack has been unwound down
-//! to the handler frame level, unwinding stops and the last personality routine
-//! transfers control to its catch block.
-//!
-//! ## Frame unwind info registration
-//!
-//! Each module has its own frame unwind info section (usually ".eh_frame"), and
-//! unwinder needs to know about all of them in order for unwinding to be able to
-//! cross module boundaries.
-//!
-//! On some platforms, like Linux, this is achieved by dynamically enumerating
-//! currently loaded modules via the dl_iterate_phdr() API and finding all
-//! .eh_frame sections.
-//!
-//! Others, like Windows, require modules to actively register their unwind info
-//! sections by calling __register_frame_info() API at startup.  In the latter
-//! case it is essential that there is only one copy of the unwinder runtime in
-//! the process.  This is usually achieved by linking to the dynamic version of
-//! the unwind runtime.
-//!
-//! Currently Rust uses unwind runtime provided by libgcc.
-
-use prelude::v1::*;
-
-use any::Any;
-use 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<Box<Any + Send + 'static>>,
-}
-
-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<bool> = Cell::new(false) }
-
-/// Invoke a closure, capturing the cause of panic if one occurs.
-///
-/// This function will return `Ok(())` if the closure did not panic, and will
-/// return `Err(cause)` if the closure panics. The `cause` returned is the
-/// object with which panic was originally invoked.
-///
-/// This function also is unsafe for a variety of reasons:
-///
-/// * This is not safe to call in a nested fashion. The unwinding
-///   interface for Rust is designed to have at most one try/catch block per
-///   thread, not multiple. No runtime checking is currently performed to uphold
-///   this invariant, so this function is not safe. A nested try/catch block
-///   may result in corruption of the outer try/catch block's state, especially
-///   if this is used within a thread itself.
-///
-/// * It is not sound to trigger unwinding while already unwinding. Rust threads
-///   have runtime checks in place to ensure this invariant, but it is not
-///   guaranteed that a rust thread is in place when invoking this function.
-///   Unwinding twice can lead to resource leaks where some destructors are not
-///   run.
-pub unsafe fn try<F: FnOnce()>(f: F) -> Result<(), Box<Any + Send>> {
-    let mut f = Some(f);
-
-    let prev = PANICKING.with(|s| s.get());
-    PANICKING.with(|s| s.set(false));
-    let ep = rust_try(try_fn::<F>, &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<F: FnOnce()>(opt_closure: *mut c_void) {
-        let opt_closure = opt_closure as *mut Option<F>;
-        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<Any + Send + 'static>) -> ! {
-    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<Exception> = 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<M: Any + Send>(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<Any + Send>,
-                      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 00000000000..39b32a3f08e
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use prelude::v1::*;
+
+use any::Any;
+use boxed;
+use libc::c_void;
+use rt::libunwind as uw;
+
+struct Exception {
+    uwe: uw::_Unwind_Exception,
+    cause: Option<Box<Any + Send + 'static>>,
+}
+
+pub unsafe fn panic(data: Box<Any + Send + 'static>) -> ! {
+    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<Exception> = Box::from_raw(exception as *mut Exception);
+        }
+    }
+}
+
+pub unsafe fn cleanup(ptr: *mut c_void) -> Box<Any + Send + 'static> {
+    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 00000000000..576035ffe9a
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Implementation of Rust stack unwinding
+//!
+//! For background on exception handling and stack unwinding please see
+//! "Exception Handling in LLVM" (llvm.org/docs/ExceptionHandling.html) and
+//! documents linked from it.
+//! These are also good reads:
+//!     http://theofilos.cs.columbia.edu/blog/2013/09/22/base_abi/
+//!     http://monoinfinito.wordpress.com/series/exception-handling-in-c/
+//!     http://www.airs.com/blog/index.php?s=exception+frames
+//!
+//! ## A brief summary
+//!
+//! Exception handling happens in two phases: a search phase and a cleanup phase.
+//!
+//! In both phases the unwinder walks stack frames from top to bottom using
+//! information from the stack frame unwind sections of the current process's
+//! modules ("module" here refers to an OS module, i.e. an executable or a
+//! dynamic library).
+//!
+//! For each stack frame, it invokes the associated "personality routine", whose
+//! address is also stored in the unwind info section.
+//!
+//! In the search phase, the job of a personality routine is to examine exception
+//! object being thrown, and to decide whether it should be caught at that stack
+//! frame.  Once the handler frame has been identified, cleanup phase begins.
+//!
+//! In the cleanup phase, personality routines invoke cleanup code associated
+//! with their stack frames (i.e. destructors).  Once stack has been unwound down
+//! to the handler frame level, unwinding stops and the last personality routine
+//! transfers control to its catch block.
+//!
+//! ## Frame unwind info registration
+//!
+//! Each module has its own frame unwind info section (usually ".eh_frame"), and
+//! unwinder needs to know about all of them in order for unwinding to be able to
+//! cross module boundaries.
+//!
+//! On some platforms, like Linux, this is achieved by dynamically enumerating
+//! currently loaded modules via the dl_iterate_phdr() API and finding all
+//! .eh_frame sections.
+//!
+//! Others, like Windows, require modules to actively register their unwind info
+//! sections by calling __register_frame_info() API at startup.  In the latter
+//! case it is essential that there is only one copy of the unwinder runtime in
+//! the process.  This is usually achieved by linking to the dynamic version of
+//! the unwind runtime.
+//!
+//! Currently Rust uses unwind runtime provided by libgcc.
+
+#![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<bool> = 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: FnOnce()>(f: F) -> Result<(), Box<Any + Send>> {
+    let mut f = Some(f);
+    return inner_try(try_fn::<F>, &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<Any + Send>> {
+        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<F: FnOnce()>(opt_closure: *mut c_void) {
+        let opt_closure = opt_closure as *mut Option<F>;
+        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<Any + Send + 'static>) -> ! {
+    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<M: Any + Send>(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<Any + Send>,
+                      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 00000000000..a72c1debe14
--- /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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use prelude::v1::*;
+
+use any::Any;
+use intrinsics;
+use libc::c_void;
+
+pub unsafe fn panic(_data: Box<Any + Send + 'static>) -> ! {
+    intrinsics::abort();
+}
+
+pub unsafe fn cleanup(_ptr: *mut c_void) -> Box<Any + Send + 'static> {
+    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 362439c1469..92371e90aba 100644
--- a/src/rt/rust_builtin.c
+++ b/src/rt/rust_builtin.c
@@ -14,7 +14,8 @@
 #include <assert.h>
 #include <stdlib.h>
 
-#if !defined(__WIN32__)
+
+#if !defined(_WIN32)
 #include <dirent.h>
 #include <pthread.h>
 #include <signal.h>
@@ -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 c755cf67caa..8824cef2a81 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 375c5fc746c..66db7326d21 100644
--- a/src/rustllvm/RustWrapper.cpp
+++ b/src/rustllvm/RustWrapper.cpp
@@ -233,7 +233,9 @@ DIT unwrapDI(LLVMMetadataRef ref) {
     return DIT(ref ? unwrap<MDNode>(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<GlobalValue>(Value);
-    V->setDLLStorageClass(GlobalValue::DLLExportStorageClass);
+    V->setDLLStorageClass(Class);
 }
 
 // Note that the two following functions look quite similar to the