Skip to content

Commit 8b37055

Browse files
committed
Auto merge of #27416 - alexcrichton:fix-dll-export, r=brson
These two commits are aimed at "fixing" our usage of `dllexport` in the compiler. Currently we blanket apply this attribute to *everything* public in a crate, but this ends up with a few downsides: * Executables are larger than the should be as a result of thinking they should export everything * Native libraries aren't handled correctly because technically a statically included native library should be exported from a DLL in some cases. * Symbols don't actually need to be exported if they never end up in a DLL. The first commit adds a new unstable attribute, `#[linked_from]`, which is a way to tell the compiler what native library a block of symbols comes from. This is used to inform the compiler what set of native libraries are statically included in the rlib (or other output). This information is later used to export them from a DLL if necessary. Currently this is only used in a few places (such as the LLVM bindings) to get the compiler to link correctly. The second commit stops adding `dllexport` to all items in LLVM and instead explicitly telling the linker what symbols should be exported. We only need to do this when building a dynamic library, and otherwise we can avoid adding `dllexport` or telling the linker about exported symbols. As a testament to this change, the size of "Hello World" on MSVC drops from 1.2MB to 67KB as a result of this patch. This is because the linker can much more aggressively remove unused code. These commits do not yet attempt to fix our story with `dllimport`, and I'll leave that to a future PR and issue, for now though I'm going to say that this Closes #7196
2 parents 5aca49c + e648c96 commit 8b37055

File tree

18 files changed

+330
-168
lines changed

18 files changed

+330
-168
lines changed

mk/platform.mk

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -277,10 +277,15 @@ $(foreach target,$(CFG_TARGET), \
277277
# Fun times!
278278
#
279279
# [1]: https://msdn.microsoft.com/en-us/library/28d6s79h.aspx
280+
#
281+
# FIXME(stage0): remove this macro and the usage below (and the commments above)
282+
# when a new snapshot is available. Also remove the
283+
# RUSTFLAGS$(1)_.._T_ variable in mk/target.mk along with
284+
# CUSTOM_DEPS (as they were only added for this)
280285
define ADD_RUSTC_LLVM_DEF_TO_MSVC
281286
ifeq ($$(findstring msvc,$(1)),msvc)
282-
RUSTFLAGS_rustc_llvm_T_$(1) += -C link-args="-DEF:$(1)/rt/rustc_llvm.def"
283-
CUSTOM_DEPS_rustc_llvm_T_$(1) += $(1)/rt/rustc_llvm.def
287+
RUSTFLAGS0_rustc_llvm_T_$(1) += -C link-args="-DEF:$(1)/rt/rustc_llvm.def"
288+
CUSTOM_DEPS0_rustc_llvm_T_$(1) += $(1)/rt/rustc_llvm.def
284289

285290
$(1)/rt/rustc_llvm.def: $$(S)src/etc/mklldef.py $$(S)src/librustc_llvm/lib.rs
286291
$$(CFG_PYTHON) $$^ $$@ rustc_llvm-$$(CFG_FILENAME_EXTRA)

mk/target.mk

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ CRATE_FULLDEPS_$(1)_T_$(2)_H_$(3)_$(4) := \
4040
$$(RT_OUTPUT_DIR_$(2))/$$(dep)) \
4141
$$(foreach dep,$$(NATIVE_TOOL_DEPS_$(4)_T_$(2)), \
4242
$$(TBIN$(1)_T_$(3)_H_$(3))/$$(dep)) \
43-
$$(CUSTOM_DEPS_$(4)_T_$(2))
43+
$$(CUSTOM_DEPS$(1)_$(4)_T_$(2))
4444
endef
4545

4646
$(foreach host,$(CFG_HOST), \
@@ -92,7 +92,7 @@ $$(TLIB$(1)_T_$(2)_H_$(3))/stamp.$(4): \
9292
$$(LLVM_LIBDIR_RUSTFLAGS_$(2)) \
9393
$$(LLVM_STDCPP_RUSTFLAGS_$(2)) \
9494
$$(RUSTFLAGS_$(4)) \
95-
$$(RUSTFLAGS_$(4)_T_$(2)) \
95+
$$(RUSTFLAGS$(1)_$(4)_T_$(2)) \
9696
--out-dir $$(@D) \
9797
-C extra-filename=-$$(CFG_FILENAME_EXTRA) \
9898
$$<

src/compiletest/procsrv.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,7 @@ fn add_target_env(cmd: &mut Command, lib_path: &str, aux_path: Option<&str>) {
2626
// Add the new dylib search path var
2727
let var = DynamicLibrary::envvar();
2828
let newpath = DynamicLibrary::create_path(&path);
29-
let newpath = newpath.to_str().unwrap().to_string();
30-
cmd.env(var, &newpath);
29+
cmd.env(var, newpath);
3130
}
3231

3332
pub struct Result {pub status: ExitStatus, pub out: String, pub err: String}

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/common.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -205,8 +205,8 @@ pub const tag_plugin_registrar_fn: usize = 0x10b; // top-level only
205205
pub const tag_method_argument_names: usize = 0x85;
206206
pub const tag_method_argument_name: usize = 0x86;
207207

208-
pub const tag_reachable_extern_fns: usize = 0x10c; // top-level only
209-
pub const tag_reachable_extern_fn_id: usize = 0x87;
208+
pub const tag_reachable_ids: usize = 0x10c; // top-level only
209+
pub const tag_reachable_id: usize = 0x87;
210210

211211
pub const tag_items_data_item_stability: usize = 0x88;
212212

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/csearch.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -352,11 +352,11 @@ pub fn get_method_arg_names(cstore: &cstore::CStore, did: ast::DefId)
352352
decoder::get_method_arg_names(&*cdata, did.node)
353353
}
354354

355-
pub fn get_reachable_extern_fns(cstore: &cstore::CStore, cnum: ast::CrateNum)
355+
pub fn get_reachable_ids(cstore: &cstore::CStore, cnum: ast::CrateNum)
356356
-> Vec<ast::DefId>
357357
{
358358
let cdata = cstore.get_crate_data(cnum);
359-
decoder::get_reachable_extern_fns(&*cdata)
359+
decoder::get_reachable_ids(&*cdata)
360360
}
361361

362362
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 {
400400
let cdata = cstore.get_crate_data(impl_did.krate);
401401
decoder::is_default_impl(&*cdata, impl_did.node)
402402
}
403+
404+
pub fn is_extern_fn(cstore: &cstore::CStore, did: ast::DefId,
405+
tcx: &ty::ctxt) -> bool {
406+
let cdata = cstore.get_crate_data(did.krate);
407+
decoder::is_extern_fn(&*cdata, did.node, tcx)
408+
}

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/librustc/metadata/decoder.rs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ use std::str;
4545
use rbml::reader;
4646
use rbml;
4747
use serialize::Decodable;
48+
use syntax::abi;
4849
use syntax::attr;
4950
use syntax::parse::token::{IdentInterner, special_idents};
5051
use syntax::parse::token;
@@ -1418,10 +1419,10 @@ pub fn get_method_arg_names(cdata: Cmd, id: ast::NodeId) -> Vec<String> {
14181419
}
14191420
}
14201421

1421-
pub fn get_reachable_extern_fns(cdata: Cmd) -> Vec<ast::DefId> {
1422+
pub fn get_reachable_ids(cdata: Cmd) -> Vec<ast::DefId> {
14221423
let items = reader::get_doc(rbml::Doc::new(cdata.data()),
1423-
tag_reachable_extern_fns);
1424-
reader::tagged_docs(items, tag_reachable_extern_fn_id).map(|doc| {
1424+
tag_reachable_ids);
1425+
reader::tagged_docs(items, tag_reachable_id).map(|doc| {
14251426
ast::DefId {
14261427
krate: cdata.cnum,
14271428
node: reader::doc_as_u32(doc),
@@ -1543,3 +1544,21 @@ pub fn get_imported_filemaps(metadata: &[u8]) -> Vec<codemap::FileMap> {
15431544
Decodable::decode(&mut decoder).unwrap()
15441545
}).collect()
15451546
}
1547+
1548+
pub fn is_extern_fn(cdata: Cmd, id: ast::NodeId, tcx: &ty::ctxt) -> bool {
1549+
let root_doc = rbml::Doc::new(cdata.data());
1550+
let items = reader::get_doc(root_doc, tag_items);
1551+
let item_doc = match maybe_find_item(id, items) {
1552+
Some(doc) => doc,
1553+
None => return false,
1554+
};
1555+
if let Fn = item_family(item_doc) {
1556+
let ty::TypeScheme { generics, ty } = get_type(cdata, id, tcx);
1557+
generics.types.is_empty() && match ty.sty {
1558+
ty::TyBareFn(_, fn_ty) => fn_ty.abi != abi::Rust,
1559+
_ => false,
1560+
}
1561+
} else {
1562+
false
1563+
}
1564+
}

0 commit comments

Comments
 (0)