From 18607149fbb0836059a96981c78e10ca52d23cd5 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 30 Jul 2015 14:20:36 -0700 Subject: [PATCH] syntax: Add a new unstable #[linked_from] attribute To correctly reexport statically included libraries from a DLL on Windows, the compiler will soon need to have knowledge about what symbols are statically included and which are not. To solve this problem a new unstable `#[linked_from]` attribute is being added and recognized on `extern` blocks to indicate which native library the symbols are coming from. The compiler then keeps track of what the set of FFI symbols are that are included statically. This information will be used in a future commit to configure how we invoke the linker on Windows. --- src/doc/reference.md | 10 +- src/librustc/metadata/creader.rs | 154 ++++++++++++++++--------------- src/librustc/metadata/cstore.rs | 15 ++- src/libsyntax/feature_gate.rs | 5 + 4 files changed, 105 insertions(+), 79 deletions(-) diff --git a/src/doc/reference.md b/src/doc/reference.md index e905ed917d7..4d5564d9faf 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/creader.rs b/src/librustc/metadata/creader.rs index bbb2d4fade1..3226a99c6b3 100644 --- a/src/librustc/metadata/creader.rs +++ b/src/librustc/metadata/creader.rs @@ -20,6 +20,7 @@ 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 @@ pub fn new(sess: &'a Session) -> CrateReader<'a> { CrateReader { sess: sess, next_crate_num: sess.cstore.next_crate_num(), + foreign_item_map: FnvHashMap(), } } @@ -490,6 +493,20 @@ pub fn find_plugin_registrar(&mut self, span: Span, name: &str) _ => 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 @@ pub fn read_crates(&mut self, krate: &ast::Crate) { 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,89 +559,75 @@ fn process_item(&mut self, i: &ast::Item) { 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; - } - - // 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 */ } - } - } - - // 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 => {} - } - } - } + ast::ItemForeignMod(ref fm) => self.process_foreign_mod(i, fm), _ => { } } } + + fn process_foreign_mod(&mut self, i: &ast::Item, fm: &ast::ForeignMod) { + if fm.abi == abi::Rust || fm.abi == abi::RustIntrinsic { + return; + } + + // 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)); + } + } } /// Imports the codemap from an external crate into the codemap of the crate diff --git a/src/librustc/metadata/cstore.rs b/src/librustc/metadata/cstore.rs index 19d494824cf..ae5e797a029 100644 --- a/src/librustc/metadata/cstore.rs +++ b/src/librustc/metadata/cstore.rs @@ -20,7 +20,7 @@ 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 @@ pub fn new(intr: Rc) -> 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 @@ pub fn reset(&self) { 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 @@ pub fn find_extern_mod_stmt_cnum(&self, emod_id: ast::NodeId) -> 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/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 60a5730a3da..87149ff81da 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -85,6 +85,7 @@ ("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 @@ enum Status { "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),