Skip to content

Commit 1d67c7f

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 ba9224f commit 1d67c7f

File tree

4 files changed

+100
-75
lines changed

4 files changed

+100
-75
lines changed

src/doc/reference.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1901,10 +1901,16 @@ On an `extern` block, the following attributes are interpreted:
19011901
name and type. This is feature gated and the exact behavior is
19021902
implementation-defined (due to variety of linker invocation syntax).
19031903
- `link` - indicate that a native library should be linked to for the
1904-
declarations in this block to be linked correctly. `link` supports an optional `kind`
1905-
key with three possible values: `dylib`, `static`, and `framework`. See [external blocks](#external-blocks) for more about external blocks. Two
1904+
declarations in this block to be linked correctly. `link` supports an optional
1905+
`kind` key with three possible values: `dylib`, `static`, and `framework`. See
1906+
[external blocks](#external-blocks) for more about external blocks. Two
19061907
examples: `#[link(name = "readline")]` and
19071908
`#[link(name = "CoreFoundation", kind = "framework")]`.
1909+
- `linked_from` - indicates what native library this block of FFI items is
1910+
coming from. This attribute is of the form `#[linked_from = "foo"]` where
1911+
`foo` is the name of a library in either `#[link]` or a `-l` flag. This
1912+
attribute is currently required to export symbols from a Rust dynamic library
1913+
on Windows, and it is feature gated behind the `linked_from` feature.
19081914

19091915
On declarations inside an `extern` block, the following attributes are
19101916
interpreted:

src/librustc/metadata/creader.rs

Lines changed: 74 additions & 71 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;
@@ -40,6 +41,7 @@ use log;
4041
pub struct CrateReader<'a> {
4142
sess: &'a Session,
4243
next_crate_num: ast::CrateNum,
44+
foreign_item_map: FnvHashMap<String, Vec<ast::NodeId>>,
4345
}
4446

4547
impl<'a, 'v> visit::Visitor<'v> for CrateReader<'a> {
@@ -150,6 +152,7 @@ impl<'a> CrateReader<'a> {
150152
CrateReader {
151153
sess: sess,
152154
next_crate_num: sess.cstore.next_crate_num(),
155+
foreign_item_map: FnvHashMap(),
153156
}
154157
}
155158

@@ -167,6 +170,7 @@ impl<'a> CrateReader<'a> {
167170
for &(ref name, kind) in &self.sess.opts.libs {
168171
register_native_lib(self.sess, None, name.clone(), kind);
169172
}
173+
self.register_statically_included_foreign_items();
170174
}
171175

172176
fn process_crate(&self, c: &ast::Crate) {
@@ -223,80 +227,65 @@ impl<'a> CrateReader<'a> {
223227
None => ()
224228
}
225229
}
226-
ast::ItemForeignMod(ref fm) => {
227-
if fm.abi == abi::Rust || fm.abi == abi::RustIntrinsic {
228-
return;
229-
}
230+
ast::ItemForeignMod(ref fm) => self.process_foreign_mod(i, fm),
231+
_ => { }
232+
}
233+
}
230234

231-
// First, add all of the custom link_args attributes
232-
let link_args = i.attrs.iter()
233-
.filter_map(|at| if at.name() == "link_args" {
234-
Some(at)
235-
} else {
236-
None
237-
})
238-
.collect::<Vec<&ast::Attribute>>();
239-
for m in &link_args {
240-
match m.value_str() {
241-
Some(linkarg) => self.sess.cstore.add_used_link_args(&linkarg),
242-
None => { /* fallthrough */ }
243-
}
244-
}
235+
fn process_foreign_mod(&mut self, i: &ast::Item, fm: &ast::ForeignMod) {
236+
if fm.abi == abi::Rust || fm.abi == abi::RustIntrinsic {
237+
return;
238+
}
245239

246-
// Next, process all of the #[link(..)]-style arguments
247-
let link_args = i.attrs.iter()
248-
.filter_map(|at| if at.name() == "link" {
249-
Some(at)
250-
} else {
251-
None
252-
})
253-
.collect::<Vec<&ast::Attribute>>();
254-
for m in &link_args {
255-
match m.meta_item_list() {
256-
Some(items) => {
257-
let kind = items.iter().find(|k| {
258-
k.name() == "kind"
259-
}).and_then(|a| a.value_str());
260-
let kind = match kind {
261-
Some(k) => {
262-
if k == "static" {
263-
cstore::NativeStatic
264-
} else if self.sess.target.target.options.is_like_osx
265-
&& k == "framework" {
266-
cstore::NativeFramework
267-
} else if k == "framework" {
268-
cstore::NativeFramework
269-
} else if k == "dylib" {
270-
cstore::NativeUnknown
271-
} else {
272-
self.sess.span_err(m.span,
273-
&format!("unknown kind: `{}`",
274-
k));
275-
cstore::NativeUnknown
276-
}
277-
}
278-
None => cstore::NativeUnknown
279-
};
280-
let n = items.iter().find(|n| {
281-
n.name() == "name"
282-
}).and_then(|a| a.value_str());
283-
let n = match n {
284-
Some(n) => n,
285-
None => {
286-
self.sess.span_err(m.span,
287-
"#[link(...)] specified without \
288-
`name = \"foo\"`");
289-
InternedString::new("foo")
290-
}
291-
};
292-
register_native_lib(self.sess, Some(m.span),
293-
n.to_string(), kind);
294-
}
295-
None => {}
296-
}
297-
}
240+
// First, add all of the custom #[link_args] attributes
241+
for m in i.attrs.iter().filter(|a| a.check_name("link_args")) {
242+
if let Some(linkarg) = m.value_str() {
243+
self.sess.cstore.add_used_link_args(&linkarg);
298244
}
299-
_ => { }
245+
}
246+
247+
// Next, process all of the #[link(..)]-style arguments
248+
for m in i.attrs.iter().filter(|a| a.check_name("link")) {
249+
let items = match m.meta_item_list() {
250+
Some(item) => item,
251+
None => continue,
252+
};
253+
let kind = items.iter().find(|k| {
254+
k.check_name("kind")
255+
}).and_then(|a| a.value_str());
256+
let kind = match kind.as_ref().map(|s| &s[..]) {
257+
Some("static") => cstore::NativeStatic,
258+
Some("dylib") => cstore::NativeUnknown,
259+
Some("framework") => cstore::NativeFramework,
260+
Some(k) => {
261+
self.sess.span_err(m.span, &format!("unknown kind: `{}`", k));
262+
cstore::NativeUnknown
263+
}
264+
None => cstore::NativeUnknown
265+
};
266+
let n = items.iter().find(|n| {
267+
n.check_name("name")
268+
}).and_then(|a| a.value_str());
269+
let n = match n {
270+
Some(n) => n,
271+
None => {
272+
self.sess.span_err(m.span, "#[link(...)] specified without \
273+
`name = \"foo\"`");
274+
InternedString::new("foo")
275+
}
276+
};
277+
register_native_lib(self.sess, Some(m.span), n.to_string(), kind);
278+
}
279+
280+
// Finally, process the #[linked_from = "..."] attribute
281+
for m in i.attrs.iter().filter(|a| a.check_name("linked_from")) {
282+
let lib_name = match m.value_str() {
283+
Some(name) => name,
284+
None => continue,
285+
};
286+
let list = self.foreign_item_map.entry(lib_name.to_string())
287+
.or_insert(Vec::new());
288+
list.extend(fm.items.iter().map(|it| it.id));
300289
}
301290
}
302291

@@ -592,6 +581,20 @@ impl<'a> CrateReader<'a> {
592581
_ => None,
593582
}
594583
}
584+
585+
fn register_statically_included_foreign_items(&mut self) {
586+
let libs = self.sess.cstore.get_used_libraries();
587+
for (lib, list) in self.foreign_item_map.iter() {
588+
let is_static = libs.borrow().iter().any(|&(ref name, kind)| {
589+
lib == name && kind == cstore::NativeStatic
590+
});
591+
if is_static {
592+
for id in list {
593+
self.sess.cstore.add_statically_included_foreign_item(*id);
594+
}
595+
}
596+
}
597+
}
595598
}
596599

597600
/// Imports the codemap from an external crate into the codemap of the crate

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;
@@ -92,6 +92,7 @@ pub struct CStore {
9292
used_crate_sources: RefCell<Vec<CrateSource>>,
9393
used_libraries: RefCell<Vec<(String, NativeLibraryKind)>>,
9494
used_link_args: RefCell<Vec<String>>,
95+
statically_included_foreign_items: RefCell<NodeSet>,
9596
pub intr: Rc<IdentInterner>,
9697
}
9798

@@ -103,7 +104,8 @@ impl CStore {
103104
used_crate_sources: RefCell::new(Vec::new()),
104105
used_libraries: RefCell::new(Vec::new()),
105106
used_link_args: RefCell::new(Vec::new()),
106-
intr: intr
107+
intr: intr,
108+
statically_included_foreign_items: RefCell::new(NodeSet()),
107109
}
108110
}
109111

@@ -162,6 +164,7 @@ impl CStore {
162164
self.used_crate_sources.borrow_mut().clear();
163165
self.used_libraries.borrow_mut().clear();
164166
self.used_link_args.borrow_mut().clear();
167+
self.statically_included_foreign_items.borrow_mut().clear();
165168
}
166169

167170
// This method is used when generating the command line to pass through to
@@ -235,6 +238,14 @@ impl CStore {
235238
-> Option<ast::CrateNum> {
236239
self.extern_mod_crate_map.borrow().get(&emod_id).cloned()
237240
}
241+
242+
pub fn add_statically_included_foreign_item(&self, id: ast::NodeId) {
243+
self.statically_included_foreign_items.borrow_mut().insert(id);
244+
}
245+
246+
pub fn is_statically_included_foreign_item(&self, id: ast::NodeId) -> bool {
247+
self.statically_included_foreign_items.borrow().contains(&id)
248+
}
238249
}
239250

240251
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),
@@ -254,6 +255,10 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType)] = &[
254255
"the `#[fundamental]` attribute \
255256
is an experimental feature")),
256257

258+
("linked_from", Gated("linked_from",
259+
"the `#[linked_from]` attribute \
260+
is an experimental feature")),
261+
257262
// FIXME: #14408 whitelist docs since rustdoc looks at them
258263
("doc", Whitelisted),
259264

0 commit comments

Comments
 (0)