From 1ec520a531b544079690f8178a7660421e8a713a Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 19 Jun 2015 14:51:29 -0700 Subject: [PATCH 1/4] mk: Move logic out of MSVC's 64-bit cfg makefile This logic applies to all MSVC targets, so instead refactor it into platform.mk so it can one day apply to 32-bit MSVC. --- mk/cfg/x86_64-pc-windows-msvc.mk | 58 --------------------------- mk/platform.mk | 69 ++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 58 deletions(-) diff --git a/mk/cfg/x86_64-pc-windows-msvc.mk b/mk/cfg/x86_64-pc-windows-msvc.mk index 69a26c03fb664..edeffcdd09b9b 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 8a5e58c46f676..abc9cc038d022 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)))) From 91d799eab0d7f6784fb4366182b5007cf055519d Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 19 Jun 2015 14:57:06 -0700 Subject: [PATCH 2/4] msvc: Implement runtime support for unwinding 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 --- mk/rt.mk | 9 +- src/librustc_trans/trans/cleanup.rs | 24 ++++- src/libstd/rt/unwind/seh.rs | 131 ++++++++++++++++++++++++++-- src/rt/rust_try_msvc_64.ll | 78 +++++++++++++++++ 4 files changed, 227 insertions(+), 15 deletions(-) create mode 100644 src/rt/rust_try_msvc_64.ll diff --git a/mk/rt.mk b/mk/rt.mk index 777a2a0fd3b4b..6513cf107726a 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/trans/cleanup.rs b/src/librustc_trans/trans/cleanup.rs index 6355a713a2ce6..b7e761fa4b991 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 a72c1debe14e0..632ab4f8e2537 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`. 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) -> ! { - intrinsics::abort(); +enum _EXCEPTION_RECORD {} + +const EXCEPTION_MAXIMUM_PARAMETERS: usize = 15; + +pub unsafe fn panic(data: Box) -> ! { + // 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 { - intrinsics::abort(); +pub unsafe fn cleanup(ptr: *mut c_void) -> Box { + // 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; + 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 0000000000000..bda136d84780b --- /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 or the MIT license +; , 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 From 9e3cb6447596049464292c0afa8c4b9a0cb8c806 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 25 Jun 2015 00:33:52 -0700 Subject: [PATCH 3/4] rustc_trans: Handle empty dlls on MSVC If a dylib doesn't actually export any symbols then link.exe won't emit a `foo.lib` file to link against (as one isn't necessary). Detect this case in the backend by omitting the `foo.lib` argument to the linker if it doesn't actually exist. --- src/librustc_trans/back/link.rs | 6 ++++-- src/librustc_trans/back/linker.rs | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index 6b8b59de3c253..cf5feabcc57e2 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 1eacec46c87bb..7253334d69989 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)); } From 759a7f1f66490191a6f809c9709151d6d27cea87 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 25 Jun 2015 09:13:30 -0700 Subject: [PATCH 4/4] test: Use liblibc in lang-item-public Makes this test case more robust by using standard libraries to ensure the binary can be built. --- src/test/auxiliary/lang-item-public.rs | 35 ++++++-------------------- src/test/run-pass/lang-item-public.rs | 30 +--------------------- 2 files changed, 8 insertions(+), 57 deletions(-) diff --git a/src/test/auxiliary/lang-item-public.rs b/src/test/auxiliary/lang-item-public.rs index d195bd7e77bd8..4b60a370187af 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 { - 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 f5b9bd4fbaa69..57a32ba599f93 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