Skip to content

Commit 1860714

Browse files
committed
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.
1 parent 5aca49c commit 1860714

File tree

4 files changed

+103
-77
lines changed

4 files changed

+103
-77
lines changed

src/doc/reference.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1924,10 +1924,16 @@ On an `extern` block, the following attributes are interpreted:
19241924
name and type. This is feature gated and the exact behavior is
19251925
implementation-defined (due to variety of linker invocation syntax).
19261926
- `link` - indicate that a native library should be linked to for the
1927-
declarations in this block to be linked correctly. `link` supports an optional `kind`
1928-
key with three possible values: `dylib`, `static`, and `framework`. See [external blocks](#external-blocks) for more about external blocks. Two
1927+
declarations in this block to be linked correctly. `link` supports an optional
1928+
`kind` key with three possible values: `dylib`, `static`, and `framework`. See
1929+
[external blocks](#external-blocks) for more about external blocks. Two
19291930
examples: `#[link(name = "readline")]` and
19301931
`#[link(name = "CoreFoundation", kind = "framework")]`.
1932+
- `linked_from` - indicates what native library this block of FFI items is
1933+
coming from. This attribute is of the form `#[linked_from = "foo"]` where
1934+
`foo` is the name of a library in either `#[link]` or a `-l` flag. This
1935+
attribute is currently required to export symbols from a Rust dynamic library
1936+
on Windows, and it is feature gated behind the `linked_from` feature.
19311937

19321938
On declarations inside an `extern` block, the following attributes are
19331939
interpreted:

src/librustc/metadata/creader.rs

Lines changed: 77 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use metadata::cstore::{CStore, CrateSource, MetadataBlob};
2020
use metadata::decoder;
2121
use metadata::loader;
2222
use metadata::loader::CratePaths;
23+
use util::nodemap::FnvHashMap;
2324

2425
use std::cell::RefCell;
2526
use std::path::PathBuf;
@@ -47,6 +48,7 @@ pub struct LocalCrateReader<'a, 'b:'a> {
4748
pub struct CrateReader<'a> {
4849
sess: &'a Session,
4950
next_crate_num: ast::CrateNum,
51+
foreign_item_map: FnvHashMap<String, Vec<ast::NodeId>>,
5052
}
5153

5254
impl<'a, 'b, 'v> visit::Visitor<'v> for LocalCrateReader<'a, 'b> {
@@ -157,6 +159,7 @@ impl<'a> CrateReader<'a> {
157159
CrateReader {
158160
sess: sess,
159161
next_crate_num: sess.cstore.next_crate_num(),
162+
foreign_item_map: FnvHashMap(),
160163
}
161164
}
162165

@@ -490,6 +493,20 @@ impl<'a> CrateReader<'a> {
490493
_ => None,
491494
}
492495
}
496+
497+
fn register_statically_included_foreign_items(&mut self) {
498+
let libs = self.sess.cstore.get_used_libraries();
499+
for (lib, list) in self.foreign_item_map.iter() {
500+
let is_static = libs.borrow().iter().any(|&(ref name, kind)| {
501+
lib == name && kind == cstore::NativeStatic
502+
});
503+
if is_static {
504+
for id in list {
505+
self.sess.cstore.add_statically_included_foreign_item(*id);
506+
}
507+
}
508+
}
509+
}
493510
}
494511

495512
impl<'a, 'b> LocalCrateReader<'a, 'b> {
@@ -515,6 +532,7 @@ impl<'a, 'b> LocalCrateReader<'a, 'b> {
515532
for &(ref name, kind) in &self.sess.opts.libs {
516533
register_native_lib(self.sess, None, name.clone(), kind);
517534
}
535+
self.creader.register_statically_included_foreign_items();
518536
}
519537

520538
fn process_crate(&self, c: &ast::Crate) {
@@ -541,87 +559,73 @@ impl<'a, 'b> LocalCrateReader<'a, 'b> {
541559
None,
542560
i.span,
543561
PathKind::Crate);
544-
self.ast_map.with_path(i.id, |path|
545-
cmeta.update_local_path(path));
562+
self.ast_map.with_path(i.id, |path| {
563+
cmeta.update_local_path(path)
564+
});
546565
self.sess.cstore.add_extern_mod_stmt_cnum(info.id, cnum);
547566
}
548567
None => ()
549568
}
550569
}
551-
ast::ItemForeignMod(ref fm) => {
552-
if fm.abi == abi::Rust || fm.abi == abi::RustIntrinsic {
553-
return;
554-
}
570+
ast::ItemForeignMod(ref fm) => self.process_foreign_mod(i, fm),
571+
_ => { }
572+
}
573+
}
555574

556-
// First, add all of the custom link_args attributes
557-
let link_args = i.attrs.iter()
558-
.filter_map(|at| if at.name() == "link_args" {
559-
Some(at)
560-
} else {
561-
None
562-
})
563-
.collect::<Vec<&ast::Attribute>>();
564-
for m in &link_args {
565-
match m.value_str() {
566-
Some(linkarg) => self.sess.cstore.add_used_link_args(&linkarg),
567-
None => { /* fallthrough */ }
568-
}
569-
}
575+
fn process_foreign_mod(&mut self, i: &ast::Item, fm: &ast::ForeignMod) {
576+
if fm.abi == abi::Rust || fm.abi == abi::RustIntrinsic {
577+
return;
578+
}
570579

571-
// Next, process all of the #[link(..)]-style arguments
572-
let link_args = i.attrs.iter()
573-
.filter_map(|at| if at.name() == "link" {
574-
Some(at)
575-
} else {
576-
None
577-
})
578-
.collect::<Vec<&ast::Attribute>>();
579-
for m in &link_args {
580-
match m.meta_item_list() {
581-
Some(items) => {
582-
let kind = items.iter().find(|k| {
583-
k.name() == "kind"
584-
}).and_then(|a| a.value_str());
585-
let kind = match kind {
586-
Some(k) => {
587-
if k == "static" {
588-
cstore::NativeStatic
589-
} else if self.sess.target.target.options.is_like_osx
590-
&& k == "framework" {
591-
cstore::NativeFramework
592-
} else if k == "framework" {
593-
cstore::NativeFramework
594-
} else if k == "dylib" {
595-
cstore::NativeUnknown
596-
} else {
597-
self.sess.span_err(m.span,
598-
&format!("unknown kind: `{}`",
599-
k));
600-
cstore::NativeUnknown
601-
}
602-
}
603-
None => cstore::NativeUnknown
604-
};
605-
let n = items.iter().find(|n| {
606-
n.name() == "name"
607-
}).and_then(|a| a.value_str());
608-
let n = match n {
609-
Some(n) => n,
610-
None => {
611-
self.sess.span_err(m.span,
612-
"#[link(...)] specified without \
613-
`name = \"foo\"`");
614-
InternedString::new("foo")
615-
}
616-
};
617-
register_native_lib(self.sess, Some(m.span),
618-
n.to_string(), kind);
619-
}
620-
None => {}
621-
}
622-
}
580+
// First, add all of the custom #[link_args] attributes
581+
for m in i.attrs.iter().filter(|a| a.check_name("link_args")) {
582+
if let Some(linkarg) = m.value_str() {
583+
self.sess.cstore.add_used_link_args(&linkarg);
623584
}
624-
_ => { }
585+
}
586+
587+
// Next, process all of the #[link(..)]-style arguments
588+
for m in i.attrs.iter().filter(|a| a.check_name("link")) {
589+
let items = match m.meta_item_list() {
590+
Some(item) => item,
591+
None => continue,
592+
};
593+
let kind = items.iter().find(|k| {
594+
k.check_name("kind")
595+
}).and_then(|a| a.value_str());
596+
let kind = match kind.as_ref().map(|s| &s[..]) {
597+
Some("static") => cstore::NativeStatic,
598+
Some("dylib") => cstore::NativeUnknown,
599+
Some("framework") => cstore::NativeFramework,
600+
Some(k) => {
601+
self.sess.span_err(m.span, &format!("unknown kind: `{}`", k));
602+
cstore::NativeUnknown
603+
}
604+
None => cstore::NativeUnknown
605+
};
606+
let n = items.iter().find(|n| {
607+
n.check_name("name")
608+
}).and_then(|a| a.value_str());
609+
let n = match n {
610+
Some(n) => n,
611+
None => {
612+
self.sess.span_err(m.span, "#[link(...)] specified without \
613+
`name = \"foo\"`");
614+
InternedString::new("foo")
615+
}
616+
};
617+
register_native_lib(self.sess, Some(m.span), n.to_string(), kind);
618+
}
619+
620+
// Finally, process the #[linked_from = "..."] attribute
621+
for m in i.attrs.iter().filter(|a| a.check_name("linked_from")) {
622+
let lib_name = match m.value_str() {
623+
Some(name) => name,
624+
None => continue,
625+
};
626+
let list = self.creader.foreign_item_map.entry(lib_name.to_string())
627+
.or_insert(Vec::new());
628+
list.extend(fm.items.iter().map(|it| it.id));
625629
}
626630
}
627631
}

src/librustc/metadata/cstore.rs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ pub use self::NativeLibraryKind::*;
2020
use back::svh::Svh;
2121
use metadata::{creader, decoder, loader};
2222
use session::search_paths::PathKind;
23-
use util::nodemap::{FnvHashMap, NodeMap};
23+
use util::nodemap::{FnvHashMap, NodeMap, NodeSet};
2424

2525
use std::cell::{RefCell, Ref};
2626
use std::rc::Rc;
@@ -97,6 +97,7 @@ pub struct CStore {
9797
used_crate_sources: RefCell<Vec<CrateSource>>,
9898
used_libraries: RefCell<Vec<(String, NativeLibraryKind)>>,
9999
used_link_args: RefCell<Vec<String>>,
100+
statically_included_foreign_items: RefCell<NodeSet>,
100101
pub intr: Rc<IdentInterner>,
101102
}
102103

@@ -108,7 +109,8 @@ impl CStore {
108109
used_crate_sources: RefCell::new(Vec::new()),
109110
used_libraries: RefCell::new(Vec::new()),
110111
used_link_args: RefCell::new(Vec::new()),
111-
intr: intr
112+
intr: intr,
113+
statically_included_foreign_items: RefCell::new(NodeSet()),
112114
}
113115
}
114116

@@ -167,6 +169,7 @@ impl CStore {
167169
self.used_crate_sources.borrow_mut().clear();
168170
self.used_libraries.borrow_mut().clear();
169171
self.used_link_args.borrow_mut().clear();
172+
self.statically_included_foreign_items.borrow_mut().clear();
170173
}
171174

172175
// This method is used when generating the command line to pass through to
@@ -240,6 +243,14 @@ impl CStore {
240243
-> Option<ast::CrateNum> {
241244
self.extern_mod_crate_map.borrow().get(&emod_id).cloned()
242245
}
246+
247+
pub fn add_statically_included_foreign_item(&self, id: ast::NodeId) {
248+
self.statically_included_foreign_items.borrow_mut().insert(id);
249+
}
250+
251+
pub fn is_statically_included_foreign_item(&self, id: ast::NodeId) -> bool {
252+
self.statically_included_foreign_items.borrow().contains(&id)
253+
}
243254
}
244255

245256
impl crate_metadata {

src/libsyntax/feature_gate.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ const KNOWN_FEATURES: &'static [(&'static str, &'static str, Status)] = &[
8585
("on_unimplemented", "1.0.0", Active),
8686
("simd_ffi", "1.0.0", Active),
8787
("allocator", "1.0.0", Active),
88+
("linked_from", "1.3.0", Active),
8889

8990
("if_let", "1.0.0", Accepted),
9091
("while_let", "1.0.0", Accepted),
@@ -269,6 +270,10 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType)] = &[
269270
"the `#[fundamental]` attribute \
270271
is an experimental feature")),
271272

273+
("linked_from", Gated("linked_from",
274+
"the `#[linked_from]` attribute \
275+
is an experimental feature")),
276+
272277
// FIXME: #14408 whitelist docs since rustdoc looks at them
273278
("doc", Whitelisted),
274279

0 commit comments

Comments
 (0)