about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2015-06-27 05:06:22 +0000
committerbors <bors@rust-lang.org>2015-06-27 05:06:22 +0000
commitd3c03d098747be06286f2aebc1e76f8c08352fdf (patch)
tree2059612a95a7a6032f8321b7fab55b1c5b6766c6
parent773052a608736b873906f9bf1c3fd222d0b54764 (diff)
parent759a7f1f66490191a6f809c9709151d6d27cea87 (diff)
downloadrust-d3c03d098747be06286f2aebc1e76f8c08352fdf.tar.gz
rust-d3c03d098747be06286f2aebc1e76f8c08352fdf.zip
Auto merge of #26569 - alexcrichton:msvc-llvm-update, r=brson
Now that LLVM has been updated, the only remaining roadblock to implementing
unwinding for MSVC is to fill out the runtime support in `std::rt::unwind::seh`.
This commit does precisely that, fixing up some other bits and pieces along the
way:

* The `seh` unwinding module now uses `RaiseException` to initiate a panic.
* The `rust_try.ll` file was rewritten for MSVC (as it's quite different) and is
  located at `rust_try_msvc_64.ll`, only included on MSVC builds for now.
* The personality function for all landing pads generated by LLVM is hard-wired
  to `__C_specific_handler` instead of the standard `rust_eh_personality` lang
  item. This is required to get LLVM to emit SEH unwinding information instead
  of DWARF unwinding information. This also means that on MSVC the
  `rust_eh_personality` function is entirely unused (but is defined as it's a
  lang item).

More details about how panicking works on SEH can be found in the
`rust_try_msvc_64.ll` or `seh.rs` files, but I'm always open to adding more
comments!

A key aspect of this PR is missing, however, which is that **unwinding is still
turned off by default for MSVC**. There is a [bug in llvm][llvm-bug] which
causes optimizations to inline enough landing pads that LLVM chokes. If the
compiler is optimized at `-O1` (where inlining isn't enabled) then it can
bootstrap with unwinding enabled, but when optimized at `-O2` (inlining is
enabled) then it hits a fatal LLVM error.

[llvm-bug]: https://llvm.org/bugs/show_bug.cgi?id=23884
-rw-r--r--mk/cfg/x86_64-pc-windows-msvc.mk58
-rw-r--r--mk/platform.mk69
-rw-r--r--mk/rt.mk9
-rw-r--r--src/librustc_trans/back/link.rs6
-rw-r--r--src/librustc_trans/back/linker.rs18
-rw-r--r--src/librustc_trans/trans/cleanup.rs24
-rw-r--r--src/libstd/rt/unwind/seh.rs131
-rw-r--r--src/rt/rust_try_msvc_64.ll78
-rw-r--r--src/test/auxiliary/lang-item-public.rs35
-rw-r--r--src/test/run-pass/lang-item-public.rs30
10 files changed, 326 insertions, 132 deletions
diff --git a/mk/cfg/x86_64-pc-windows-msvc.mk b/mk/cfg/x86_64-pc-windows-msvc.mk
index 69a26c03fb6..edeffcdd09b 100644
--- a/mk/cfg/x86_64-pc-windows-msvc.mk
+++ b/mk/cfg/x86_64-pc-windows-msvc.mk
@@ -23,64 +23,6 @@ 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)
-
 # All windows nightiles are currently a GNU triple, so this MSVC triple is not
 # bootstrapping from itself. This is relevant during stage0, and other parts of
 # the build system take this into account.
diff --git a/mk/platform.mk b/mk/platform.mk
index 8a5e58c46f6..abc9cc038d0 100644
--- a/mk/platform.mk
+++ b/mk/platform.mk
@@ -238,3 +238,72 @@ endef
 
 $(foreach target,$(CFG_TARGET), \
   $(eval $(call CFG_MAKE_TOOLCHAIN,$(target))))
+
+# 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.
+define ADD_LLVM_AR_TO_MSVC_DEPS
+ifeq ($$(findstring msvc,$(1)),msvc)
+NATIVE_TOOL_DEPS_core_T_$(1) += llvm-ar.exe
+INSTALLED_BINS_$(1) += llvm-ar.exe
+endif
+endef
+
+$(foreach target,$(CFG_TARGET), \
+  $(eval $(call ADD_LLVM_AR_TO_MSVC_DEPS,$(target))))
+
+# 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
+define ADD_RUSTC_LLVM_DEF_TO_MSVC
+ifeq ($$(findstring msvc,$(1)),msvc)
+RUSTFLAGS_rustc_llvm_T_$(1) += -C link-args="-DEF:$(1)/rt/rustc_llvm.def"
+CUSTOM_DEPS_rustc_llvm_T_$(1) += $(1)/rt/rustc_llvm.def
+
+$(1)/rt/rustc_llvm.def: $$(S)src/etc/mklldef.py $$(S)src/librustc_llvm/lib.rs
+	$$(CFG_PYTHON) $$^ $$@ rustc_llvm-$$(CFG_FILENAME_EXTRA)
+endif
+endef
+
+$(foreach target,$(CFG_TARGET), \
+  $(eval $(call ADD_RUSTC_LLVM_DEF_TO_MSVC,$(target))))
diff --git a/mk/rt.mk b/mk/rt.mk
index 777a2a0fd3b..6513cf10772 100644
--- a/mk/rt.mk
+++ b/mk/rt.mk
@@ -53,9 +53,12 @@ NATIVE_DEPS_hoedown_$(1) := hoedown/src/autolink.c \
 NATIVE_DEPS_miniz_$(1) = miniz.c
 NATIVE_DEPS_rust_builtin_$(1) := rust_builtin.c \
 			rust_android_dummy.c
-NATIVE_DEPS_rustrt_native_$(1) := \
-			rust_try.ll \
-			arch/$$(HOST_$(1))/record_sp.S
+NATIVE_DEPS_rustrt_native_$(1) := arch/$$(HOST_$(1))/record_sp.S
+ifeq ($$(findstring msvc,$(1)),msvc)
+NATIVE_DEPS_rustrt_native_$(1) += rust_try_msvc_64.ll
+else
+NATIVE_DEPS_rustrt_native_$(1) += rust_try.ll
+endif
 NATIVE_DEPS_rust_test_helpers_$(1) := rust_test_helpers.c
 NATIVE_DEPS_morestack_$(1) := arch/$$(HOST_$(1))/morestack.S
 
diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs
index 6b8b59de3c2..cf5feabcc57 100644
--- a/src/librustc_trans/back/link.rs
+++ b/src/librustc_trans/back/link.rs
@@ -1214,11 +1214,13 @@ fn add_upstream_rust_crates(cmd: &mut Linker, sess: &Session,
 
         // Just need to tell the linker about where the library lives and
         // what its name is
-        if let Some(dir) = cratepath.parent() {
+        let parent = cratepath.parent();
+        if let Some(dir) = parent {
             cmd.include_path(&fix_windows_verbatim_for_gcc(dir));
         }
         let filestem = cratepath.file_stem().unwrap().to_str().unwrap();
-        cmd.link_dylib(&unlib(&sess.target, filestem));
+        cmd.link_rust_dylib(&unlib(&sess.target, filestem),
+                            parent.unwrap_or(Path::new("")));
     }
 }
 
diff --git a/src/librustc_trans/back/linker.rs b/src/librustc_trans/back/linker.rs
index 1eacec46c87..7253334d699 100644
--- a/src/librustc_trans/back/linker.rs
+++ b/src/librustc_trans/back/linker.rs
@@ -11,6 +11,7 @@
 use std::ffi::OsString;
 use std::path::{Path, PathBuf};
 use std::process::Command;
+use std::fs;
 
 use rustc_back::archive;
 use session::Session;
@@ -25,6 +26,7 @@ use session::config;
 /// MSVC linker (e.g. `link.exe`) is being used.
 pub trait Linker {
     fn link_dylib(&mut self, lib: &str);
+    fn link_rust_dylib(&mut self, lib: &str, path: &Path);
     fn link_framework(&mut self, framework: &str);
     fn link_staticlib(&mut self, lib: &str);
     fn link_rlib(&mut self, lib: &Path);
@@ -67,6 +69,10 @@ impl<'a> Linker for GnuLinker<'a> {
     fn position_independent_executable(&mut self) { self.cmd.arg("-pie"); }
     fn args(&mut self, args: &[String]) { self.cmd.args(args); }
 
+    fn link_rust_dylib(&mut self, lib: &str, _path: &Path) {
+        self.cmd.arg("-l").arg(lib);
+    }
+
     fn link_framework(&mut self, framework: &str) {
         self.cmd.arg("-framework").arg(framework);
     }
@@ -189,6 +195,18 @@ impl<'a> Linker for MsvcLinker<'a> {
     fn link_dylib(&mut self, lib: &str) {
         self.cmd.arg(&format!("{}.lib", lib));
     }
+
+    fn link_rust_dylib(&mut self, lib: &str, path: &Path) {
+        // When producing a dll, the MSVC linker may not actually emit a
+        // `foo.lib` file if the dll doesn't actually export any symbols, so we
+        // check to see if the file is there and just omit linking to it if it's
+        // not present.
+        let name = format!("{}.lib", lib);
+        if fs::metadata(&path.join(&name)).is_ok() {
+            self.cmd.arg(name);
+        }
+    }
+
     fn link_staticlib(&mut self, lib: &str) {
         self.cmd.arg(&format!("{}.lib", lib));
     }
diff --git a/src/librustc_trans/trans/cleanup.rs b/src/librustc_trans/trans/cleanup.rs
index 6355a713a2c..b7e761fa4b9 100644
--- a/src/librustc_trans/trans/cleanup.rs
+++ b/src/librustc_trans/trans/cleanup.rs
@@ -856,18 +856,36 @@ impl<'blk, 'tcx> CleanupHelperMethods<'blk, 'tcx> for FunctionContext<'blk, 'tcx
         // this function, so we just codegen a generic reference to it. We don't
         // specify any of the types for the function, we just make it a symbol
         // that LLVM can later use.
+        //
+        // Note that MSVC is a little special here in that we don't use the
+        // `eh_personality` lang item at all. Currently LLVM has support for
+        // both Dwarf and SEH unwind mechanisms for MSVC targets and uses the
+        // *name of the personality function* to decide what kind of unwind side
+        // tables/landing pads to emit. It looks like Dwarf is used by default,
+        // injecting a dependency on the `_Unwind_Resume` symbol for resuming
+        // an "exception", but for MSVC we want to force SEH. This means that we
+        // can't actually have the personality function be our standard
+        // `rust_eh_personality` function, but rather we wired it up to the
+        // CRT's custom `__C_specific_handler` personality funciton, which
+        // forces LLVM to consider landing pads as "landing pads for SEH".
+        let target = &self.ccx.sess().target.target;
         let llpersonality = match pad_bcx.tcx().lang_items.eh_personality() {
-            Some(def_id) => {
+            Some(def_id) if !target.options.is_like_msvc => {
                 callee::trans_fn_ref(pad_bcx.ccx(), def_id, ExprId(0),
                                      pad_bcx.fcx.param_substs).val
             }
-            None => {
+            _ => {
                 let mut personality = self.ccx.eh_personality().borrow_mut();
                 match *personality {
                     Some(llpersonality) => llpersonality,
                     None => {
+                        let name = if target.options.is_like_msvc {
+                            "__C_specific_handler"
+                        } else {
+                            "rust_eh_personality"
+                        };
                         let fty = Type::variadic_func(&[], &Type::i32(self.ccx));
-                        let f = declare::declare_cfn(self.ccx, "rust_eh_personality", fty,
+                        let f = declare::declare_cfn(self.ccx, name, fty,
                                                      self.ccx.tcx().types.i32);
                         *personality = Some(f);
                         f
diff --git a/src/libstd/rt/unwind/seh.rs b/src/libstd/rt/unwind/seh.rs
index a72c1debe14..632ab4f8e25 100644
--- a/src/libstd/rt/unwind/seh.rs
+++ b/src/libstd/rt/unwind/seh.rs
@@ -8,23 +8,136 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+//! Win64 SEH (see http://msdn.microsoft.com/en-us/library/1eyas8tf.aspx)
+//!
+//! On Windows (currently only on MSVC), the default exception handling
+//! mechanism is Structured Exception Handling (SEH). This is quite different
+//! than Dwarf-based exception handling (e.g. what other unix platforms use) in
+//! terms of compiler internals, so LLVM is required to have a good deal of
+//! extra support for SEH. Currently this support is somewhat lacking, so what's
+//! here is the bare bones of SEH support.
+//!
+//! In a nutshell, what happens here is:
+//!
+//! 1. The `panic` function calls the standard Windows function `RaiseException`
+//!    with a Rust-specific code, triggering the unwinding process.
+//! 2. All landing pads generated by the compiler (just "cleanup" landing pads)
+//!    use the personality function `__C_specific_handler`, a function in the
+//!    CRT, and the unwinding code in Windows will use this personality function
+//!    to execute all cleanup code on the stack.
+//! 3. Eventually the "catch" code in `rust_try` (located in
+//!    src/rt/rust_try_msvc_64.ll) is executed, which will ensure that the
+//!    exception being caught is indeed a Rust exception, returning control back
+//!    into Rust.
+//!
+//! Some specific differences from the gcc-based exception handling are:
+//!
+//! * Rust has no custom personality function, it is instead *always*
+//!   __C_specific_handler, so the filtering is done in a C++-like manner
+//!   instead of in the personality function itself. Note that the specific
+//!   syntax for this (found in the rust_try_msvc_64.ll) is taken from an LLVM
+//!   test case for SEH.
+//! * We've got some data to transmit across the unwinding boundary,
+//!   specifically a `Box<Any + Send + 'static>`. In Dwarf-based unwinding this
+//!   data is part of the payload of the exception, but I have not currently
+//!   figured out how to do this with LLVM's bindings. Judging by some comments
+//!   in the LLVM test cases this may not even be possible currently with LLVM,
+//!   so this is just abandoned entirely. Instead the data is stored in a
+//!   thread-local in `panic` and retrieved during `cleanup`.
+//!
+//! So given all that, the bindings here are pretty small,
+
+#![allow(bad_style)]
+
 use prelude::v1::*;
 
 use any::Any;
-use intrinsics;
-use libc::c_void;
+use libc::{c_ulong, DWORD, c_void};
+use sys_common::thread_local::StaticKey;
+
+//                        0x R U S T
+const RUST_PANIC: DWORD = 0x52555354;
+static PANIC_DATA: StaticKey = StaticKey::new(None);
+
+// This function is provided by kernel32.dll
+extern "system" {
+    fn RaiseException(dwExceptionCode: DWORD,
+                      dwExceptionFlags: DWORD,
+                      nNumberOfArguments: DWORD,
+                      lpArguments: *const c_ulong);
+}
+
+#[repr(C)]
+pub struct EXCEPTION_POINTERS {
+    ExceptionRecord: *mut EXCEPTION_RECORD,
+    ContextRecord: *mut CONTEXT,
+}
+
+enum CONTEXT {}
+
+#[repr(C)]
+struct EXCEPTION_RECORD {
+    ExceptionCode: DWORD,
+    ExceptionFlags: DWORD,
+    ExceptionRecord: *mut _EXCEPTION_RECORD,
+    ExceptionAddress: *mut c_void,
+    NumberParameters: DWORD,
+    ExceptionInformation: [*mut c_ulong; EXCEPTION_MAXIMUM_PARAMETERS],
+}
 
-pub unsafe fn panic(_data: Box<Any + Send + 'static>) -> ! {
-    intrinsics::abort();
+enum _EXCEPTION_RECORD {}
+
+const EXCEPTION_MAXIMUM_PARAMETERS: usize = 15;
+
+pub unsafe fn panic(data: Box<Any + Send + 'static>) -> ! {
+    // See module docs above for an explanation of why `data` is stored in a
+    // thread local instead of being passed as an argument to the
+    // `RaiseException` function (which can in theory carry along arbitrary
+    // data).
+    let exception = Box::new(data);
+    rtassert!(PANIC_DATA.get().is_null());
+    PANIC_DATA.set(Box::into_raw(exception) as *mut u8);
+
+    RaiseException(RUST_PANIC, 0, 0, 0 as *const _);
+    rtabort!("could not unwind stack");
 }
 
-pub unsafe fn cleanup(_ptr: *mut c_void) -> Box<Any + Send + 'static> {
-    intrinsics::abort();
+pub unsafe fn cleanup(ptr: *mut c_void) -> Box<Any + Send + 'static> {
+    // The `ptr` here actually corresponds to the code of the exception, and our
+    // real data is stored in our thread local.
+    rtassert!(ptr as DWORD == RUST_PANIC);
+
+    let data = PANIC_DATA.get() as *mut Box<Any + Send + 'static>;
+    PANIC_DATA.set(0 as *mut u8);
+    rtassert!(!data.is_null());
+
+    *Box::from_raw(data)
 }
 
+// This is required by the compiler to exist (e.g. it's a lang item), but it's
+// never actually called by the compiler because __C_specific_handler is the
+// personality function that is always used. Hence this is just an aborting
+// stub.
 #[lang = "eh_personality"]
-#[no_mangle]
-pub extern fn rust_eh_personality() {}
+fn rust_eh_personality() {
+    unsafe { ::intrinsics::abort() }
+}
 
+// This is a function referenced from `rust_try_msvc_64.ll` which is used to
+// filter the exceptions being caught by that function.
+//
+// In theory local variables can be accessed through the `rbp` parameter of this
+// function, but a comment in an LLVM test case indicates that this is not
+// implemented in LLVM, so this is just an idempotent function which doesn't
+// ferry along any other information.
+//
+// This function just takes a look at the current EXCEPTION_RECORD being thrown
+// to ensure that it's code is RUST_PANIC, which was set by the call to
+// `RaiseException` above in the `panic` function.
 #[no_mangle]
-pub extern fn rust_eh_personality_catch() {}
+pub extern fn __rust_try_filter(eh_ptrs: *mut EXCEPTION_POINTERS,
+                                _rbp: *mut c_void) -> i32 {
+    unsafe {
+        ((*(*eh_ptrs).ExceptionRecord).ExceptionCode == RUST_PANIC) as i32
+    }
+}
diff --git a/src/rt/rust_try_msvc_64.ll b/src/rt/rust_try_msvc_64.ll
new file mode 100644
index 00000000000..bda136d8478
--- /dev/null
+++ b/src/rt/rust_try_msvc_64.ll
@@ -0,0 +1,78 @@
+; 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.
+
+; 64-bit MSVC's definition of the `rust_try` function. This function can't be
+; defined in Rust as it's a "try-catch" block that's not expressible in Rust's
+; syntax, so we're using LLVM to produce an object file with the associated
+; handler.
+;
+; To use the correct system implementation details, this file is separate from
+; the standard rust_try.ll as we need specifically use the __C_specific_handler
+; personality function or otherwise LLVM doesn't emit SEH handling tables.
+; There's also a few fiddly bits about SEH right now in LLVM that require us to
+; structure this a fairly particular way!
+;
+; See also: src/libstd/rt/unwind/seh.rs
+
+define i8* @rust_try(void (i8*)* %f, i8* %env) {
+    invoke void %f(i8* %env)
+        to label %normal
+        unwind label %catch
+
+normal:
+    ret i8* null
+
+; Here's where most of the magic happens, this is the only landing pad in rust
+; tagged with "catch" to indicate that we're catching an exception. The other
+; catch handlers in rust_try.ll just catch *all* exceptions, but that's because
+; most exceptions are already filtered out by their personality function.
+;
+; For MSVC we're just using a standard personality function that we can't
+; customize, so we need to do the exception filtering ourselves, and this is
+; currently performed by the `__rust_try_filter` function. This function,
+; specified in the landingpad instruction, will be invoked by Windows SEH
+; routines and will return whether the exception in question can be caught (aka
+; the Rust runtime is the one that threw the exception).
+;
+; To get this to compile (currently LLVM segfaults if it's not in this
+; particular structure), when the landingpad is executing we test to make sure
+; that the ID of the exception being thrown is indeed the one that we were
+; expecting. If it's not, we resume the exception, and otherwise we return the
+; pointer that we got
+;
+; Full disclosure: It's not clear to me what this `llvm.eh.typeid` stuff is
+; doing *other* then just allowing LLVM to compile this file without
+; segfaulting. I would expect the entire landing pad to just be:
+;
+;     %vals = landingpad ...
+;     %ehptr = extractvalue { i8*, i32 } %vals, 0
+;     ret i8* %ehptr
+;
+; but apparently LLVM chokes on this, so we do the more complicated thing to
+; placate it.
+catch:
+    %vals = landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__C_specific_handler to i8*)
+        catch i8* bitcast (i32 (i8*, i8*)* @__rust_try_filter to i8*)
+    %ehptr = extractvalue { i8*, i32 } %vals, 0
+    %sel = extractvalue { i8*, i32 } %vals, 1
+    %filter_sel = call i32 @llvm.eh.typeid.for(i8* bitcast (i32 (i8*, i8*)* @__rust_try_filter to i8*))
+    %is_filter = icmp eq i32 %sel, %filter_sel
+    br i1 %is_filter, label %catch-return, label %catch-resume
+
+catch-return:
+    ret i8* %ehptr
+
+catch-resume:
+    resume { i8*, i32 } %vals
+}
+
+declare i32 @__C_specific_handler(...)
+declare i32 @__rust_try_filter(i8*, i8*)
+declare i32 @llvm.eh.typeid.for(i8*) readnone nounwind
diff --git a/src/test/auxiliary/lang-item-public.rs b/src/test/auxiliary/lang-item-public.rs
index d195bd7e77b..4b60a370187 100644
--- a/src/test/auxiliary/lang-item-public.rs
+++ b/src/test/auxiliary/lang-item-public.rs
@@ -8,15 +8,12 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#![feature(no_std)]
+#![feature(no_std, core, libc)]
 #![no_std]
 #![feature(lang_items)]
 
-#[lang="sized"]
-pub trait Sized { }
-
-#[lang="panic"]
-fn panic(_: &(&'static str, &'static str, usize)) -> ! { loop {} }
+extern crate core;
+extern crate libc;
 
 #[lang = "stack_exhausted"]
 extern fn stack_exhausted() {}
@@ -24,26 +21,8 @@ extern fn stack_exhausted() {}
 #[lang = "eh_personality"]
 extern fn eh_personality() {}
 
-#[lang="copy"]
-pub trait Copy {
-    // Empty.
-}
-
-#[lang="rem"]
-pub trait Rem<RHS=Self> {
-    type Output = Self;
-    fn rem(self, rhs: RHS) -> Self::Output;
-}
-
-impl Rem for isize {
-    type Output = isize;
-
-    #[inline]
-    fn rem(self, other: isize) -> isize {
-        // if you use `self % other` here, as one would expect, you
-        // get back an error because of potential failure/overflow,
-        // which tries to invoke error fns that don't have the
-        // appropriate signatures anymore. So...just return 0.
-        0
-    }
+#[lang = "panic_fmt"]
+extern fn rust_begin_unwind(msg: core::fmt::Arguments, file: &'static str,
+                            line: u32) -> ! {
+    loop {}
 }
diff --git a/src/test/run-pass/lang-item-public.rs b/src/test/run-pass/lang-item-public.rs
index f5b9bd4fbaa..57a32ba599f 100644
--- a/src/test/run-pass/lang-item-public.rs
+++ b/src/test/run-pass/lang-item-public.rs
@@ -11,39 +11,11 @@
 // aux-build:lang-item-public.rs
 // ignore-android
 
-#![feature(lang_items, start, no_std)]
+#![feature(start, no_std)]
 #![no_std]
 
 extern crate lang_item_public as lang_lib;
 
-#[cfg(target_os = "linux")]
-#[link(name = "c")]
-extern {}
-
-#[cfg(target_os = "android")]
-#[link(name = "c")]
-extern {}
-
-#[cfg(target_os = "freebsd")]
-#[link(name = "execinfo")]
-extern {}
-
-#[cfg(target_os = "freebsd")]
-#[link(name = "c")]
-extern {}
-
-#[cfg(target_os = "dragonfly")]
-#[link(name = "c")]
-extern {}
-
-#[cfg(any(target_os = "bitrig", target_os = "openbsd"))]
-#[link(name = "c")]
-extern {}
-
-#[cfg(target_os = "macos")]
-#[link(name = "System")]
-extern {}
-
 #[start]
 fn main(_: isize, _: *const *const u8) -> isize {
     1_isize % 1_isize