mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 14:55:26 +00:00
Auto merge of #38117 - michaelwoerister:hidden-symbols, r=alexcrichton
Improve symbol visibility handling for dynamic libraries. This will hopefully fix issue https://github.com/rust-lang/rust/issues/37530 and maybe also https://github.com/rust-lang/rust/issues/32887. I'm relying on @m4b to post some numbers on how awesome the improvement for cdylibs is `:)` cc @rust-lang/compiler @rust-lang/tools @cuviper @froydnj r? @alexcrichton
This commit is contained in:
commit
daf8c1dfce
@ -328,8 +328,9 @@ pub trait CrateStore<'tcx> {
|
||||
fn crate_hash(&self, cnum: CrateNum) -> Svh;
|
||||
fn crate_disambiguator(&self, cnum: CrateNum) -> Symbol;
|
||||
fn plugin_registrar_fn(&self, cnum: CrateNum) -> Option<DefId>;
|
||||
fn derive_registrar_fn(&self, cnum: CrateNum) -> Option<DefId>;
|
||||
fn native_libraries(&self, cnum: CrateNum) -> Vec<NativeLibrary>;
|
||||
fn reachable_ids(&self, cnum: CrateNum) -> Vec<DefId>;
|
||||
fn exported_symbols(&self, cnum: CrateNum) -> Vec<DefId>;
|
||||
fn is_no_builtins(&self, cnum: CrateNum) -> bool;
|
||||
|
||||
// resolve
|
||||
@ -491,9 +492,11 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore {
|
||||
-> Symbol { bug!("crate_disambiguator") }
|
||||
fn plugin_registrar_fn(&self, cnum: CrateNum) -> Option<DefId>
|
||||
{ bug!("plugin_registrar_fn") }
|
||||
fn derive_registrar_fn(&self, cnum: CrateNum) -> Option<DefId>
|
||||
{ bug!("derive_registrar_fn") }
|
||||
fn native_libraries(&self, cnum: CrateNum) -> Vec<NativeLibrary>
|
||||
{ bug!("native_libraries") }
|
||||
fn reachable_ids(&self, cnum: CrateNum) -> Vec<DefId> { bug!("reachable_ids") }
|
||||
fn exported_symbols(&self, cnum: CrateNum) -> Vec<DefId> { bug!("exported_symbols") }
|
||||
fn is_no_builtins(&self, cnum: CrateNum) -> bool { bug!("is_no_builtins") }
|
||||
|
||||
// resolve
|
||||
|
@ -64,6 +64,15 @@ pub enum Linkage {
|
||||
CommonLinkage = 10,
|
||||
}
|
||||
|
||||
// LLVMRustVisibility
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
#[repr(C)]
|
||||
pub enum Visibility {
|
||||
Default = 0,
|
||||
Hidden = 1,
|
||||
Protected = 2,
|
||||
}
|
||||
|
||||
/// LLVMDiagnosticSeverity
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(C)]
|
||||
@ -399,13 +408,6 @@ pub type OperandBundleDefRef = *mut OperandBundleDef_opaque;
|
||||
pub type DiagnosticHandler = unsafe extern "C" fn(DiagnosticInfoRef, *mut c_void);
|
||||
pub type InlineAsmDiagHandler = unsafe extern "C" fn(SMDiagnosticRef, *const c_void, c_uint);
|
||||
|
||||
/// LLVMVisibility
|
||||
#[repr(C)]
|
||||
pub enum Visibility {
|
||||
Default,
|
||||
Hidden,
|
||||
Protected,
|
||||
}
|
||||
|
||||
pub mod debuginfo {
|
||||
use super::MetadataRef;
|
||||
@ -655,7 +657,8 @@ extern "C" {
|
||||
pub fn LLVMRustSetLinkage(Global: ValueRef, RustLinkage: Linkage);
|
||||
pub fn LLVMGetSection(Global: ValueRef) -> *const c_char;
|
||||
pub fn LLVMSetSection(Global: ValueRef, Section: *const c_char);
|
||||
pub fn LLVMSetVisibility(Global: ValueRef, Viz: Visibility);
|
||||
pub fn LLVMRustGetVisibility(Global: ValueRef) -> Visibility;
|
||||
pub fn LLVMRustSetVisibility(Global: ValueRef, Viz: Visibility);
|
||||
pub fn LLVMGetAlignment(Global: ValueRef) -> c_uint;
|
||||
pub fn LLVMSetAlignment(Global: ValueRef, Bytes: c_uint);
|
||||
pub fn LLVMSetDLLStorageClass(V: ValueRef, C: DLLStorageClass);
|
||||
|
@ -306,14 +306,22 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore {
|
||||
})
|
||||
}
|
||||
|
||||
fn derive_registrar_fn(&self, cnum: CrateNum) -> Option<DefId>
|
||||
{
|
||||
self.get_crate_data(cnum).root.macro_derive_registrar.map(|index| DefId {
|
||||
krate: cnum,
|
||||
index: index
|
||||
})
|
||||
}
|
||||
|
||||
fn native_libraries(&self, cnum: CrateNum) -> Vec<NativeLibrary>
|
||||
{
|
||||
self.get_crate_data(cnum).get_native_libraries()
|
||||
}
|
||||
|
||||
fn reachable_ids(&self, cnum: CrateNum) -> Vec<DefId>
|
||||
fn exported_symbols(&self, cnum: CrateNum) -> Vec<DefId>
|
||||
{
|
||||
self.get_crate_data(cnum).get_reachable_ids()
|
||||
self.get_crate_data(cnum).get_exported_symbols()
|
||||
}
|
||||
|
||||
fn is_no_builtins(&self, cnum: CrateNum) -> bool {
|
||||
|
@ -1038,8 +1038,8 @@ impl<'a, 'tcx> CrateMetadata {
|
||||
arg_names.decode(self).collect()
|
||||
}
|
||||
|
||||
pub fn get_reachable_ids(&self) -> Vec<DefId> {
|
||||
self.root.reachable_ids.decode(self).map(|index| self.local_def_id(index)).collect()
|
||||
pub fn get_exported_symbols(&self) -> Vec<DefId> {
|
||||
self.root.exported_symbols.decode(self).map(|index| self.local_def_id(index)).collect()
|
||||
}
|
||||
|
||||
pub fn get_macro(&self, id: DefIndex) -> (ast::Name, MacroDef) {
|
||||
|
@ -50,7 +50,7 @@ pub struct EncodeContext<'a, 'tcx: 'a> {
|
||||
reexports: &'a def::ExportMap,
|
||||
link_meta: &'a LinkMeta,
|
||||
cstore: &'a cstore::CStore,
|
||||
reachable: &'a NodeSet,
|
||||
exported_symbols: &'a NodeSet,
|
||||
|
||||
lazy_state: LazyState,
|
||||
type_shorthands: FxHashMap<Ty<'tcx>, usize>,
|
||||
@ -1223,16 +1223,16 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
||||
self.lazy_seq(all_impls)
|
||||
}
|
||||
|
||||
// Encodes all reachable symbols in this crate into the metadata.
|
||||
// Encodes all symbols exported from 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(&mut self) -> LazySeq<DefIndex> {
|
||||
let reachable = self.reachable;
|
||||
fn encode_exported_symbols(&mut self) -> LazySeq<DefIndex> {
|
||||
let exported_symbols = self.exported_symbols;
|
||||
let tcx = self.tcx;
|
||||
self.lazy_seq(reachable.iter().map(|&id| tcx.map.local_def_id(id).index))
|
||||
self.lazy_seq(exported_symbols.iter().map(|&id| tcx.map.local_def_id(id).index))
|
||||
}
|
||||
|
||||
fn encode_dylib_dependency_formats(&mut self) -> LazySeq<Option<LinkagePreference>> {
|
||||
@ -1278,10 +1278,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
||||
let impls = self.encode_impls();
|
||||
let impl_bytes = self.position() - i;
|
||||
|
||||
// Encode reachability info.
|
||||
// Encode exported symbols info.
|
||||
i = self.position();
|
||||
let reachable_ids = self.encode_reachable();
|
||||
let reachable_bytes = self.position() - i;
|
||||
let exported_symbols = self.encode_exported_symbols();
|
||||
let exported_symbols_bytes = self.position() - i;
|
||||
|
||||
// Encode and index the items.
|
||||
i = self.position();
|
||||
@ -1319,7 +1319,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
||||
native_libraries: native_libraries,
|
||||
codemap: codemap,
|
||||
impls: impls,
|
||||
reachable_ids: reachable_ids,
|
||||
exported_symbols: exported_symbols,
|
||||
index: index,
|
||||
});
|
||||
|
||||
@ -1339,7 +1339,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
||||
println!(" native bytes: {}", native_lib_bytes);
|
||||
println!(" codemap bytes: {}", codemap_bytes);
|
||||
println!(" impl bytes: {}", impl_bytes);
|
||||
println!(" reachable bytes: {}", reachable_bytes);
|
||||
println!(" exp. symbols bytes: {}", exported_symbols_bytes);
|
||||
println!(" item bytes: {}", item_bytes);
|
||||
println!(" index bytes: {}", index_bytes);
|
||||
println!(" zero bytes: {}", zero_bytes);
|
||||
@ -1377,7 +1377,7 @@ pub fn encode_metadata<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
cstore: &cstore::CStore,
|
||||
reexports: &def::ExportMap,
|
||||
link_meta: &LinkMeta,
|
||||
reachable: &NodeSet)
|
||||
exported_symbols: &NodeSet)
|
||||
-> Vec<u8> {
|
||||
let mut cursor = Cursor::new(vec![]);
|
||||
cursor.write_all(METADATA_HEADER).unwrap();
|
||||
@ -1392,7 +1392,7 @@ pub fn encode_metadata<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
reexports: reexports,
|
||||
link_meta: link_meta,
|
||||
cstore: cstore,
|
||||
reachable: reachable,
|
||||
exported_symbols: exported_symbols,
|
||||
lazy_state: LazyState::NoNode,
|
||||
type_shorthands: Default::default(),
|
||||
predicate_shorthands: Default::default(),
|
||||
|
@ -180,7 +180,7 @@ pub struct CrateRoot {
|
||||
pub native_libraries: LazySeq<NativeLibrary>,
|
||||
pub codemap: LazySeq<syntax_pos::FileMap>,
|
||||
pub impls: LazySeq<TraitImpls>,
|
||||
pub reachable_ids: LazySeq<DefIndex>,
|
||||
pub exported_symbols: LazySeq<DefIndex>,
|
||||
pub index: LazySeq<index::Index>,
|
||||
}
|
||||
|
||||
|
@ -17,11 +17,11 @@ use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
|
||||
use context::SharedCrateContext;
|
||||
use monomorphize::Instance;
|
||||
|
||||
use back::archive;
|
||||
use back::symbol_export::{self, ExportedSymbols};
|
||||
use middle::dependency_format::Linkage;
|
||||
use rustc::hir::def_id::CrateNum;
|
||||
use rustc::hir::def_id::{LOCAL_CRATE, CrateNum};
|
||||
use session::Session;
|
||||
use session::config::CrateType;
|
||||
use session::config;
|
||||
@ -34,10 +34,10 @@ pub struct LinkerInfo {
|
||||
|
||||
impl<'a, 'tcx> LinkerInfo {
|
||||
pub fn new(scx: &SharedCrateContext<'a, 'tcx>,
|
||||
reachable: &[String]) -> LinkerInfo {
|
||||
exports: &ExportedSymbols) -> LinkerInfo {
|
||||
LinkerInfo {
|
||||
exports: scx.sess().crate_types.borrow().iter().map(|&c| {
|
||||
(c, exported_symbols(scx, reachable, c))
|
||||
(c, exported_symbols(scx, exports, c))
|
||||
}).collect(),
|
||||
}
|
||||
}
|
||||
@ -253,11 +253,28 @@ impl<'a> Linker for GnuLinker<'a> {
|
||||
let mut arg = OsString::new();
|
||||
let path = tmpdir.join("list");
|
||||
|
||||
if self.sess.target.target.options.is_like_solaris {
|
||||
debug!("EXPORTED SYMBOLS:");
|
||||
|
||||
if self.sess.target.target.options.is_like_osx {
|
||||
// Write a plain, newline-separated list of symbols
|
||||
let res = (|| -> io::Result<()> {
|
||||
let mut f = BufWriter::new(File::create(&path)?);
|
||||
for sym in self.info.exports[&crate_type].iter() {
|
||||
debug!(" _{}", sym);
|
||||
writeln!(f, "_{}", sym)?;
|
||||
}
|
||||
Ok(())
|
||||
})();
|
||||
if let Err(e) = res {
|
||||
self.sess.fatal(&format!("failed to write lib.def file: {}", e));
|
||||
}
|
||||
} else {
|
||||
// Write an LD version script
|
||||
let res = (|| -> io::Result<()> {
|
||||
let mut f = BufWriter::new(File::create(&path)?);
|
||||
writeln!(f, "{{\n global:")?;
|
||||
for sym in self.info.exports[&crate_type].iter() {
|
||||
debug!(" {};", sym);
|
||||
writeln!(f, " {};", sym)?;
|
||||
}
|
||||
writeln!(f, "\n local:\n *;\n}};")?;
|
||||
@ -266,33 +283,17 @@ impl<'a> Linker for GnuLinker<'a> {
|
||||
if let Err(e) = res {
|
||||
self.sess.fatal(&format!("failed to write version script: {}", e));
|
||||
}
|
||||
|
||||
arg.push("-Wl,-M,");
|
||||
arg.push(&path);
|
||||
} else {
|
||||
let prefix = if self.sess.target.target.options.is_like_osx {
|
||||
"_"
|
||||
} else {
|
||||
""
|
||||
};
|
||||
let res = (|| -> io::Result<()> {
|
||||
let mut f = BufWriter::new(File::create(&path)?);
|
||||
for sym in self.info.exports[&crate_type].iter() {
|
||||
writeln!(f, "{}{}", prefix, sym)?;
|
||||
}
|
||||
Ok(())
|
||||
})();
|
||||
if let Err(e) = res {
|
||||
self.sess.fatal(&format!("failed to write lib.def file: {}", e));
|
||||
}
|
||||
if self.sess.target.target.options.is_like_osx {
|
||||
arg.push("-Wl,-exported_symbols_list,");
|
||||
} else {
|
||||
arg.push("-Wl,--retain-symbols-file=");
|
||||
}
|
||||
arg.push(&path);
|
||||
}
|
||||
|
||||
if self.sess.target.target.options.is_like_osx {
|
||||
arg.push("-Wl,-exported_symbols_list,");
|
||||
} else if self.sess.target.target.options.is_like_solaris {
|
||||
arg.push("-Wl,-M,");
|
||||
} else {
|
||||
arg.push("-Wl,--version-script=");
|
||||
}
|
||||
|
||||
arg.push(&path);
|
||||
self.cmd.arg(arg);
|
||||
}
|
||||
|
||||
@ -473,43 +474,29 @@ impl<'a> Linker for MsvcLinker<'a> {
|
||||
}
|
||||
|
||||
fn exported_symbols(scx: &SharedCrateContext,
|
||||
reachable: &[String],
|
||||
exported_symbols: &ExportedSymbols,
|
||||
crate_type: CrateType)
|
||||
-> Vec<String> {
|
||||
// See explanation in GnuLinker::export_symbols, for
|
||||
// why we don't ever need dylib symbols on non-MSVC.
|
||||
if crate_type == CrateType::CrateTypeDylib ||
|
||||
crate_type == CrateType::CrateTypeProcMacro {
|
||||
if !scx.sess().target.target.options.is_like_msvc {
|
||||
return vec![];
|
||||
}
|
||||
}
|
||||
let export_threshold = symbol_export::crate_export_threshold(crate_type);
|
||||
|
||||
let mut symbols = reachable.to_vec();
|
||||
let mut symbols = Vec::new();
|
||||
exported_symbols.for_each_exported_symbol(LOCAL_CRATE, export_threshold, |name, _| {
|
||||
symbols.push(name.to_owned());
|
||||
});
|
||||
|
||||
// If we're producing anything other than a dylib then the `reachable` array
|
||||
// above is the exhaustive set of symbols we should be exporting.
|
||||
//
|
||||
// For dylibs, however, we need to 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.
|
||||
if crate_type != CrateType::CrateTypeDylib {
|
||||
return symbols
|
||||
}
|
||||
|
||||
let cstore = &scx.sess().cstore;
|
||||
let formats = scx.sess().dependency_formats.borrow();
|
||||
let deps = formats[&crate_type].iter();
|
||||
symbols.extend(deps.enumerate().filter_map(|(i, f)| {
|
||||
if *f == Linkage::Static {
|
||||
Some(CrateNum::new(i + 1))
|
||||
} else {
|
||||
None
|
||||
|
||||
for (index, dep_format) in deps.enumerate() {
|
||||
let cnum = CrateNum::new(index + 1);
|
||||
// For each dependency that we are linking to statically ...
|
||||
if *dep_format == Linkage::Static {
|
||||
// ... we add its symbol list to our export list.
|
||||
exported_symbols.for_each_exported_symbol(cnum, export_threshold, |name, _| {
|
||||
symbols.push(name.to_owned());
|
||||
})
|
||||
}
|
||||
}).flat_map(|cnum| {
|
||||
cstore.reachable_ids(cnum)
|
||||
}).map(|did| -> String {
|
||||
Instance::mono(scx, did).symbol_name(scx)
|
||||
}));
|
||||
}
|
||||
|
||||
symbols
|
||||
}
|
||||
|
@ -8,14 +8,16 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use super::link;
|
||||
use super::write;
|
||||
use back::link;
|
||||
use back::write;
|
||||
use back::symbol_export::{self, ExportedSymbols};
|
||||
use rustc::session::{self, config};
|
||||
use llvm;
|
||||
use llvm::archive_ro::ArchiveRO;
|
||||
use llvm::{ModuleRef, TargetMachineRef, True, False};
|
||||
use rustc::util::common::time;
|
||||
use rustc::util::common::path2cstr;
|
||||
use rustc::hir::def_id::LOCAL_CRATE;
|
||||
use back::write::{ModuleConfig, with_llvm_pmb};
|
||||
|
||||
use libc;
|
||||
@ -24,8 +26,23 @@ use flate;
|
||||
use std::ffi::CString;
|
||||
use std::path::Path;
|
||||
|
||||
pub fn run(sess: &session::Session, llmod: ModuleRef,
|
||||
tm: TargetMachineRef, reachable: &[String],
|
||||
pub fn crate_type_allows_lto(crate_type: config::CrateType) -> bool {
|
||||
match crate_type {
|
||||
config::CrateTypeExecutable |
|
||||
config::CrateTypeStaticlib |
|
||||
config::CrateTypeCdylib => true,
|
||||
|
||||
config::CrateTypeDylib |
|
||||
config::CrateTypeRlib |
|
||||
config::CrateTypeMetadata |
|
||||
config::CrateTypeProcMacro => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run(sess: &session::Session,
|
||||
llmod: ModuleRef,
|
||||
tm: TargetMachineRef,
|
||||
exported_symbols: &ExportedSymbols,
|
||||
config: &ModuleConfig,
|
||||
temp_no_opt_bc_filename: &Path) {
|
||||
if sess.opts.cg.prefer_dynamic {
|
||||
@ -38,17 +55,31 @@ pub fn run(sess: &session::Session, llmod: ModuleRef,
|
||||
|
||||
// Make sure we actually can run LTO
|
||||
for crate_type in sess.crate_types.borrow().iter() {
|
||||
match *crate_type {
|
||||
config::CrateTypeExecutable |
|
||||
config::CrateTypeCdylib |
|
||||
config::CrateTypeStaticlib => {}
|
||||
_ => {
|
||||
sess.fatal("lto can only be run for executables and \
|
||||
if !crate_type_allows_lto(*crate_type) {
|
||||
sess.fatal("lto can only be run for executables, cdylibs and \
|
||||
static library outputs");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let export_threshold =
|
||||
symbol_export::crates_export_threshold(&sess.crate_types.borrow()[..]);
|
||||
|
||||
let symbol_filter = &|&(ref name, level): &(String, _)| {
|
||||
if symbol_export::is_below_threshold(level, export_threshold) {
|
||||
let mut bytes = Vec::with_capacity(name.len() + 1);
|
||||
bytes.extend(name.bytes());
|
||||
Some(CString::new(bytes).unwrap())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
let mut symbol_white_list: Vec<CString> = exported_symbols
|
||||
.exported_symbols(LOCAL_CRATE)
|
||||
.iter()
|
||||
.filter_map(symbol_filter)
|
||||
.collect();
|
||||
|
||||
// For each of our upstream dependencies, find the corresponding rlib and
|
||||
// load the bitcode from the archive. Then merge it into the current LLVM
|
||||
// module that we've got.
|
||||
@ -58,6 +89,11 @@ pub fn run(sess: &session::Session, llmod: ModuleRef,
|
||||
return;
|
||||
}
|
||||
|
||||
symbol_white_list.extend(
|
||||
exported_symbols.exported_symbols(cnum)
|
||||
.iter()
|
||||
.filter_map(symbol_filter));
|
||||
|
||||
let archive = ArchiveRO::open(&path).expect("wanted an rlib");
|
||||
let bytecodes = archive.iter().filter_map(|child| {
|
||||
child.ok().and_then(|c| c.name().map(|name| (name, c)))
|
||||
@ -118,11 +154,10 @@ pub fn run(sess: &session::Session, llmod: ModuleRef,
|
||||
}
|
||||
});
|
||||
|
||||
// Internalize everything but the reachable symbols of the current module
|
||||
let cstrs: Vec<CString> = reachable.iter().map(|s| {
|
||||
CString::new(s.clone()).unwrap()
|
||||
}).collect();
|
||||
let arr: Vec<*const libc::c_char> = cstrs.iter().map(|c| c.as_ptr()).collect();
|
||||
// Internalize everything but the exported symbols of the current module
|
||||
let arr: Vec<*const libc::c_char> = symbol_white_list.iter()
|
||||
.map(|c| c.as_ptr())
|
||||
.collect();
|
||||
let ptr = arr.as_ptr();
|
||||
unsafe {
|
||||
llvm::LLVMRustRunRestrictionPass(llmod,
|
||||
|
193
src/librustc_trans/back/symbol_export.rs
Normal file
193
src/librustc_trans/back/symbol_export.rs
Normal file
@ -0,0 +1,193 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
use context::SharedCrateContext;
|
||||
use monomorphize::Instance;
|
||||
use symbol_map::SymbolMap;
|
||||
use util::nodemap::FxHashMap;
|
||||
use rustc::hir::def_id::{DefId, CrateNum, LOCAL_CRATE};
|
||||
use rustc::session::config;
|
||||
use syntax::attr;
|
||||
use trans_item::TransItem;
|
||||
|
||||
/// The SymbolExportLevel of a symbols specifies from which kinds of crates
|
||||
/// the symbol will be exported. `C` symbols will be exported from any
|
||||
/// kind of crate, including cdylibs which export very few things.
|
||||
/// `Rust` will only be exported if the crate produced is a Rust
|
||||
/// dylib.
|
||||
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
|
||||
pub enum SymbolExportLevel {
|
||||
C,
|
||||
Rust,
|
||||
}
|
||||
|
||||
/// The set of symbols exported from each crate in the crate graph.
|
||||
pub struct ExportedSymbols {
|
||||
exports: FxHashMap<CrateNum, Vec<(String, SymbolExportLevel)>>,
|
||||
}
|
||||
|
||||
impl ExportedSymbols {
|
||||
|
||||
pub fn empty() -> ExportedSymbols {
|
||||
ExportedSymbols {
|
||||
exports: FxHashMap(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compute_from<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
|
||||
symbol_map: &SymbolMap<'tcx>)
|
||||
-> ExportedSymbols {
|
||||
let mut local_crate: Vec<_> = scx
|
||||
.exported_symbols()
|
||||
.iter()
|
||||
.map(|&node_id| {
|
||||
scx.tcx().map.local_def_id(node_id)
|
||||
})
|
||||
.map(|def_id| {
|
||||
(symbol_for_def_id(scx, def_id, symbol_map),
|
||||
export_level(scx, def_id))
|
||||
})
|
||||
.collect();
|
||||
|
||||
if scx.sess().entry_fn.borrow().is_some() {
|
||||
local_crate.push(("main".to_string(), SymbolExportLevel::C));
|
||||
}
|
||||
|
||||
if let Some(id) = scx.sess().derive_registrar_fn.get() {
|
||||
let svh = &scx.link_meta().crate_hash;
|
||||
let def_id = scx.tcx().map.local_def_id(id);
|
||||
let idx = def_id.index;
|
||||
let registrar = scx.sess().generate_derive_registrar_symbol(svh, idx);
|
||||
local_crate.push((registrar, SymbolExportLevel::C));
|
||||
}
|
||||
|
||||
if scx.sess().crate_types.borrow().contains(&config::CrateTypeDylib) {
|
||||
local_crate.push((scx.metadata_symbol_name(),
|
||||
SymbolExportLevel::Rust));
|
||||
}
|
||||
|
||||
let mut exports = FxHashMap();
|
||||
exports.insert(LOCAL_CRATE, local_crate);
|
||||
|
||||
for cnum in scx.sess().cstore.crates() {
|
||||
debug_assert!(cnum != LOCAL_CRATE);
|
||||
|
||||
if scx.sess().cstore.plugin_registrar_fn(cnum).is_some() ||
|
||||
scx.sess().cstore.derive_registrar_fn(cnum).is_some() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let crate_exports = scx
|
||||
.sess()
|
||||
.cstore
|
||||
.exported_symbols(cnum)
|
||||
.iter()
|
||||
.map(|&def_id| {
|
||||
debug!("EXTERN-SYMBOL: {:?}", def_id);
|
||||
let name = Instance::mono(scx, def_id).symbol_name(scx);
|
||||
(name, export_level(scx, def_id))
|
||||
})
|
||||
.collect();
|
||||
|
||||
exports.insert(cnum, crate_exports);
|
||||
}
|
||||
|
||||
return ExportedSymbols {
|
||||
exports: exports
|
||||
};
|
||||
|
||||
fn export_level(scx: &SharedCrateContext,
|
||||
sym_def_id: DefId)
|
||||
-> SymbolExportLevel {
|
||||
let attrs = scx.tcx().get_attrs(sym_def_id);
|
||||
if attr::contains_extern_indicator(scx.sess().diagnostic(), &attrs) {
|
||||
SymbolExportLevel::C
|
||||
} else {
|
||||
SymbolExportLevel::Rust
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn exported_symbols(&self,
|
||||
cnum: CrateNum)
|
||||
-> &[(String, SymbolExportLevel)] {
|
||||
match self.exports.get(&cnum) {
|
||||
Some(exports) => &exports[..],
|
||||
None => &[]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn for_each_exported_symbol<F>(&self,
|
||||
cnum: CrateNum,
|
||||
export_threshold: SymbolExportLevel,
|
||||
mut f: F)
|
||||
where F: FnMut(&str, SymbolExportLevel)
|
||||
{
|
||||
for &(ref name, export_level) in self.exported_symbols(cnum) {
|
||||
if is_below_threshold(export_level, export_threshold) {
|
||||
f(&name[..], export_level)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn crate_export_threshold(crate_type: config::CrateType)
|
||||
-> SymbolExportLevel {
|
||||
match crate_type {
|
||||
config::CrateTypeExecutable |
|
||||
config::CrateTypeStaticlib |
|
||||
config::CrateTypeProcMacro |
|
||||
config::CrateTypeCdylib => SymbolExportLevel::C,
|
||||
config::CrateTypeRlib |
|
||||
config::CrateTypeMetadata |
|
||||
config::CrateTypeDylib => SymbolExportLevel::Rust,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn crates_export_threshold(crate_types: &[config::CrateType])
|
||||
-> SymbolExportLevel {
|
||||
if crate_types.iter().any(|&crate_type| {
|
||||
crate_export_threshold(crate_type) == SymbolExportLevel::Rust
|
||||
}) {
|
||||
SymbolExportLevel::Rust
|
||||
} else {
|
||||
SymbolExportLevel::C
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_below_threshold(level: SymbolExportLevel,
|
||||
threshold: SymbolExportLevel)
|
||||
-> bool {
|
||||
if threshold == SymbolExportLevel::Rust {
|
||||
// We export everything from Rust dylibs
|
||||
true
|
||||
} else {
|
||||
level == SymbolExportLevel::C
|
||||
}
|
||||
}
|
||||
|
||||
fn symbol_for_def_id<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
|
||||
def_id: DefId,
|
||||
symbol_map: &SymbolMap<'tcx>)
|
||||
-> String {
|
||||
// Just try to look things up in the symbol map. If nothing's there, we
|
||||
// recompute.
|
||||
if let Some(node_id) = scx.tcx().map.as_local_node_id(def_id) {
|
||||
if let Some(sym) = symbol_map.get(TransItem::Static(node_id)) {
|
||||
return sym.to_owned();
|
||||
}
|
||||
}
|
||||
|
||||
let instance = Instance::mono(scx, def_id);
|
||||
|
||||
symbol_map.get(TransItem::Fn(instance))
|
||||
.map(str::to_owned)
|
||||
.unwrap_or_else(|| instance.symbol_name(scx))
|
||||
}
|
@ -10,6 +10,7 @@
|
||||
|
||||
use back::lto;
|
||||
use back::link::{get_linker, remove};
|
||||
use back::symbol_export::ExportedSymbols;
|
||||
use rustc_incremental::{save_trans_partition, in_incr_comp_dir};
|
||||
use session::config::{OutputFilenames, OutputTypes, Passes, SomePasses, AllPasses};
|
||||
use session::Session;
|
||||
@ -328,7 +329,7 @@ impl ModuleConfig {
|
||||
struct CodegenContext<'a> {
|
||||
// Extra resources used for LTO: (sess, reachable). This will be `None`
|
||||
// when running in a worker thread.
|
||||
lto_ctxt: Option<(&'a Session, &'a [String])>,
|
||||
lto_ctxt: Option<(&'a Session, &'a ExportedSymbols)>,
|
||||
// Handler to use for diagnostics produced during codegen.
|
||||
handler: &'a Handler,
|
||||
// LLVM passes added by plugins.
|
||||
@ -343,9 +344,11 @@ struct CodegenContext<'a> {
|
||||
}
|
||||
|
||||
impl<'a> CodegenContext<'a> {
|
||||
fn new_with_session(sess: &'a Session, reachable: &'a [String]) -> CodegenContext<'a> {
|
||||
fn new_with_session(sess: &'a Session,
|
||||
exported_symbols: &'a ExportedSymbols)
|
||||
-> CodegenContext<'a> {
|
||||
CodegenContext {
|
||||
lto_ctxt: Some((sess, reachable)),
|
||||
lto_ctxt: Some((sess, exported_symbols)),
|
||||
handler: sess.diagnostic(),
|
||||
plugin_passes: sess.plugin_llvm_passes.borrow().clone(),
|
||||
remark: sess.opts.cg.remark.clone(),
|
||||
@ -516,14 +519,14 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext,
|
||||
llvm::LLVMDisposePassManager(mpm);
|
||||
|
||||
match cgcx.lto_ctxt {
|
||||
Some((sess, reachable)) if sess.lto() => {
|
||||
Some((sess, exported_symbols)) if sess.lto() => {
|
||||
time(sess.time_passes(), "all lto passes", || {
|
||||
let temp_no_opt_bc_filename =
|
||||
output_names.temp_path_ext("no-opt.lto.bc", module_name);
|
||||
lto::run(sess,
|
||||
llmod,
|
||||
tm,
|
||||
reachable,
|
||||
exported_symbols,
|
||||
&config,
|
||||
&temp_no_opt_bc_filename);
|
||||
});
|
||||
@ -753,7 +756,7 @@ pub fn run_passes(sess: &Session,
|
||||
// potentially create hundreds of them).
|
||||
let num_workers = work_items.len() - 1;
|
||||
if num_workers == 1 {
|
||||
run_work_singlethreaded(sess, &trans.reachable, work_items);
|
||||
run_work_singlethreaded(sess, &trans.exported_symbols, work_items);
|
||||
} else {
|
||||
run_work_multithreaded(sess, work_items, num_workers);
|
||||
}
|
||||
@ -997,9 +1000,9 @@ fn execute_work_item(cgcx: &CodegenContext,
|
||||
}
|
||||
|
||||
fn run_work_singlethreaded(sess: &Session,
|
||||
reachable: &[String],
|
||||
exported_symbols: &ExportedSymbols,
|
||||
work_items: Vec<WorkItem>) {
|
||||
let cgcx = CodegenContext::new_with_session(sess, reachable);
|
||||
let cgcx = CodegenContext::new_with_session(sess, exported_symbols);
|
||||
|
||||
// Since we're running single-threaded, we can pass the session to
|
||||
// the proc, allowing `optimize_and_codegen` to perform LTO.
|
||||
|
@ -33,10 +33,10 @@ use super::ModuleTranslation;
|
||||
use assert_module_sources;
|
||||
use back::link;
|
||||
use back::linker::LinkerInfo;
|
||||
use back::symbol_export::{self, ExportedSymbols};
|
||||
use llvm::{Linkage, ValueRef, Vector, get_param};
|
||||
use llvm;
|
||||
use rustc::hir::def::Def;
|
||||
use rustc::hir::def_id::DefId;
|
||||
use rustc::hir::def_id::{DefId, LOCAL_CRATE};
|
||||
use middle::lang_items::{LangItem, ExchangeMallocFnLangItem, StartFnLangItem};
|
||||
use rustc::ty::subst::Substs;
|
||||
use rustc::traits;
|
||||
@ -84,7 +84,6 @@ use util::nodemap::{NodeSet, FxHashMap, FxHashSet};
|
||||
use arena::TypedArena;
|
||||
use libc::c_uint;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::borrow::Cow;
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::ptr;
|
||||
use std::rc::Rc;
|
||||
@ -1243,7 +1242,7 @@ fn contains_null(s: &str) -> bool {
|
||||
}
|
||||
|
||||
fn write_metadata(cx: &SharedCrateContext,
|
||||
reachable_ids: &NodeSet) -> Vec<u8> {
|
||||
exported_symbols: &NodeSet) -> Vec<u8> {
|
||||
use flate;
|
||||
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord)]
|
||||
@ -1275,7 +1274,7 @@ fn write_metadata(cx: &SharedCrateContext,
|
||||
let metadata = cstore.encode_metadata(cx.tcx(),
|
||||
cx.export_map(),
|
||||
cx.link_meta(),
|
||||
reachable_ids);
|
||||
exported_symbols);
|
||||
if kind == MetadataKind::Uncompressed {
|
||||
return metadata;
|
||||
}
|
||||
@ -1313,16 +1312,23 @@ fn write_metadata(cx: &SharedCrateContext,
|
||||
fn internalize_symbols<'a, 'tcx>(sess: &Session,
|
||||
ccxs: &CrateContextList<'a, 'tcx>,
|
||||
symbol_map: &SymbolMap<'tcx>,
|
||||
reachable: &FxHashSet<&str>) {
|
||||
exported_symbols: &ExportedSymbols) {
|
||||
let export_threshold =
|
||||
symbol_export::crates_export_threshold(&sess.crate_types.borrow()[..]);
|
||||
|
||||
let exported_symbols = exported_symbols
|
||||
.exported_symbols(LOCAL_CRATE)
|
||||
.iter()
|
||||
.filter(|&&(_, export_level)| {
|
||||
symbol_export::is_below_threshold(export_level, export_threshold)
|
||||
})
|
||||
.map(|&(ref name, _)| &name[..])
|
||||
.collect::<FxHashSet<&str>>();
|
||||
|
||||
let scx = ccxs.shared();
|
||||
let tcx = scx.tcx();
|
||||
|
||||
// In incr. comp. mode, we can't necessarily see all refs since we
|
||||
// don't generate LLVM IR for reused modules, so skip this
|
||||
// step. Later we should get smarter.
|
||||
if sess.opts.debugging_opts.incremental.is_some() {
|
||||
return;
|
||||
}
|
||||
let incr_comp = sess.opts.debugging_opts.incremental.is_some();
|
||||
|
||||
// 'unsafe' because we are holding on to CStr's from the LLVM module within
|
||||
// this block.
|
||||
@ -1330,34 +1336,43 @@ fn internalize_symbols<'a, 'tcx>(sess: &Session,
|
||||
let mut referenced_somewhere = FxHashSet();
|
||||
|
||||
// Collect all symbols that need to stay externally visible because they
|
||||
// are referenced via a declaration in some other codegen unit.
|
||||
for ccx in ccxs.iter_need_trans() {
|
||||
for val in iter_globals(ccx.llmod()).chain(iter_functions(ccx.llmod())) {
|
||||
let linkage = llvm::LLVMRustGetLinkage(val);
|
||||
// We only care about external declarations (not definitions)
|
||||
// and available_externally definitions.
|
||||
let is_available_externally = linkage == llvm::Linkage::AvailableExternallyLinkage;
|
||||
let is_decl = llvm::LLVMIsDeclaration(val) != 0;
|
||||
// are referenced via a declaration in some other codegen unit. In
|
||||
// incremental compilation, we don't need to collect. See below for more
|
||||
// information.
|
||||
if !incr_comp {
|
||||
for ccx in ccxs.iter_need_trans() {
|
||||
for val in iter_globals(ccx.llmod()).chain(iter_functions(ccx.llmod())) {
|
||||
let linkage = llvm::LLVMRustGetLinkage(val);
|
||||
// We only care about external declarations (not definitions)
|
||||
// and available_externally definitions.
|
||||
let is_available_externally =
|
||||
linkage == llvm::Linkage::AvailableExternallyLinkage;
|
||||
let is_decl = llvm::LLVMIsDeclaration(val) == llvm::True;
|
||||
|
||||
if is_decl || is_available_externally {
|
||||
let symbol_name = CStr::from_ptr(llvm::LLVMGetValueName(val));
|
||||
referenced_somewhere.insert(symbol_name);
|
||||
if is_decl || is_available_externally {
|
||||
let symbol_name = CStr::from_ptr(llvm::LLVMGetValueName(val));
|
||||
referenced_somewhere.insert(symbol_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Also collect all symbols for which we cannot adjust linkage, because
|
||||
// it is fixed by some directive in the source code (e.g. #[no_mangle]).
|
||||
let linkage_fixed_explicitly: FxHashSet<_> = scx
|
||||
.translation_items()
|
||||
.borrow()
|
||||
.iter()
|
||||
.cloned()
|
||||
.filter(|trans_item|{
|
||||
trans_item.explicit_linkage(tcx).is_some()
|
||||
})
|
||||
.map(|trans_item| symbol_map.get_or_compute(scx, trans_item))
|
||||
.collect();
|
||||
// it is fixed by some directive in the source code.
|
||||
let (locally_defined_symbols, linkage_fixed_explicitly) = {
|
||||
let mut locally_defined_symbols = FxHashSet();
|
||||
let mut linkage_fixed_explicitly = FxHashSet();
|
||||
|
||||
for trans_item in scx.translation_items().borrow().iter() {
|
||||
let symbol_name = symbol_map.get_or_compute(scx, *trans_item);
|
||||
if trans_item.explicit_linkage(tcx).is_some() {
|
||||
linkage_fixed_explicitly.insert(symbol_name.clone());
|
||||
}
|
||||
locally_defined_symbols.insert(symbol_name);
|
||||
}
|
||||
|
||||
(locally_defined_symbols, linkage_fixed_explicitly)
|
||||
};
|
||||
|
||||
// Examine each external definition. If the definition is not used in
|
||||
// any other compilation unit, and is not reachable from other crates,
|
||||
@ -1369,23 +1384,46 @@ fn internalize_symbols<'a, 'tcx>(sess: &Session,
|
||||
let is_externally_visible = (linkage == llvm::Linkage::ExternalLinkage) ||
|
||||
(linkage == llvm::Linkage::LinkOnceODRLinkage) ||
|
||||
(linkage == llvm::Linkage::WeakODRLinkage);
|
||||
let is_definition = llvm::LLVMIsDeclaration(val) == 0;
|
||||
|
||||
// If this is a definition (as opposed to just a declaration)
|
||||
// and externally visible, check if we can internalize it
|
||||
if is_definition && is_externally_visible {
|
||||
let name_cstr = CStr::from_ptr(llvm::LLVMGetValueName(val));
|
||||
let name_str = name_cstr.to_str().unwrap();
|
||||
let name_cow = Cow::Borrowed(name_str);
|
||||
if !is_externally_visible {
|
||||
// This symbol is not visible outside of its codegen unit,
|
||||
// so there is nothing to do for it.
|
||||
continue;
|
||||
}
|
||||
|
||||
let is_referenced_somewhere = referenced_somewhere.contains(&name_cstr);
|
||||
let is_reachable = reachable.contains(&name_str);
|
||||
let has_fixed_linkage = linkage_fixed_explicitly.contains(&name_cow);
|
||||
let name_cstr = CStr::from_ptr(llvm::LLVMGetValueName(val));
|
||||
let name_str = name_cstr.to_str().unwrap();
|
||||
|
||||
if !is_referenced_somewhere && !is_reachable && !has_fixed_linkage {
|
||||
llvm::LLVMRustSetLinkage(val, llvm::Linkage::InternalLinkage);
|
||||
llvm::LLVMSetDLLStorageClass(val,
|
||||
llvm::DLLStorageClass::Default);
|
||||
if exported_symbols.contains(&name_str) {
|
||||
// This symbol is explicitly exported, so we can't
|
||||
// mark it as internal or hidden.
|
||||
continue;
|
||||
}
|
||||
|
||||
let is_declaration = llvm::LLVMIsDeclaration(val) == llvm::True;
|
||||
|
||||
if is_declaration {
|
||||
if locally_defined_symbols.contains(name_str) {
|
||||
// Only mark declarations from the current crate as hidden.
|
||||
// Otherwise we would mark things as hidden that are
|
||||
// imported from other crates or native libraries.
|
||||
llvm::LLVMRustSetVisibility(val, llvm::Visibility::Hidden);
|
||||
}
|
||||
} else {
|
||||
let has_fixed_linkage = linkage_fixed_explicitly.contains(name_str);
|
||||
|
||||
if !has_fixed_linkage {
|
||||
// In incremental compilation mode, we can't be sure that
|
||||
// we saw all references because we don't know what's in
|
||||
// cached compilation units, so we always assume that the
|
||||
// given item has been referenced.
|
||||
if incr_comp || referenced_somewhere.contains(&name_cstr) {
|
||||
llvm::LLVMRustSetVisibility(val, llvm::Visibility::Hidden);
|
||||
} else {
|
||||
llvm::LLVMRustSetLinkage(val, llvm::Linkage::InternalLinkage);
|
||||
}
|
||||
|
||||
llvm::LLVMSetDLLStorageClass(val, llvm::DLLStorageClass::Default);
|
||||
llvm::UnsetComdat(val);
|
||||
}
|
||||
}
|
||||
@ -1481,7 +1519,7 @@ fn iter_functions(llmod: llvm::ModuleRef) -> ValueIter {
|
||||
///
|
||||
/// 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(tcx: TyCtxt, reachable: NodeSet) -> NodeSet {
|
||||
pub fn find_exported_symbols(tcx: TyCtxt, reachable: NodeSet) -> NodeSet {
|
||||
reachable.into_iter().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
|
||||
@ -1535,7 +1573,7 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
let krate = tcx.map.krate();
|
||||
|
||||
let ty::CrateAnalysis { export_map, reachable, name, .. } = analysis;
|
||||
let reachable = filter_reachable_ids(tcx, reachable);
|
||||
let exported_symbols = find_exported_symbols(tcx, reachable);
|
||||
|
||||
let check_overflow = if let Some(v) = tcx.sess.opts.debugging_opts.force_overflow_checks {
|
||||
v
|
||||
@ -1548,11 +1586,11 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
let shared_ccx = SharedCrateContext::new(tcx,
|
||||
export_map,
|
||||
link_meta.clone(),
|
||||
reachable,
|
||||
exported_symbols,
|
||||
check_overflow);
|
||||
// Translate the metadata.
|
||||
let metadata = time(tcx.sess.time_passes(), "write metadata", || {
|
||||
write_metadata(&shared_ccx, shared_ccx.reachable())
|
||||
write_metadata(&shared_ccx, shared_ccx.exported_symbols())
|
||||
});
|
||||
|
||||
let metadata_module = ModuleTranslation {
|
||||
@ -1602,13 +1640,13 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
// Skip crate items and just output metadata in -Z no-trans mode.
|
||||
if tcx.sess.opts.debugging_opts.no_trans ||
|
||||
tcx.sess.crate_types.borrow().iter().all(|ct| ct == &config::CrateTypeMetadata) {
|
||||
let linker_info = LinkerInfo::new(&shared_ccx, &[]);
|
||||
let linker_info = LinkerInfo::new(&shared_ccx, &ExportedSymbols::empty());
|
||||
return CrateTranslation {
|
||||
modules: modules,
|
||||
metadata_module: metadata_module,
|
||||
link: link_meta,
|
||||
metadata: metadata,
|
||||
reachable: vec![],
|
||||
exported_symbols: ExportedSymbols::empty(),
|
||||
no_builtins: no_builtins,
|
||||
linker_info: linker_info,
|
||||
windows_subsystem: None,
|
||||
@ -1688,56 +1726,17 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
}
|
||||
|
||||
let sess = shared_ccx.sess();
|
||||
let mut reachable_symbols = shared_ccx.reachable().iter().map(|&id| {
|
||||
let def_id = shared_ccx.tcx().map.local_def_id(id);
|
||||
symbol_for_def_id(def_id, &shared_ccx, &symbol_map)
|
||||
}).collect::<Vec<_>>();
|
||||
|
||||
if sess.entry_fn.borrow().is_some() {
|
||||
reachable_symbols.push("main".to_string());
|
||||
}
|
||||
|
||||
if sess.crate_types.borrow().contains(&config::CrateTypeDylib) {
|
||||
reachable_symbols.push(shared_ccx.metadata_symbol_name());
|
||||
}
|
||||
|
||||
// For the purposes of LTO or when creating a cdylib, 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 we need to
|
||||
// preserve them.
|
||||
//
|
||||
// Note that this happens even if LTO isn't requested or we're not creating
|
||||
// a cdylib. In those cases, though, we're not even reading the
|
||||
// `reachable_symbols` list later on so it should be ok.
|
||||
for cnum in sess.cstore.crates() {
|
||||
let syms = sess.cstore.reachable_ids(cnum);
|
||||
reachable_symbols.extend(syms.into_iter().filter(|&def_id| {
|
||||
let applicable = match sess.cstore.describe_def(def_id) {
|
||||
Some(Def::Static(..)) => true,
|
||||
Some(Def::Fn(_)) => {
|
||||
shared_ccx.tcx().item_generics(def_id).types.is_empty()
|
||||
}
|
||||
_ => false
|
||||
};
|
||||
|
||||
if applicable {
|
||||
let attrs = shared_ccx.tcx().get_attrs(def_id);
|
||||
attr::contains_extern_indicator(sess.diagnostic(), &attrs)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}).map(|did| {
|
||||
symbol_for_def_id(did, &shared_ccx, &symbol_map)
|
||||
}));
|
||||
}
|
||||
let exported_symbols = ExportedSymbols::compute_from(&shared_ccx,
|
||||
&symbol_map);
|
||||
|
||||
// Now that we have all symbols that are exported from the CGUs of this
|
||||
// crate, we can run the `internalize_symbols` pass.
|
||||
time(shared_ccx.sess().time_passes(), "internalize symbols", || {
|
||||
internalize_symbols(sess,
|
||||
&crate_context_list,
|
||||
&symbol_map,
|
||||
&reachable_symbols.iter()
|
||||
.map(|s| &s[..])
|
||||
.collect())
|
||||
&exported_symbols);
|
||||
});
|
||||
|
||||
if tcx.sess.opts.debugging_opts.print_type_sizes {
|
||||
@ -1749,7 +1748,7 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
create_imps(&crate_context_list);
|
||||
}
|
||||
|
||||
let linker_info = LinkerInfo::new(&shared_ccx, &reachable_symbols);
|
||||
let linker_info = LinkerInfo::new(&shared_ccx, &exported_symbols);
|
||||
|
||||
let subsystem = attr::first_attr_value_str_by_name(&krate.attrs,
|
||||
"windows_subsystem");
|
||||
@ -1767,7 +1766,7 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
metadata_module: metadata_module,
|
||||
link: link_meta,
|
||||
metadata: metadata,
|
||||
reachable: reachable_symbols,
|
||||
exported_symbols: exported_symbols,
|
||||
no_builtins: no_builtins,
|
||||
linker_info: linker_info,
|
||||
windows_subsystem: windows_subsystem,
|
||||
@ -2107,22 +2106,3 @@ fn collect_and_partition_translation_items<'a, 'tcx>(scx: &SharedCrateContext<'a
|
||||
|
||||
(codegen_units, symbol_map)
|
||||
}
|
||||
|
||||
fn symbol_for_def_id<'a, 'tcx>(def_id: DefId,
|
||||
scx: &SharedCrateContext<'a, 'tcx>,
|
||||
symbol_map: &SymbolMap<'tcx>)
|
||||
-> String {
|
||||
// Just try to look things up in the symbol map. If nothing's there, we
|
||||
// recompute.
|
||||
if let Some(node_id) = scx.tcx().map.as_local_node_id(def_id) {
|
||||
if let Some(sym) = symbol_map.get(TransItem::Static(node_id)) {
|
||||
return sym.to_owned();
|
||||
}
|
||||
}
|
||||
|
||||
let instance = Instance::mono(scx, def_id);
|
||||
|
||||
symbol_map.get(TransItem::Fn(instance))
|
||||
.map(str::to_owned)
|
||||
.unwrap_or_else(|| instance.symbol_name(scx))
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ pub struct SharedCrateContext<'a, 'tcx: 'a> {
|
||||
metadata_llcx: ContextRef,
|
||||
|
||||
export_map: ExportMap,
|
||||
reachable: NodeSet,
|
||||
exported_symbols: NodeSet,
|
||||
link_meta: LinkMeta,
|
||||
tcx: TyCtxt<'a, 'tcx, 'tcx>,
|
||||
stats: Stats,
|
||||
@ -437,7 +437,7 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> {
|
||||
pub fn new(tcx: TyCtxt<'b, 'tcx, 'tcx>,
|
||||
export_map: ExportMap,
|
||||
link_meta: LinkMeta,
|
||||
reachable: NodeSet,
|
||||
exported_symbols: NodeSet,
|
||||
check_overflow: bool)
|
||||
-> SharedCrateContext<'b, 'tcx> {
|
||||
let (metadata_llcx, metadata_llmod) = unsafe {
|
||||
@ -454,7 +454,7 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> {
|
||||
// they're not available to be linked against. This poses a few problems
|
||||
// for the compiler, some of which are somewhat fundamental, but we use
|
||||
// the `use_dll_storage_attrs` variable below to attach the `dllexport`
|
||||
// attribute to all LLVM functions that are reachable (e.g. they're
|
||||
// attribute to all LLVM functions that are exported e.g. they're
|
||||
// already tagged with external linkage). This is suboptimal for a few
|
||||
// reasons:
|
||||
//
|
||||
@ -493,7 +493,7 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> {
|
||||
metadata_llmod: metadata_llmod,
|
||||
metadata_llcx: metadata_llcx,
|
||||
export_map: export_map,
|
||||
reachable: reachable,
|
||||
exported_symbols: exported_symbols,
|
||||
link_meta: link_meta,
|
||||
tcx: tcx,
|
||||
stats: Stats {
|
||||
@ -527,8 +527,8 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> {
|
||||
&self.export_map
|
||||
}
|
||||
|
||||
pub fn reachable<'a>(&'a self) -> &'a NodeSet {
|
||||
&self.reachable
|
||||
pub fn exported_symbols<'a>(&'a self) -> &'a NodeSet {
|
||||
&self.exported_symbols
|
||||
}
|
||||
|
||||
pub fn trait_cache(&self) -> &RefCell<DepTrackingMap<TraitSelectionCache<'tcx>>> {
|
||||
@ -768,8 +768,8 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
|
||||
&self.shared.export_map
|
||||
}
|
||||
|
||||
pub fn reachable<'a>(&'a self) -> &'a NodeSet {
|
||||
&self.shared.reachable
|
||||
pub fn exported_symbols<'a>(&'a self) -> &'a NodeSet {
|
||||
&self.shared.exported_symbols
|
||||
}
|
||||
|
||||
pub fn link_meta<'a>(&'a self) -> &'a LinkMeta {
|
||||
|
@ -34,7 +34,7 @@ pub fn is_node_local_to_unit(cx: &CrateContext, node_id: ast::NodeId) -> bool
|
||||
// visible). It might better to use the `exported_items` set from
|
||||
// `driver::CrateAnalysis` in the future, but (atm) this set is not
|
||||
// available in the translation pass.
|
||||
!cx.reachable().contains(&node_id)
|
||||
!cx.exported_symbols().contains(&node_id)
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
|
@ -78,7 +78,7 @@ fn declare_raw_fn(ccx: &CrateContext, name: &str, callconv: llvm::CallConv, ty:
|
||||
// don't want the symbols to get exported.
|
||||
if attr::contains_name(ccx.tcx().map.krate_attrs(), "compiler_builtins") {
|
||||
unsafe {
|
||||
llvm::LLVMSetVisibility(llfn, llvm::Visibility::Hidden);
|
||||
llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -78,6 +78,7 @@ pub mod back {
|
||||
pub mod linker;
|
||||
pub mod link;
|
||||
pub mod lto;
|
||||
pub mod symbol_export;
|
||||
pub mod symbol_names;
|
||||
pub mod write;
|
||||
pub mod msvc;
|
||||
@ -169,7 +170,7 @@ pub struct CrateTranslation {
|
||||
pub metadata_module: ModuleTranslation,
|
||||
pub link: middle::cstore::LinkMeta,
|
||||
pub metadata: Vec<u8>,
|
||||
pub reachable: Vec<String>,
|
||||
pub exported_symbols: back::symbol_export::ExportedSymbols,
|
||||
pub no_builtins: bool,
|
||||
pub windows_subsystem: Option<String>,
|
||||
pub linker_info: back::linker::LinkerInfo
|
||||
|
@ -1408,3 +1408,45 @@ extern "C" void LLVMRustSetLinkage(LLVMValueRef V, LLVMRustLinkage RustLinkage)
|
||||
extern "C" LLVMContextRef LLVMRustGetValueContext(LLVMValueRef V) {
|
||||
return wrap(&unwrap(V)->getContext());
|
||||
}
|
||||
|
||||
enum class LLVMRustVisibility {
|
||||
Default = 0,
|
||||
Hidden = 1,
|
||||
Protected = 2,
|
||||
};
|
||||
|
||||
static LLVMRustVisibility to_rust(LLVMVisibility vis) {
|
||||
switch (vis) {
|
||||
case LLVMDefaultVisibility:
|
||||
return LLVMRustVisibility::Default;
|
||||
case LLVMHiddenVisibility:
|
||||
return LLVMRustVisibility::Hidden;
|
||||
case LLVMProtectedVisibility:
|
||||
return LLVMRustVisibility::Protected;
|
||||
|
||||
default:
|
||||
llvm_unreachable("Invalid LLVMRustVisibility value!");
|
||||
}
|
||||
}
|
||||
|
||||
static LLVMVisibility from_rust(LLVMRustVisibility vis) {
|
||||
switch (vis) {
|
||||
case LLVMRustVisibility::Default:
|
||||
return LLVMDefaultVisibility;
|
||||
case LLVMRustVisibility::Hidden:
|
||||
return LLVMHiddenVisibility;
|
||||
case LLVMRustVisibility::Protected:
|
||||
return LLVMProtectedVisibility;
|
||||
|
||||
default:
|
||||
llvm_unreachable("Invalid LLVMRustVisibility value!");
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" LLVMRustVisibility LLVMRustGetVisibility(LLVMValueRef V) {
|
||||
return to_rust(LLVMGetVisibility(V));
|
||||
}
|
||||
|
||||
extern "C" void LLVMRustSetVisibility(LLVMValueRef V, LLVMRustVisibility RustVisibility) {
|
||||
LLVMSetVisibility(V, from_rust(RustVisibility));
|
||||
}
|
||||
|
@ -10,5 +10,5 @@ all:
|
||||
$(RUSTC) foo.rs --emit=llvm-ir -C codegen-units=3
|
||||
[ "$$(cat "$(TMPDIR)"/foo.?.ll | grep -c define\ i32\ .*inlined)" -eq "0" ]
|
||||
[ "$$(cat "$(TMPDIR)"/foo.?.ll | grep -c define\ internal\ i32\ .*inlined)" -eq "2" ]
|
||||
[ "$$(cat "$(TMPDIR)"/foo.?.ll | grep -c define\ i32\ .*normal)" -eq "1" ]
|
||||
[ "$$(cat "$(TMPDIR)"/foo.?.ll | grep -c declare\ i32\ .*normal)" -eq "2" ]
|
||||
[ "$$(cat "$(TMPDIR)"/foo.?.ll | grep -c define\ hidden\ i32\ .*normal)" -eq "1" ]
|
||||
[ "$$(cat "$(TMPDIR)"/foo.?.ll | grep -c declare\ hidden\ i32\ .*normal)" -eq "2" ]
|
||||
|
50
src/test/run-make/symbol-visibility/Makefile
Normal file
50
src/test/run-make/symbol-visibility/Makefile
Normal file
@ -0,0 +1,50 @@
|
||||
include ../tools.mk
|
||||
|
||||
ifdef IS_WINDOWS
|
||||
# Do nothing on MSVC.
|
||||
# On MINGW the --version-script, --dynamic-list, and --retain-symbol args don't
|
||||
# seem to work reliably.
|
||||
all:
|
||||
exit 0
|
||||
else
|
||||
|
||||
NM=nm -D
|
||||
DYLIB_EXT=so
|
||||
CDYLIB_NAME=liba_cdylib.so
|
||||
RDYLIB_NAME=liba_rust_dylib.so
|
||||
EXE_NAME=an_executable
|
||||
|
||||
ifeq ($(UNAME),Darwin)
|
||||
NM=nm -gU
|
||||
DYLIB_EXT=dylib
|
||||
CDYLIB_NAME=liba_cdylib.dylib
|
||||
RDYLIB_NAME=liba_rust_dylib.dylib
|
||||
EXE_NAME=an_executable
|
||||
endif
|
||||
|
||||
all:
|
||||
$(RUSTC) an_rlib.rs
|
||||
$(RUSTC) a_cdylib.rs
|
||||
$(RUSTC) a_rust_dylib.rs
|
||||
$(RUSTC) an_executable.rs
|
||||
|
||||
# Check that a cdylib exports its public #[no_mangle] functions
|
||||
[ "$$($(NM) $(TMPDIR)/$(CDYLIB_NAME) | grep -c public_c_function_from_cdylib)" -eq "1" ]
|
||||
# Check that a cdylib exports the public #[no_mangle] functions of dependencies
|
||||
[ "$$($(NM) $(TMPDIR)/$(CDYLIB_NAME) | grep -c public_c_function_from_rlib)" -eq "1" ]
|
||||
# Check that a cdylib DOES NOT export any public Rust functions
|
||||
[ "$$($(NM) $(TMPDIR)/$(CDYLIB_NAME) | grep -c _ZN.*h.*E)" -eq "0" ]
|
||||
|
||||
# Check that a Rust dylib exports its monomorphic functions
|
||||
[ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c public_c_function_from_rust_dylib)" -eq "1" ]
|
||||
[ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c _ZN.*public_rust_function_from_rust_dylib.*E)" -eq "1" ]
|
||||
|
||||
# Check that a Rust dylib exports the monomorphic functions from its dependencies
|
||||
[ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c public_c_function_from_rlib)" -eq "1" ]
|
||||
[ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c public_rust_function_from_rlib)" -eq "1" ]
|
||||
|
||||
# Check that an executable does not export any dynamic symbols
|
||||
[ "$$($(NM) $(TMPDIR)/$(EXE_NAME) | grep -c public_c_function_from_rlib)" -eq "0" ]
|
||||
[ "$$($(NM) $(TMPDIR)/$(EXE_NAME) | grep -c public_rust_function_from_exe)" -eq "0" ]
|
||||
|
||||
endif
|
22
src/test/run-make/symbol-visibility/a_cdylib.rs
Normal file
22
src/test/run-make/symbol-visibility/a_cdylib.rs
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
#![crate_type="cdylib"]
|
||||
|
||||
extern crate an_rlib;
|
||||
|
||||
// This should not be exported
|
||||
pub fn public_rust_function_from_cdylib() {}
|
||||
|
||||
// This should be exported
|
||||
#[no_mangle]
|
||||
pub extern "C" fn public_c_function_from_cdylib() {
|
||||
an_rlib::public_c_function_from_rlib();
|
||||
}
|
20
src/test/run-make/symbol-visibility/a_rust_dylib.rs
Normal file
20
src/test/run-make/symbol-visibility/a_rust_dylib.rs
Normal file
@ -0,0 +1,20 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
#![crate_type="dylib"]
|
||||
|
||||
extern crate an_rlib;
|
||||
|
||||
// This should be exported
|
||||
pub fn public_rust_function_from_rust_dylib() {}
|
||||
|
||||
// This should be exported
|
||||
#[no_mangle]
|
||||
pub extern "C" fn public_c_function_from_rust_dylib() {}
|
17
src/test/run-make/symbol-visibility/an_executable.rs
Normal file
17
src/test/run-make/symbol-visibility/an_executable.rs
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
#![crate_type="bin"]
|
||||
|
||||
extern crate an_rlib;
|
||||
|
||||
pub fn public_rust_function_from_exe() {}
|
||||
|
||||
fn main() {}
|
16
src/test/run-make/symbol-visibility/an_rlib.rs
Normal file
16
src/test/run-make/symbol-visibility/an_rlib.rs
Normal file
@ -0,0 +1,16 @@
|
||||
// Copyright 2016 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.
|
||||
|
||||
#![crate_type="rlib"]
|
||||
|
||||
pub fn public_rust_function_from_rlib() {}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn public_c_function_from_rlib() {}
|
Loading…
Reference in New Issue
Block a user