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
This commit is contained in:
bors 2015-08-11 02:10:31 +00:00
commit 8b3705528a
18 changed files with 332 additions and 170 deletions

View File

@ -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)

View File

@ -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) \
$$<

View File

@ -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}

View File

@ -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:

View File

@ -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;

View File

@ -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<String, Vec<ast::NodeId>>,
}
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,89 +559,75 @@ 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;
}
// 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::<Vec<&ast::Attribute>>();
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::<Vec<&ast::Attribute>>();
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

View File

@ -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<ast::DefId>
{
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)
}

View File

@ -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<Vec<CrateSource>>,
used_libraries: RefCell<Vec<(String, NativeLibraryKind)>>,
used_link_args: RefCell<Vec<String>>,
statically_included_foreign_items: RefCell<NodeSet>,
pub intr: Rc<IdentInterner>,
}
@ -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<ast::CrateNum> {
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 {

View File

@ -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<String> {
}
}
pub fn get_reachable_extern_fns(cdata: Cmd) -> Vec<ast::DefId> {
pub fn get_reachable_ids(cdata: Cmd) -> Vec<ast::DefId> {
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<codemap::FileMap> {
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
}
}

View File

@ -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<Vec<u8>>,
// 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.

View File

@ -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;

View File

@ -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.

View File

@ -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);
}
}

View File

@ -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<u8> {
pub fn write_metadata(cx: &SharedCrateContext, krate: &ast::Crate,
reachable: &NodeSet) -> Vec<u8> {
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<u8> {
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<u8> {
/// 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<String>) {
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<String>) {
}
}
/// 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<String> = 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::<Vec<_>>();
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,
}

View File

@ -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),

View File

@ -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;
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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() {}

View File

@ -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)]