diff --git a/mk/platform.mk b/mk/platform.mk index 2802e5ee4a219..082c0d526a0f0 100644 --- a/mk/platform.mk +++ b/mk/platform.mk @@ -277,10 +277,15 @@ $(foreach target,$(CFG_TARGET), \ # Fun times! # # [1]: https://msdn.microsoft.com/en-us/library/28d6s79h.aspx +# +# FIXME(stage0): remove this macro and the usage below (and the commments above) +# when a new snapshot is available. Also remove the +# RUSTFLAGS$(1)_.._T_ variable in mk/target.mk along with +# CUSTOM_DEPS (as they were only added for this) 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 +RUSTFLAGS0_rustc_llvm_T_$(1) += -C link-args="-DEF:$(1)/rt/rustc_llvm.def" +CUSTOM_DEPS0_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) diff --git a/mk/target.mk b/mk/target.mk index 1af4a2f4694fe..cd22a77bd22e1 100644 --- a/mk/target.mk +++ b/mk/target.mk @@ -40,7 +40,7 @@ CRATE_FULLDEPS_$(1)_T_$(2)_H_$(3)_$(4) := \ $$(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)) + $$(CUSTOM_DEPS$(1)_$(4)_T_$(2)) endef $(foreach host,$(CFG_HOST), \ @@ -92,7 +92,7 @@ $$(TLIB$(1)_T_$(2)_H_$(3))/stamp.$(4): \ $$(LLVM_LIBDIR_RUSTFLAGS_$(2)) \ $$(LLVM_STDCPP_RUSTFLAGS_$(2)) \ $$(RUSTFLAGS_$(4)) \ - $$(RUSTFLAGS_$(4)_T_$(2)) \ + $$(RUSTFLAGS$(1)_$(4)_T_$(2)) \ --out-dir $$(@D) \ -C extra-filename=-$$(CFG_FILENAME_EXTRA) \ $$< diff --git a/src/compiletest/procsrv.rs b/src/compiletest/procsrv.rs index b30efaa6c29d8..878dc00e7b462 100644 --- a/src/compiletest/procsrv.rs +++ b/src/compiletest/procsrv.rs @@ -26,8 +26,7 @@ fn add_target_env(cmd: &mut Command, lib_path: &str, aux_path: Option<&str>) { // Add the new dylib search path var let var = DynamicLibrary::envvar(); let newpath = DynamicLibrary::create_path(&path); - let newpath = newpath.to_str().unwrap().to_string(); - cmd.env(var, &newpath); + cmd.env(var, newpath); } pub struct Result {pub status: ExitStatus, pub out: String, pub err: String} diff --git a/src/doc/reference.md b/src/doc/reference.md index e905ed917d7fd..4d5564d9fafb0 100644 --- a/src/doc/reference.md +++ b/src/doc/reference.md @@ -1924,10 +1924,16 @@ On an `extern` block, the following attributes are interpreted: name and type. This is feature gated and the exact behavior is implementation-defined (due to variety of linker invocation syntax). - `link` - indicate that a native library should be linked to for the - declarations in this block to be linked correctly. `link` supports an optional `kind` - key with three possible values: `dylib`, `static`, and `framework`. See [external blocks](#external-blocks) for more about external blocks. Two + declarations in this block to be linked correctly. `link` supports an optional + `kind` key with three possible values: `dylib`, `static`, and `framework`. See + [external blocks](#external-blocks) for more about external blocks. Two examples: `#[link(name = "readline")]` and `#[link(name = "CoreFoundation", kind = "framework")]`. +- `linked_from` - indicates what native library this block of FFI items is + coming from. This attribute is of the form `#[linked_from = "foo"]` where + `foo` is the name of a library in either `#[link]` or a `-l` flag. This + attribute is currently required to export symbols from a Rust dynamic library + on Windows, and it is feature gated behind the `linked_from` feature. On declarations inside an `extern` block, the following attributes are interpreted: diff --git a/src/librustc/metadata/common.rs b/src/librustc/metadata/common.rs index 5f785fefa1241..abcff6e78e2c6 100644 --- a/src/librustc/metadata/common.rs +++ b/src/librustc/metadata/common.rs @@ -205,8 +205,8 @@ pub const tag_plugin_registrar_fn: usize = 0x10b; // top-level only pub const tag_method_argument_names: usize = 0x85; pub const tag_method_argument_name: usize = 0x86; -pub const tag_reachable_extern_fns: usize = 0x10c; // top-level only -pub const tag_reachable_extern_fn_id: usize = 0x87; +pub const tag_reachable_ids: usize = 0x10c; // top-level only +pub const tag_reachable_id: usize = 0x87; pub const tag_items_data_item_stability: usize = 0x88; diff --git a/src/librustc/metadata/creader.rs b/src/librustc/metadata/creader.rs index bbb2d4fade1f0..3226a99c6b3ae 100644 --- a/src/librustc/metadata/creader.rs +++ b/src/librustc/metadata/creader.rs @@ -20,6 +20,7 @@ use metadata::cstore::{CStore, CrateSource, MetadataBlob}; use metadata::decoder; use metadata::loader; use metadata::loader::CratePaths; +use util::nodemap::FnvHashMap; use std::cell::RefCell; use std::path::PathBuf; @@ -47,6 +48,7 @@ pub struct LocalCrateReader<'a, 'b:'a> { pub struct CrateReader<'a> { sess: &'a Session, next_crate_num: ast::CrateNum, + foreign_item_map: FnvHashMap>, } impl<'a, 'b, 'v> visit::Visitor<'v> for LocalCrateReader<'a, 'b> { @@ -157,6 +159,7 @@ impl<'a> CrateReader<'a> { CrateReader { sess: sess, next_crate_num: sess.cstore.next_crate_num(), + foreign_item_map: FnvHashMap(), } } @@ -490,6 +493,20 @@ impl<'a> CrateReader<'a> { _ => None, } } + + fn register_statically_included_foreign_items(&mut self) { + let libs = self.sess.cstore.get_used_libraries(); + for (lib, list) in self.foreign_item_map.iter() { + let is_static = libs.borrow().iter().any(|&(ref name, kind)| { + lib == name && kind == cstore::NativeStatic + }); + if is_static { + for id in list { + self.sess.cstore.add_statically_included_foreign_item(*id); + } + } + } + } } impl<'a, 'b> LocalCrateReader<'a, 'b> { @@ -515,6 +532,7 @@ impl<'a, 'b> LocalCrateReader<'a, 'b> { for &(ref name, kind) in &self.sess.opts.libs { register_native_lib(self.sess, None, name.clone(), kind); } + self.creader.register_statically_included_foreign_items(); } fn process_crate(&self, c: &ast::Crate) { @@ -541,87 +559,73 @@ impl<'a, 'b> LocalCrateReader<'a, 'b> { None, i.span, PathKind::Crate); - self.ast_map.with_path(i.id, |path| - cmeta.update_local_path(path)); + self.ast_map.with_path(i.id, |path| { + cmeta.update_local_path(path) + }); self.sess.cstore.add_extern_mod_stmt_cnum(info.id, cnum); } None => () } } - ast::ItemForeignMod(ref fm) => { - if fm.abi == abi::Rust || fm.abi == abi::RustIntrinsic { - return; - } + ast::ItemForeignMod(ref fm) => self.process_foreign_mod(i, fm), + _ => { } + } + } - // First, add all of the custom link_args attributes - let link_args = i.attrs.iter() - .filter_map(|at| if at.name() == "link_args" { - Some(at) - } else { - None - }) - .collect::>(); - for m in &link_args { - match m.value_str() { - Some(linkarg) => self.sess.cstore.add_used_link_args(&linkarg), - None => { /* fallthrough */ } - } - } + fn process_foreign_mod(&mut self, i: &ast::Item, fm: &ast::ForeignMod) { + if fm.abi == abi::Rust || fm.abi == abi::RustIntrinsic { + return; + } - // Next, process all of the #[link(..)]-style arguments - let link_args = i.attrs.iter() - .filter_map(|at| if at.name() == "link" { - Some(at) - } else { - None - }) - .collect::>(); - for m in &link_args { - match m.meta_item_list() { - Some(items) => { - let kind = items.iter().find(|k| { - k.name() == "kind" - }).and_then(|a| a.value_str()); - let kind = match kind { - Some(k) => { - if k == "static" { - cstore::NativeStatic - } else if self.sess.target.target.options.is_like_osx - && k == "framework" { - cstore::NativeFramework - } else if k == "framework" { - cstore::NativeFramework - } else if k == "dylib" { - cstore::NativeUnknown - } else { - self.sess.span_err(m.span, - &format!("unknown kind: `{}`", - k)); - cstore::NativeUnknown - } - } - None => cstore::NativeUnknown - }; - let n = items.iter().find(|n| { - n.name() == "name" - }).and_then(|a| a.value_str()); - let n = match n { - Some(n) => n, - None => { - self.sess.span_err(m.span, - "#[link(...)] specified without \ - `name = \"foo\"`"); - InternedString::new("foo") - } - }; - register_native_lib(self.sess, Some(m.span), - n.to_string(), kind); - } - None => {} - } - } + // First, add all of the custom #[link_args] attributes + for m in i.attrs.iter().filter(|a| a.check_name("link_args")) { + if let Some(linkarg) = m.value_str() { + self.sess.cstore.add_used_link_args(&linkarg); } - _ => { } + } + + // Next, process all of the #[link(..)]-style arguments + for m in i.attrs.iter().filter(|a| a.check_name("link")) { + let items = match m.meta_item_list() { + Some(item) => item, + None => continue, + }; + let kind = items.iter().find(|k| { + k.check_name("kind") + }).and_then(|a| a.value_str()); + let kind = match kind.as_ref().map(|s| &s[..]) { + Some("static") => cstore::NativeStatic, + Some("dylib") => cstore::NativeUnknown, + Some("framework") => cstore::NativeFramework, + Some(k) => { + self.sess.span_err(m.span, &format!("unknown kind: `{}`", k)); + cstore::NativeUnknown + } + None => cstore::NativeUnknown + }; + let n = items.iter().find(|n| { + n.check_name("name") + }).and_then(|a| a.value_str()); + let n = match n { + Some(n) => n, + None => { + self.sess.span_err(m.span, "#[link(...)] specified without \ + `name = \"foo\"`"); + InternedString::new("foo") + } + }; + register_native_lib(self.sess, Some(m.span), n.to_string(), kind); + } + + // Finally, process the #[linked_from = "..."] attribute + for m in i.attrs.iter().filter(|a| a.check_name("linked_from")) { + let lib_name = match m.value_str() { + Some(name) => name, + None => continue, + }; + let list = self.creader.foreign_item_map.entry(lib_name.to_string()) + .or_insert(Vec::new()); + list.extend(fm.items.iter().map(|it| it.id)); } } } diff --git a/src/librustc/metadata/csearch.rs b/src/librustc/metadata/csearch.rs index efa17912a32f0..2ade251018f26 100644 --- a/src/librustc/metadata/csearch.rs +++ b/src/librustc/metadata/csearch.rs @@ -352,11 +352,11 @@ pub fn get_method_arg_names(cstore: &cstore::CStore, did: ast::DefId) decoder::get_method_arg_names(&*cdata, did.node) } -pub fn get_reachable_extern_fns(cstore: &cstore::CStore, cnum: ast::CrateNum) +pub fn get_reachable_ids(cstore: &cstore::CStore, cnum: ast::CrateNum) -> Vec { let cdata = cstore.get_crate_data(cnum); - decoder::get_reachable_extern_fns(&*cdata) + decoder::get_reachable_ids(&*cdata) } pub fn is_typedef(cstore: &cstore::CStore, did: ast::DefId) -> bool { @@ -400,3 +400,9 @@ pub fn is_default_impl(cstore: &cstore::CStore, impl_did: ast::DefId) -> bool { let cdata = cstore.get_crate_data(impl_did.krate); decoder::is_default_impl(&*cdata, impl_did.node) } + +pub fn is_extern_fn(cstore: &cstore::CStore, did: ast::DefId, + tcx: &ty::ctxt) -> bool { + let cdata = cstore.get_crate_data(did.krate); + decoder::is_extern_fn(&*cdata, did.node, tcx) +} diff --git a/src/librustc/metadata/cstore.rs b/src/librustc/metadata/cstore.rs index 19d494824cf3a..ae5e797a0299b 100644 --- a/src/librustc/metadata/cstore.rs +++ b/src/librustc/metadata/cstore.rs @@ -20,7 +20,7 @@ pub use self::NativeLibraryKind::*; use back::svh::Svh; use metadata::{creader, decoder, loader}; use session::search_paths::PathKind; -use util::nodemap::{FnvHashMap, NodeMap}; +use util::nodemap::{FnvHashMap, NodeMap, NodeSet}; use std::cell::{RefCell, Ref}; use std::rc::Rc; @@ -97,6 +97,7 @@ pub struct CStore { used_crate_sources: RefCell>, used_libraries: RefCell>, used_link_args: RefCell>, + statically_included_foreign_items: RefCell, pub intr: Rc, } @@ -108,7 +109,8 @@ impl CStore { used_crate_sources: RefCell::new(Vec::new()), used_libraries: RefCell::new(Vec::new()), used_link_args: RefCell::new(Vec::new()), - intr: intr + intr: intr, + statically_included_foreign_items: RefCell::new(NodeSet()), } } @@ -167,6 +169,7 @@ impl CStore { self.used_crate_sources.borrow_mut().clear(); self.used_libraries.borrow_mut().clear(); self.used_link_args.borrow_mut().clear(); + self.statically_included_foreign_items.borrow_mut().clear(); } // This method is used when generating the command line to pass through to @@ -240,6 +243,14 @@ impl CStore { -> Option { self.extern_mod_crate_map.borrow().get(&emod_id).cloned() } + + pub fn add_statically_included_foreign_item(&self, id: ast::NodeId) { + self.statically_included_foreign_items.borrow_mut().insert(id); + } + + pub fn is_statically_included_foreign_item(&self, id: ast::NodeId) -> bool { + self.statically_included_foreign_items.borrow().contains(&id) + } } impl crate_metadata { diff --git a/src/librustc/metadata/decoder.rs b/src/librustc/metadata/decoder.rs index df5f798217f4b..c6c18fa14a340 100644 --- a/src/librustc/metadata/decoder.rs +++ b/src/librustc/metadata/decoder.rs @@ -45,6 +45,7 @@ use std::str; use rbml::reader; use rbml; use serialize::Decodable; +use syntax::abi; use syntax::attr; use syntax::parse::token::{IdentInterner, special_idents}; use syntax::parse::token; @@ -1418,10 +1419,10 @@ pub fn get_method_arg_names(cdata: Cmd, id: ast::NodeId) -> Vec { } } -pub fn get_reachable_extern_fns(cdata: Cmd) -> Vec { +pub fn get_reachable_ids(cdata: Cmd) -> Vec { let items = reader::get_doc(rbml::Doc::new(cdata.data()), - tag_reachable_extern_fns); - reader::tagged_docs(items, tag_reachable_extern_fn_id).map(|doc| { + tag_reachable_ids); + reader::tagged_docs(items, tag_reachable_id).map(|doc| { ast::DefId { krate: cdata.cnum, node: reader::doc_as_u32(doc), @@ -1543,3 +1544,21 @@ pub fn get_imported_filemaps(metadata: &[u8]) -> Vec { Decodable::decode(&mut decoder).unwrap() }).collect() } + +pub fn is_extern_fn(cdata: Cmd, id: ast::NodeId, tcx: &ty::ctxt) -> bool { + let root_doc = rbml::Doc::new(cdata.data()); + let items = reader::get_doc(root_doc, tag_items); + let item_doc = match maybe_find_item(id, items) { + Some(doc) => doc, + None => return false, + }; + if let Fn = item_family(item_doc) { + let ty::TypeScheme { generics, ty } = get_type(cdata, id, tcx); + generics.types.is_empty() && match ty.sty { + ty::TyBareFn(_, fn_ty) => fn_ty.abi != abi::Rust, + _ => false, + } + } else { + false + } +} diff --git a/src/librustc/metadata/encoder.rs b/src/librustc/metadata/encoder.rs index d5c189ff044d2..e0f35b6817b4f 100644 --- a/src/librustc/metadata/encoder.rs +++ b/src/librustc/metadata/encoder.rs @@ -1781,9 +1781,8 @@ fn encode_crate_deps(rbml_w: &mut Encoder, cstore: &cstore::CStore) { // FIXME (#2166): This is not nearly enough to support correct versioning // but is enough to get transitive crate dependencies working. rbml_w.start_tag(tag_crate_deps); - let r = get_ordered_deps(cstore); - for dep in &r { - encode_crate_dep(rbml_w, (*dep).clone()); + for dep in &get_ordered_deps(cstore) { + encode_crate_dep(rbml_w, dep); } rbml_w.end_tag(); } @@ -1971,24 +1970,22 @@ fn encode_misc_info(ecx: &EncodeContext, rbml_w.end_tag(); } -fn encode_reachable_extern_fns(ecx: &EncodeContext, rbml_w: &mut Encoder) { - rbml_w.start_tag(tag_reachable_extern_fns); - +// Encodes all reachable symbols in this crate into the metadata. +// +// This pass is seeded off the reachability list calculated in the +// middle::reachable module but filters out items that either don't have a +// symbol associated with them (they weren't translated) or if they're an FFI +// definition (as that's not defined in this crate). +fn encode_reachable(ecx: &EncodeContext, rbml_w: &mut Encoder) { + rbml_w.start_tag(tag_reachable_ids); for id in ecx.reachable { - if let Some(ast_map::NodeItem(i)) = ecx.tcx.map.find(*id) { - if let ast::ItemFn(_, _, _, abi, ref generics, _) = i.node { - if abi != abi::Rust && !generics.is_type_parameterized() { - rbml_w.wr_tagged_u32(tag_reachable_extern_fn_id, *id); - } - } - } + rbml_w.wr_tagged_u32(tag_reachable_id, *id); } - rbml_w.end_tag(); } fn encode_crate_dep(rbml_w: &mut Encoder, - dep: decoder::CrateDep) { + dep: &decoder::CrateDep) { rbml_w.start_tag(tag_crate_dep); rbml_w.wr_tagged_str(tag_crate_dep_crate_name, &dep.name); rbml_w.wr_tagged_str(tag_crate_dep_hash, dep.hash.as_str()); @@ -2170,7 +2167,7 @@ fn encode_metadata_inner(wr: &mut Cursor>, // Encode miscellaneous info. i = rbml_w.writer.seek(SeekFrom::Current(0)).unwrap(); encode_misc_info(&ecx, krate, &mut rbml_w); - encode_reachable_extern_fns(&ecx, &mut rbml_w); + encode_reachable(&ecx, &mut rbml_w); stats.misc_bytes = rbml_w.writer.seek(SeekFrom::Current(0)).unwrap() - i; // Encode and index the items. diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs index 9ee046915daca..e0d585d6f5b52 100644 --- a/src/librustc_llvm/lib.rs +++ b/src/librustc_llvm/lib.rs @@ -31,6 +31,7 @@ #![feature(link_args)] #![feature(staged_api)] #![feature(vec_push_all)] +#![cfg_attr(not(stage0), feature(linked_from))] extern crate libc; #[macro_use] #[no_link] extern crate rustc_bitflags; @@ -598,6 +599,7 @@ pub mod debuginfo { // automatically updated whenever LLVM is updated to include an up-to-date // set of the libraries we need to link to LLVM for. #[link(name = "rustllvm", kind = "static")] +#[cfg_attr(not(stage0), linked_from = "rustllvm")] // not quite true but good enough extern { /* Create and destroy contexts. */ pub fn LLVMContextCreate() -> ContextRef; diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs index 3ab557bc1eb68..46c7b80670f3c 100644 --- a/src/librustc_trans/back/link.rs +++ b/src/librustc_trans/back/link.rs @@ -902,6 +902,12 @@ fn link_args(cmd: &mut Linker, } cmd.output_filename(out_filename); + // If we're building a dynamic library then some platforms need to make sure + // that all symbols are exported correctly from the dynamic library. + if dylib { + cmd.export_symbols(sess, trans, tmpdir); + } + // 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. diff --git a/src/librustc_trans/back/linker.rs b/src/librustc_trans/back/linker.rs index 3a709955098c3..8bd86a3a34a19 100644 --- a/src/librustc_trans/back/linker.rs +++ b/src/librustc_trans/back/linker.rs @@ -9,14 +9,21 @@ // except according to those terms. use std::ffi::OsString; +use std::fs::{self, File}; +use std::io::{self, BufWriter}; +use std::io::prelude::*; use std::path::{Path, PathBuf}; use std::process::Command; -use std::fs; use back::archive; +use metadata::csearch; +use metadata::cstore; use session::Session; -use session::config; use session::config::DebugInfoLevel::{NoDebugInfo, LimitedDebugInfo, FullDebugInfo}; +use session::config::CrateTypeDylib; +use session::config; +use syntax::ast; +use trans::CrateTranslation; /// Linker abstraction used by back::link to build up the command to invoke a /// linker. @@ -48,6 +55,8 @@ pub trait Linker { fn hint_dynamic(&mut self); fn whole_archives(&mut self); fn no_whole_archives(&mut self); + fn export_symbols(&mut self, sess: &Session, trans: &CrateTranslation, + tmpdir: &Path); } pub struct GnuLinker<'a> { @@ -192,6 +201,10 @@ impl<'a> Linker for GnuLinker<'a> { if !self.takes_hints() { return } self.cmd.arg("-Wl,-Bdynamic"); } + + fn export_symbols(&mut self, _: &Session, _: &CrateTranslation, _: &Path) { + // noop, visibility in object files takes care of this + } } pub struct MsvcLinker<'a> { @@ -301,4 +314,61 @@ impl<'a> Linker for MsvcLinker<'a> { // we do on Unix platforms. fn hint_static(&mut self) {} fn hint_dynamic(&mut self) {} + + // Currently the compiler doesn't use `dllexport` (an LLVM attribute) to + // export symbols from a dynamic library. When building a dynamic library, + // however, we're going to want some symbols exported, so this function + // generates a DEF file which lists all the symbols. + // + // The linker will read this `*.def` file and export all the symbols from + // the dynamic library. Note that this is not as simple as just exporting + // all the symbols in the current crate (as specified by `trans.reachable`) + // but rather we also need to possibly export the symbols of upstream + // crates. Upstream rlibs may be linked statically to this dynamic library, + // in which case they may continue to transitively be used and hence need + // their symbols exported. + fn export_symbols(&mut self, sess: &Session, trans: &CrateTranslation, + tmpdir: &Path) { + let path = tmpdir.join("lib.def"); + let res = (|| -> io::Result<()> { + let mut f = BufWriter::new(try!(File::create(&path))); + + // Start off with the standard module name header and then go + // straight to exports. + try!(writeln!(f, "LIBRARY")); + try!(writeln!(f, "EXPORTS")); + + // Write out all our local symbols + for sym in trans.reachable.iter() { + try!(writeln!(f, " {}", sym)); + } + + // Take a look at how all upstream crates are linked into this + // dynamic library. For all statically linked libraries we take all + // their reachable symbols and emit them as well. + let cstore = &sess.cstore; + let symbols = trans.crate_formats[&CrateTypeDylib].iter(); + let symbols = symbols.enumerate().filter_map(|(i, f)| { + if let Some(cstore::RequireStatic) = *f { + Some((i + 1) as ast::CrateNum) + } else { + None + } + }).flat_map(|cnum| { + csearch::get_reachable_ids(cstore, cnum) + }).map(|did| { + csearch::get_symbol(cstore, did) + }); + for symbol in symbols { + try!(writeln!(f, " {}", symbol)); + } + Ok(()) + })(); + if let Err(e) = res { + sess.fatal(&format!("failed to write lib.def file: {}", e)); + } + let mut arg = OsString::from("/DEF:"); + arg.push(path); + self.cmd.arg(&arg); + } } diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 583c2c0908522..716b129081724 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -81,7 +81,7 @@ use trans::type_of::*; use trans::value::Value; use util::common::indenter; use util::sha2::Sha256; -use util::nodemap::NodeMap; +use util::nodemap::{NodeMap, NodeSet}; use arena::TypedArena; use libc::c_uint; @@ -2007,17 +2007,11 @@ 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); } @@ -2158,28 +2152,12 @@ pub fn register_fn_llvmty(ccx: &CrateContext, ty::FnConverging(ccx.tcx().mk_nil())).unwrap_or_else(||{ ccx.sess().span_fatal(sp, &format!("symbol `{}` is already defined", sym)); }); - finish_register_fn(ccx, sym, node_id, llfn); + finish_register_fn(ccx, sym, node_id); llfn } -fn finish_register_fn(ccx: &CrateContext, sym: String, node_id: ast::NodeId, - llfn: ValueRef) { +fn finish_register_fn(ccx: &CrateContext, sym: String, node_id: ast::NodeId) { ccx.item_symbols().borrow_mut().insert(node_id, sym); - - // The eh_personality function need to be externally linkable. - let def = ast_util::local_def(node_id); - 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); - } - } - if ccx.tcx().lang_items.eh_unwind_resume() == Some(def) { - llvm::SetLinkage(llfn, llvm::ExternalLinkage); - if ccx.use_dll_storage_attrs() { - llvm::SetDLLStorageClass(llfn, llvm::DLLExportStorageClass); - } - } } fn register_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, @@ -2201,7 +2179,7 @@ fn register_fn<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, let llfn = declare::define_rust_fn(ccx, &sym[..], node_type).unwrap_or_else(||{ ccx.sess().span_fatal(sp, &format!("symbol `{}` is already defined", sym)); }); - finish_register_fn(ccx, sym, node_id, llfn); + finish_register_fn(ccx, sym, node_id); llfn } @@ -2215,8 +2193,8 @@ pub fn is_entry_fn(sess: &Session, node_id: ast::NodeId) -> bool { /// Create the `main` function which will initialise the rust runtime and call users’ main /// function. pub fn create_entry_wrapper(ccx: &CrateContext, - sp: Span, - main_llfn: ValueRef) { + sp: Span, + main_llfn: ValueRef) { let et = ccx.sess().entry_type.get().unwrap(); match et { config::EntryMain => { @@ -2242,12 +2220,6 @@ pub fn create_entry_wrapper(ccx: &CrateContext, panic!(); }); - // 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 { - llvm::SetDLLStorageClass(llfn, llvm::DLLExportStorageClass); - } - let llbb = unsafe { llvm::LLVMAppendBasicBlockInContext(ccx.llcx(), llfn, "top\0".as_ptr() as *const _) @@ -2524,7 +2496,8 @@ fn register_method(ccx: &CrateContext, id: ast::NodeId, } pub fn crate_ctxt_to_encode_parms<'a, 'tcx>(cx: &'a SharedCrateContext<'a, 'tcx>, - ie: encoder::EncodeInlinedItem<'a>) + ie: encoder::EncodeInlinedItem<'a>, + reachable: &'a NodeSet) -> encoder::EncodeParams<'a, 'tcx> { encoder::EncodeParams { diag: cx.sess().diagnostic(), @@ -2534,11 +2507,12 @@ pub fn crate_ctxt_to_encode_parms<'a, 'tcx>(cx: &'a SharedCrateContext<'a, 'tcx> link_meta: cx.link_meta(), cstore: &cx.sess().cstore, encode_inlined_item: ie, - reachable: cx.reachable(), + reachable: reachable, } } -pub fn write_metadata(cx: &SharedCrateContext, krate: &ast::Crate) -> Vec { +pub fn write_metadata(cx: &SharedCrateContext, krate: &ast::Crate, + reachable: &NodeSet) -> Vec { use flate; let any_library = cx.sess().crate_types.borrow().iter().any(|ty| { @@ -2551,7 +2525,8 @@ pub fn write_metadata(cx: &SharedCrateContext, krate: &ast::Crate) -> Vec { let encode_inlined_item: encoder::EncodeInlinedItem = Box::new(|ecx, rbml_w, ii| astencode::encode_inlined_item(ecx, rbml_w, ii)); - let encode_parms = crate_ctxt_to_encode_parms(cx, encode_inlined_item); + let encode_parms = crate_ctxt_to_encode_parms(cx, encode_inlined_item, + reachable); let metadata = encoder::encode_metadata(encode_parms, krate); let mut compressed = encoder::metadata_encoding_version.to_vec(); compressed.push_all(&flate::deflate_bytes(&metadata)); @@ -2576,7 +2551,7 @@ pub fn write_metadata(cx: &SharedCrateContext, krate: &ast::Crate) -> Vec { /// Find any symbols that are defined in one compilation unit, but not declared /// in any other compilation unit. Give these symbols internal linkage. -fn internalize_symbols(cx: &SharedCrateContext, reachable: &HashSet) { +fn internalize_symbols(cx: &SharedCrateContext, reachable: &HashSet<&str>) { unsafe { let mut declared = HashSet::new(); @@ -2659,6 +2634,41 @@ fn internalize_symbols(cx: &SharedCrateContext, reachable: &HashSet) { } } +/// The context provided lists a set of reachable ids as calculated by +/// middle::reachable, but this contains far more ids and symbols than we're +/// actually exposing from the object file. This function will filter the set in +/// the context to the set of ids which correspond to symbols that are exposed +/// from the object file being generated. +/// +/// This list is later used by linkers to determine the set of symbols needed to +/// be exposed from a dynamic library and it's also encoded into the metadata. +pub fn filter_reachable_ids(ccx: &SharedCrateContext) -> NodeSet { + ccx.reachable().iter().map(|x| *x).filter(|id| { + // First, only worry about nodes which have a symbol name + ccx.item_symbols().borrow().contains_key(id) + }).filter(|&id| { + // Next, we want to ignore some FFI functions that are not exposed from + // this crate. Reachable FFI functions can be lumped into two + // categories: + // + // 1. Those that are included statically via a static library + // 2. Those included otherwise (e.g. dynamically or via a framework) + // + // Although our LLVM module is not literally emitting code for the + // statically included symbols, it's an export of our library which + // needs to be passed on to the linker and encoded in the metadata. + // + // As a result, if this id is an FFI item (foreign item) then we only + // let it through if it's included statically. + match ccx.tcx().map.get(id) { + ast_map::NodeForeignItem(..) => { + ccx.sess().cstore.is_statically_included_foreign_item(id) + } + _ => true, + } + }).collect() +} + pub fn trans_crate(tcx: &ty::ctxt, analysis: ty::CrateAnalysis) -> CrateTranslation { let ty::CrateAnalysis { export_map, reachable, name, .. } = analysis; let krate = tcx.map.krate(); @@ -2734,8 +2744,10 @@ pub fn trans_crate(tcx: &ty::ctxt, analysis: ty::CrateAnalysis) -> CrateTranslat } } + let reachable_symbol_ids = filter_reachable_ids(&shared_ccx); + // Translate the metadata. - let metadata = write_metadata(&shared_ccx, krate); + let metadata = write_metadata(&shared_ccx, krate, &reachable_symbol_ids); if shared_ccx.sess().trans_stats() { let stats = shared_ccx.stats(); @@ -2770,31 +2782,31 @@ pub fn trans_crate(tcx: &ty::ctxt, analysis: ty::CrateAnalysis) -> CrateTranslat .map(|ccx| ModuleTranslation { llcx: ccx.llcx(), llmod: ccx.llmod() }) .collect(); - let mut reachable: Vec = shared_ccx.reachable().iter().filter_map(|id| { - shared_ccx.item_symbols().borrow().get(id).map(|s| s.to_string()) - }).collect(); + let sess = shared_ccx.sess(); + let mut reachable_symbols = reachable_symbol_ids.iter().map(|id| { + shared_ccx.item_symbols().borrow()[id].to_string() + }).collect::>(); + if sess.entry_fn.borrow().is_some() { + reachable_symbols.push("main".to_string()); + } // For the purposes of LTO, we add to the reachable set all of the upstream // reachable extern fns. These functions are all part of the public ABI of // the final product, so LTO needs to preserve them. - shared_ccx.sess().cstore.iter_crate_data(|cnum, _| { - let syms = csearch::get_reachable_extern_fns(&shared_ccx.sess().cstore, cnum); - reachable.extend(syms.into_iter().map(|did| { - csearch::get_symbol(&shared_ccx.sess().cstore, did) - })); - }); - - // Make sure that some other crucial symbols are not eliminated from the - // module, including the main function. - reachable.push("main".to_string()); - - // referenced from .eh_frame section on some platforms - reachable.push("rust_eh_personality".to_string()); - // referenced from rt/rust_try.ll - reachable.push("rust_eh_personality_catch".to_string()); + if sess.lto() { + sess.cstore.iter_crate_data(|cnum, _| { + let syms = csearch::get_reachable_ids(&sess.cstore, cnum); + reachable_symbols.extend(syms.into_iter().filter(|did| { + csearch::is_extern_fn(&sess.cstore, *did, shared_ccx.tcx()) + }).map(|did| { + csearch::get_symbol(&sess.cstore, did) + })); + }); + } if codegen_units > 1 { - internalize_symbols(&shared_ccx, &reachable.iter().cloned().collect()); + internalize_symbols(&shared_ccx, + &reachable_symbols.iter().map(|x| &x[..]).collect()); } let metadata_module = ModuleTranslation { @@ -2809,7 +2821,7 @@ pub fn trans_crate(tcx: &ty::ctxt, analysis: ty::CrateAnalysis) -> CrateTranslat metadata_module: metadata_module, link: link_meta, metadata: metadata, - reachable: reachable, + reachable: reachable_symbols, crate_formats: formats, no_builtins: no_builtins, } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 60a5730a3da0c..87149ff81da78 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -85,6 +85,7 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Status)] = &[ ("on_unimplemented", "1.0.0", Active), ("simd_ffi", "1.0.0", Active), ("allocator", "1.0.0", Active), + ("linked_from", "1.3.0", Active), ("if_let", "1.0.0", Accepted), ("while_let", "1.0.0", Accepted), @@ -269,6 +270,10 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType)] = &[ "the `#[fundamental]` attribute \ is an experimental feature")), + ("linked_from", Gated("linked_from", + "the `#[linked_from]` attribute \ + is an experimental feature")), + // FIXME: #14408 whitelist docs since rustdoc looks at them ("doc", Whitelisted), diff --git a/src/test/auxiliary/issue-25185-1.rs b/src/test/auxiliary/issue-25185-1.rs index b9da39cbbcb4e..1ec29501b763c 100644 --- a/src/test/auxiliary/issue-25185-1.rs +++ b/src/test/auxiliary/issue-25185-1.rs @@ -10,9 +10,12 @@ // no-prefer-dynamic +#![feature(linked_from)] + #![crate_type = "rlib"] #[link(name = "rust_test_helpers", kind = "static")] +#[linked_from = "rust_test_helpers"] extern { pub fn rust_dbg_extern_identity_u32(u: u32) -> u32; } diff --git a/src/test/compile-fail/feature-gate-linked-from.rs b/src/test/compile-fail/feature-gate-linked-from.rs new file mode 100644 index 0000000000000..8705684111eb4 --- /dev/null +++ b/src/test/compile-fail/feature-gate-linked-from.rs @@ -0,0 +1,16 @@ +// 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. + +#[linked_from = "foo"] //~ ERROR experimental feature +extern { + fn foo(); +} + +fn main() {} diff --git a/src/test/run-pass/variadic-ffi.rs b/src/test/run-pass/variadic-ffi.rs index fd70c0409fb98..6351cc76a2e4c 100644 --- a/src/test/run-pass/variadic-ffi.rs +++ b/src/test/run-pass/variadic-ffi.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// ignore-msvc -- sprintf isn't a symbol in msvcrt? maybe a #define? #![feature(libc, std_misc)]