Skip to content

Commit daf8c1d

Browse files
committedDec 5, 2016
Auto merge of #38117 - michaelwoerister:hidden-symbols, r=alexcrichton
Improve symbol visibility handling for dynamic libraries. This will hopefully fix issue #37530 and maybe also #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
2 parents 06b8d1d + 8ecdc4e commit daf8c1d

File tree

22 files changed

+623
-243
lines changed

22 files changed

+623
-243
lines changed
 

‎src/librustc/middle/cstore.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -328,8 +328,9 @@ pub trait CrateStore<'tcx> {
328328
fn crate_hash(&self, cnum: CrateNum) -> Svh;
329329
fn crate_disambiguator(&self, cnum: CrateNum) -> Symbol;
330330
fn plugin_registrar_fn(&self, cnum: CrateNum) -> Option<DefId>;
331+
fn derive_registrar_fn(&self, cnum: CrateNum) -> Option<DefId>;
331332
fn native_libraries(&self, cnum: CrateNum) -> Vec<NativeLibrary>;
332-
fn reachable_ids(&self, cnum: CrateNum) -> Vec<DefId>;
333+
fn exported_symbols(&self, cnum: CrateNum) -> Vec<DefId>;
333334
fn is_no_builtins(&self, cnum: CrateNum) -> bool;
334335

335336
// resolve
@@ -491,9 +492,11 @@ impl<'tcx> CrateStore<'tcx> for DummyCrateStore {
491492
-> Symbol { bug!("crate_disambiguator") }
492493
fn plugin_registrar_fn(&self, cnum: CrateNum) -> Option<DefId>
493494
{ bug!("plugin_registrar_fn") }
495+
fn derive_registrar_fn(&self, cnum: CrateNum) -> Option<DefId>
496+
{ bug!("derive_registrar_fn") }
494497
fn native_libraries(&self, cnum: CrateNum) -> Vec<NativeLibrary>
495498
{ bug!("native_libraries") }
496-
fn reachable_ids(&self, cnum: CrateNum) -> Vec<DefId> { bug!("reachable_ids") }
499+
fn exported_symbols(&self, cnum: CrateNum) -> Vec<DefId> { bug!("exported_symbols") }
497500
fn is_no_builtins(&self, cnum: CrateNum) -> bool { bug!("is_no_builtins") }
498501

499502
// resolve

‎src/librustc_llvm/ffi.rs

+11-8
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,15 @@ pub enum Linkage {
6464
CommonLinkage = 10,
6565
}
6666

67+
// LLVMRustVisibility
68+
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
69+
#[repr(C)]
70+
pub enum Visibility {
71+
Default = 0,
72+
Hidden = 1,
73+
Protected = 2,
74+
}
75+
6776
/// LLVMDiagnosticSeverity
6877
#[derive(Copy, Clone, Debug)]
6978
#[repr(C)]
@@ -399,13 +408,6 @@ pub type OperandBundleDefRef = *mut OperandBundleDef_opaque;
399408
pub type DiagnosticHandler = unsafe extern "C" fn(DiagnosticInfoRef, *mut c_void);
400409
pub type InlineAsmDiagHandler = unsafe extern "C" fn(SMDiagnosticRef, *const c_void, c_uint);
401410

402-
/// LLVMVisibility
403-
#[repr(C)]
404-
pub enum Visibility {
405-
Default,
406-
Hidden,
407-
Protected,
408-
}
409411

410412
pub mod debuginfo {
411413
use super::MetadataRef;
@@ -655,7 +657,8 @@ extern "C" {
655657
pub fn LLVMRustSetLinkage(Global: ValueRef, RustLinkage: Linkage);
656658
pub fn LLVMGetSection(Global: ValueRef) -> *const c_char;
657659
pub fn LLVMSetSection(Global: ValueRef, Section: *const c_char);
658-
pub fn LLVMSetVisibility(Global: ValueRef, Viz: Visibility);
660+
pub fn LLVMRustGetVisibility(Global: ValueRef) -> Visibility;
661+
pub fn LLVMRustSetVisibility(Global: ValueRef, Viz: Visibility);
659662
pub fn LLVMGetAlignment(Global: ValueRef) -> c_uint;
660663
pub fn LLVMSetAlignment(Global: ValueRef, Bytes: c_uint);
661664
pub fn LLVMSetDLLStorageClass(V: ValueRef, C: DLLStorageClass);

‎src/librustc_metadata/cstore_impl.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -306,14 +306,22 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore {
306306
})
307307
}
308308

309+
fn derive_registrar_fn(&self, cnum: CrateNum) -> Option<DefId>
310+
{
311+
self.get_crate_data(cnum).root.macro_derive_registrar.map(|index| DefId {
312+
krate: cnum,
313+
index: index
314+
})
315+
}
316+
309317
fn native_libraries(&self, cnum: CrateNum) -> Vec<NativeLibrary>
310318
{
311319
self.get_crate_data(cnum).get_native_libraries()
312320
}
313321

314-
fn reachable_ids(&self, cnum: CrateNum) -> Vec<DefId>
322+
fn exported_symbols(&self, cnum: CrateNum) -> Vec<DefId>
315323
{
316-
self.get_crate_data(cnum).get_reachable_ids()
324+
self.get_crate_data(cnum).get_exported_symbols()
317325
}
318326

319327
fn is_no_builtins(&self, cnum: CrateNum) -> bool {

‎src/librustc_metadata/decoder.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1038,8 +1038,8 @@ impl<'a, 'tcx> CrateMetadata {
10381038
arg_names.decode(self).collect()
10391039
}
10401040

1041-
pub fn get_reachable_ids(&self) -> Vec<DefId> {
1042-
self.root.reachable_ids.decode(self).map(|index| self.local_def_id(index)).collect()
1041+
pub fn get_exported_symbols(&self) -> Vec<DefId> {
1042+
self.root.exported_symbols.decode(self).map(|index| self.local_def_id(index)).collect()
10431043
}
10441044

10451045
pub fn get_macro(&self, id: DefIndex) -> (ast::Name, MacroDef) {

‎src/librustc_metadata/encoder.rs

+12-12
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ pub struct EncodeContext<'a, 'tcx: 'a> {
5050
reexports: &'a def::ExportMap,
5151
link_meta: &'a LinkMeta,
5252
cstore: &'a cstore::CStore,
53-
reachable: &'a NodeSet,
53+
exported_symbols: &'a NodeSet,
5454

5555
lazy_state: LazyState,
5656
type_shorthands: FxHashMap<Ty<'tcx>, usize>,
@@ -1223,16 +1223,16 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
12231223
self.lazy_seq(all_impls)
12241224
}
12251225

1226-
// Encodes all reachable symbols in this crate into the metadata.
1226+
// Encodes all symbols exported from this crate into the metadata.
12271227
//
12281228
// This pass is seeded off the reachability list calculated in the
12291229
// middle::reachable module but filters out items that either don't have a
12301230
// symbol associated with them (they weren't translated) or if they're an FFI
12311231
// definition (as that's not defined in this crate).
1232-
fn encode_reachable(&mut self) -> LazySeq<DefIndex> {
1233-
let reachable = self.reachable;
1232+
fn encode_exported_symbols(&mut self) -> LazySeq<DefIndex> {
1233+
let exported_symbols = self.exported_symbols;
12341234
let tcx = self.tcx;
1235-
self.lazy_seq(reachable.iter().map(|&id| tcx.map.local_def_id(id).index))
1235+
self.lazy_seq(exported_symbols.iter().map(|&id| tcx.map.local_def_id(id).index))
12361236
}
12371237

12381238
fn encode_dylib_dependency_formats(&mut self) -> LazySeq<Option<LinkagePreference>> {
@@ -1278,10 +1278,10 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
12781278
let impls = self.encode_impls();
12791279
let impl_bytes = self.position() - i;
12801280

1281-
// Encode reachability info.
1281+
// Encode exported symbols info.
12821282
i = self.position();
1283-
let reachable_ids = self.encode_reachable();
1284-
let reachable_bytes = self.position() - i;
1283+
let exported_symbols = self.encode_exported_symbols();
1284+
let exported_symbols_bytes = self.position() - i;
12851285

12861286
// Encode and index the items.
12871287
i = self.position();
@@ -1319,7 +1319,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
13191319
native_libraries: native_libraries,
13201320
codemap: codemap,
13211321
impls: impls,
1322-
reachable_ids: reachable_ids,
1322+
exported_symbols: exported_symbols,
13231323
index: index,
13241324
});
13251325

@@ -1339,7 +1339,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
13391339
println!(" native bytes: {}", native_lib_bytes);
13401340
println!(" codemap bytes: {}", codemap_bytes);
13411341
println!(" impl bytes: {}", impl_bytes);
1342-
println!(" reachable bytes: {}", reachable_bytes);
1342+
println!(" exp. symbols bytes: {}", exported_symbols_bytes);
13431343
println!(" item bytes: {}", item_bytes);
13441344
println!(" index bytes: {}", index_bytes);
13451345
println!(" zero bytes: {}", zero_bytes);
@@ -1377,7 +1377,7 @@ pub fn encode_metadata<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
13771377
cstore: &cstore::CStore,
13781378
reexports: &def::ExportMap,
13791379
link_meta: &LinkMeta,
1380-
reachable: &NodeSet)
1380+
exported_symbols: &NodeSet)
13811381
-> Vec<u8> {
13821382
let mut cursor = Cursor::new(vec![]);
13831383
cursor.write_all(METADATA_HEADER).unwrap();
@@ -1392,7 +1392,7 @@ pub fn encode_metadata<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
13921392
reexports: reexports,
13931393
link_meta: link_meta,
13941394
cstore: cstore,
1395-
reachable: reachable,
1395+
exported_symbols: exported_symbols,
13961396
lazy_state: LazyState::NoNode,
13971397
type_shorthands: Default::default(),
13981398
predicate_shorthands: Default::default(),

‎src/librustc_metadata/schema.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ pub struct CrateRoot {
180180
pub native_libraries: LazySeq<NativeLibrary>,
181181
pub codemap: LazySeq<syntax_pos::FileMap>,
182182
pub impls: LazySeq<TraitImpls>,
183-
pub reachable_ids: LazySeq<DefIndex>,
183+
pub exported_symbols: LazySeq<DefIndex>,
184184
pub index: LazySeq<index::Index>,
185185
}
186186

‎src/librustc_trans/back/linker.rs

+43-56
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ use std::path::{Path, PathBuf};
1717
use std::process::Command;
1818

1919
use context::SharedCrateContext;
20-
use monomorphize::Instance;
2120

2221
use back::archive;
22+
use back::symbol_export::{self, ExportedSymbols};
2323
use middle::dependency_format::Linkage;
24-
use rustc::hir::def_id::CrateNum;
24+
use rustc::hir::def_id::{LOCAL_CRATE, CrateNum};
2525
use session::Session;
2626
use session::config::CrateType;
2727
use session::config;
@@ -34,10 +34,10 @@ pub struct LinkerInfo {
3434

3535
impl<'a, 'tcx> LinkerInfo {
3636
pub fn new(scx: &SharedCrateContext<'a, 'tcx>,
37-
reachable: &[String]) -> LinkerInfo {
37+
exports: &ExportedSymbols) -> LinkerInfo {
3838
LinkerInfo {
3939
exports: scx.sess().crate_types.borrow().iter().map(|&c| {
40-
(c, exported_symbols(scx, reachable, c))
40+
(c, exported_symbols(scx, exports, c))
4141
}).collect(),
4242
}
4343
}
@@ -253,46 +253,47 @@ impl<'a> Linker for GnuLinker<'a> {
253253
let mut arg = OsString::new();
254254
let path = tmpdir.join("list");
255255

256-
if self.sess.target.target.options.is_like_solaris {
256+
debug!("EXPORTED SYMBOLS:");
257+
258+
if self.sess.target.target.options.is_like_osx {
259+
// Write a plain, newline-separated list of symbols
257260
let res = (|| -> io::Result<()> {
258261
let mut f = BufWriter::new(File::create(&path)?);
259-
writeln!(f, "{{\n global:")?;
260262
for sym in self.info.exports[&crate_type].iter() {
261-
writeln!(f, " {};", sym)?;
263+
debug!(" _{}", sym);
264+
writeln!(f, "_{}", sym)?;
262265
}
263-
writeln!(f, "\n local:\n *;\n}};")?;
264266
Ok(())
265267
})();
266268
if let Err(e) = res {
267-
self.sess.fatal(&format!("failed to write version script: {}", e));
269+
self.sess.fatal(&format!("failed to write lib.def file: {}", e));
268270
}
269-
270-
arg.push("-Wl,-M,");
271-
arg.push(&path);
272271
} else {
273-
let prefix = if self.sess.target.target.options.is_like_osx {
274-
"_"
275-
} else {
276-
""
277-
};
272+
// Write an LD version script
278273
let res = (|| -> io::Result<()> {
279274
let mut f = BufWriter::new(File::create(&path)?);
275+
writeln!(f, "{{\n global:")?;
280276
for sym in self.info.exports[&crate_type].iter() {
281-
writeln!(f, "{}{}", prefix, sym)?;
277+
debug!(" {};", sym);
278+
writeln!(f, " {};", sym)?;
282279
}
280+
writeln!(f, "\n local:\n *;\n}};")?;
283281
Ok(())
284282
})();
285283
if let Err(e) = res {
286-
self.sess.fatal(&format!("failed to write lib.def file: {}", e));
287-
}
288-
if self.sess.target.target.options.is_like_osx {
289-
arg.push("-Wl,-exported_symbols_list,");
290-
} else {
291-
arg.push("-Wl,--retain-symbols-file=");
284+
self.sess.fatal(&format!("failed to write version script: {}", e));
292285
}
293-
arg.push(&path);
294286
}
295287

288+
if self.sess.target.target.options.is_like_osx {
289+
arg.push("-Wl,-exported_symbols_list,");
290+
} else if self.sess.target.target.options.is_like_solaris {
291+
arg.push("-Wl,-M,");
292+
} else {
293+
arg.push("-Wl,--version-script=");
294+
}
295+
296+
arg.push(&path);
296297
self.cmd.arg(arg);
297298
}
298299

@@ -473,43 +474,29 @@ impl<'a> Linker for MsvcLinker<'a> {
473474
}
474475

475476
fn exported_symbols(scx: &SharedCrateContext,
476-
reachable: &[String],
477+
exported_symbols: &ExportedSymbols,
477478
crate_type: CrateType)
478479
-> Vec<String> {
479-
// See explanation in GnuLinker::export_symbols, for
480-
// why we don't ever need dylib symbols on non-MSVC.
481-
if crate_type == CrateType::CrateTypeDylib ||
482-
crate_type == CrateType::CrateTypeProcMacro {
483-
if !scx.sess().target.target.options.is_like_msvc {
484-
return vec![];
485-
}
486-
}
480+
let export_threshold = symbol_export::crate_export_threshold(crate_type);
487481

488-
let mut symbols = reachable.to_vec();
482+
let mut symbols = Vec::new();
483+
exported_symbols.for_each_exported_symbol(LOCAL_CRATE, export_threshold, |name, _| {
484+
symbols.push(name.to_owned());
485+
});
489486

490-
// If we're producing anything other than a dylib then the `reachable` array
491-
// above is the exhaustive set of symbols we should be exporting.
492-
//
493-
// For dylibs, however, we need to take a look at how all upstream crates
494-
// are linked into this dynamic library. For all statically linked
495-
// libraries we take all their reachable symbols and emit them as well.
496-
if crate_type != CrateType::CrateTypeDylib {
497-
return symbols
498-
}
499-
500-
let cstore = &scx.sess().cstore;
501487
let formats = scx.sess().dependency_formats.borrow();
502488
let deps = formats[&crate_type].iter();
503-
symbols.extend(deps.enumerate().filter_map(|(i, f)| {
504-
if *f == Linkage::Static {
505-
Some(CrateNum::new(i + 1))
506-
} else {
507-
None
489+
490+
for (index, dep_format) in deps.enumerate() {
491+
let cnum = CrateNum::new(index + 1);
492+
// For each dependency that we are linking to statically ...
493+
if *dep_format == Linkage::Static {
494+
// ... we add its symbol list to our export list.
495+
exported_symbols.for_each_exported_symbol(cnum, export_threshold, |name, _| {
496+
symbols.push(name.to_owned());
497+
})
508498
}
509-
}).flat_map(|cnum| {
510-
cstore.reachable_ids(cnum)
511-
}).map(|did| -> String {
512-
Instance::mono(scx, did).symbol_name(scx)
513-
}));
499+
}
500+
514501
symbols
515502
}

‎src/librustc_trans/back/lto.rs

+51-16
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,16 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
use super::link;
12-
use super::write;
11+
use back::link;
12+
use back::write;
13+
use back::symbol_export::{self, ExportedSymbols};
1314
use rustc::session::{self, config};
1415
use llvm;
1516
use llvm::archive_ro::ArchiveRO;
1617
use llvm::{ModuleRef, TargetMachineRef, True, False};
1718
use rustc::util::common::time;
1819
use rustc::util::common::path2cstr;
20+
use rustc::hir::def_id::LOCAL_CRATE;
1921
use back::write::{ModuleConfig, with_llvm_pmb};
2022

2123
use libc;
@@ -24,8 +26,23 @@ use flate;
2426
use std::ffi::CString;
2527
use std::path::Path;
2628

27-
pub fn run(sess: &session::Session, llmod: ModuleRef,
28-
tm: TargetMachineRef, reachable: &[String],
29+
pub fn crate_type_allows_lto(crate_type: config::CrateType) -> bool {
30+
match crate_type {
31+
config::CrateTypeExecutable |
32+
config::CrateTypeStaticlib |
33+
config::CrateTypeCdylib => true,
34+
35+
config::CrateTypeDylib |
36+
config::CrateTypeRlib |
37+
config::CrateTypeMetadata |
38+
config::CrateTypeProcMacro => false,
39+
}
40+
}
41+
42+
pub fn run(sess: &session::Session,
43+
llmod: ModuleRef,
44+
tm: TargetMachineRef,
45+
exported_symbols: &ExportedSymbols,
2946
config: &ModuleConfig,
3047
temp_no_opt_bc_filename: &Path) {
3148
if sess.opts.cg.prefer_dynamic {
@@ -38,17 +55,31 @@ pub fn run(sess: &session::Session, llmod: ModuleRef,
3855

3956
// Make sure we actually can run LTO
4057
for crate_type in sess.crate_types.borrow().iter() {
41-
match *crate_type {
42-
config::CrateTypeExecutable |
43-
config::CrateTypeCdylib |
44-
config::CrateTypeStaticlib => {}
45-
_ => {
46-
sess.fatal("lto can only be run for executables and \
58+
if !crate_type_allows_lto(*crate_type) {
59+
sess.fatal("lto can only be run for executables, cdylibs and \
4760
static library outputs");
48-
}
4961
}
5062
}
5163

64+
let export_threshold =
65+
symbol_export::crates_export_threshold(&sess.crate_types.borrow()[..]);
66+
67+
let symbol_filter = &|&(ref name, level): &(String, _)| {
68+
if symbol_export::is_below_threshold(level, export_threshold) {
69+
let mut bytes = Vec::with_capacity(name.len() + 1);
70+
bytes.extend(name.bytes());
71+
Some(CString::new(bytes).unwrap())
72+
} else {
73+
None
74+
}
75+
};
76+
77+
let mut symbol_white_list: Vec<CString> = exported_symbols
78+
.exported_symbols(LOCAL_CRATE)
79+
.iter()
80+
.filter_map(symbol_filter)
81+
.collect();
82+
5283
// For each of our upstream dependencies, find the corresponding rlib and
5384
// load the bitcode from the archive. Then merge it into the current LLVM
5485
// module that we've got.
@@ -58,6 +89,11 @@ pub fn run(sess: &session::Session, llmod: ModuleRef,
5889
return;
5990
}
6091

92+
symbol_white_list.extend(
93+
exported_symbols.exported_symbols(cnum)
94+
.iter()
95+
.filter_map(symbol_filter));
96+
6197
let archive = ArchiveRO::open(&path).expect("wanted an rlib");
6298
let bytecodes = archive.iter().filter_map(|child| {
6399
child.ok().and_then(|c| c.name().map(|name| (name, c)))
@@ -118,11 +154,10 @@ pub fn run(sess: &session::Session, llmod: ModuleRef,
118154
}
119155
});
120156

121-
// Internalize everything but the reachable symbols of the current module
122-
let cstrs: Vec<CString> = reachable.iter().map(|s| {
123-
CString::new(s.clone()).unwrap()
124-
}).collect();
125-
let arr: Vec<*const libc::c_char> = cstrs.iter().map(|c| c.as_ptr()).collect();
157+
// Internalize everything but the exported symbols of the current module
158+
let arr: Vec<*const libc::c_char> = symbol_white_list.iter()
159+
.map(|c| c.as_ptr())
160+
.collect();
126161
let ptr = arr.as_ptr();
127162
unsafe {
128163
llvm::LLVMRustRunRestrictionPass(llmod,
+193
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
use context::SharedCrateContext;
12+
use monomorphize::Instance;
13+
use symbol_map::SymbolMap;
14+
use util::nodemap::FxHashMap;
15+
use rustc::hir::def_id::{DefId, CrateNum, LOCAL_CRATE};
16+
use rustc::session::config;
17+
use syntax::attr;
18+
use trans_item::TransItem;
19+
20+
/// The SymbolExportLevel of a symbols specifies from which kinds of crates
21+
/// the symbol will be exported. `C` symbols will be exported from any
22+
/// kind of crate, including cdylibs which export very few things.
23+
/// `Rust` will only be exported if the crate produced is a Rust
24+
/// dylib.
25+
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
26+
pub enum SymbolExportLevel {
27+
C,
28+
Rust,
29+
}
30+
31+
/// The set of symbols exported from each crate in the crate graph.
32+
pub struct ExportedSymbols {
33+
exports: FxHashMap<CrateNum, Vec<(String, SymbolExportLevel)>>,
34+
}
35+
36+
impl ExportedSymbols {
37+
38+
pub fn empty() -> ExportedSymbols {
39+
ExportedSymbols {
40+
exports: FxHashMap(),
41+
}
42+
}
43+
44+
pub fn compute_from<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
45+
symbol_map: &SymbolMap<'tcx>)
46+
-> ExportedSymbols {
47+
let mut local_crate: Vec<_> = scx
48+
.exported_symbols()
49+
.iter()
50+
.map(|&node_id| {
51+
scx.tcx().map.local_def_id(node_id)
52+
})
53+
.map(|def_id| {
54+
(symbol_for_def_id(scx, def_id, symbol_map),
55+
export_level(scx, def_id))
56+
})
57+
.collect();
58+
59+
if scx.sess().entry_fn.borrow().is_some() {
60+
local_crate.push(("main".to_string(), SymbolExportLevel::C));
61+
}
62+
63+
if let Some(id) = scx.sess().derive_registrar_fn.get() {
64+
let svh = &scx.link_meta().crate_hash;
65+
let def_id = scx.tcx().map.local_def_id(id);
66+
let idx = def_id.index;
67+
let registrar = scx.sess().generate_derive_registrar_symbol(svh, idx);
68+
local_crate.push((registrar, SymbolExportLevel::C));
69+
}
70+
71+
if scx.sess().crate_types.borrow().contains(&config::CrateTypeDylib) {
72+
local_crate.push((scx.metadata_symbol_name(),
73+
SymbolExportLevel::Rust));
74+
}
75+
76+
let mut exports = FxHashMap();
77+
exports.insert(LOCAL_CRATE, local_crate);
78+
79+
for cnum in scx.sess().cstore.crates() {
80+
debug_assert!(cnum != LOCAL_CRATE);
81+
82+
if scx.sess().cstore.plugin_registrar_fn(cnum).is_some() ||
83+
scx.sess().cstore.derive_registrar_fn(cnum).is_some() {
84+
continue;
85+
}
86+
87+
let crate_exports = scx
88+
.sess()
89+
.cstore
90+
.exported_symbols(cnum)
91+
.iter()
92+
.map(|&def_id| {
93+
debug!("EXTERN-SYMBOL: {:?}", def_id);
94+
let name = Instance::mono(scx, def_id).symbol_name(scx);
95+
(name, export_level(scx, def_id))
96+
})
97+
.collect();
98+
99+
exports.insert(cnum, crate_exports);
100+
}
101+
102+
return ExportedSymbols {
103+
exports: exports
104+
};
105+
106+
fn export_level(scx: &SharedCrateContext,
107+
sym_def_id: DefId)
108+
-> SymbolExportLevel {
109+
let attrs = scx.tcx().get_attrs(sym_def_id);
110+
if attr::contains_extern_indicator(scx.sess().diagnostic(), &attrs) {
111+
SymbolExportLevel::C
112+
} else {
113+
SymbolExportLevel::Rust
114+
}
115+
}
116+
}
117+
118+
pub fn exported_symbols(&self,
119+
cnum: CrateNum)
120+
-> &[(String, SymbolExportLevel)] {
121+
match self.exports.get(&cnum) {
122+
Some(exports) => &exports[..],
123+
None => &[]
124+
}
125+
}
126+
127+
pub fn for_each_exported_symbol<F>(&self,
128+
cnum: CrateNum,
129+
export_threshold: SymbolExportLevel,
130+
mut f: F)
131+
where F: FnMut(&str, SymbolExportLevel)
132+
{
133+
for &(ref name, export_level) in self.exported_symbols(cnum) {
134+
if is_below_threshold(export_level, export_threshold) {
135+
f(&name[..], export_level)
136+
}
137+
}
138+
}
139+
}
140+
141+
pub fn crate_export_threshold(crate_type: config::CrateType)
142+
-> SymbolExportLevel {
143+
match crate_type {
144+
config::CrateTypeExecutable |
145+
config::CrateTypeStaticlib |
146+
config::CrateTypeProcMacro |
147+
config::CrateTypeCdylib => SymbolExportLevel::C,
148+
config::CrateTypeRlib |
149+
config::CrateTypeMetadata |
150+
config::CrateTypeDylib => SymbolExportLevel::Rust,
151+
}
152+
}
153+
154+
pub fn crates_export_threshold(crate_types: &[config::CrateType])
155+
-> SymbolExportLevel {
156+
if crate_types.iter().any(|&crate_type| {
157+
crate_export_threshold(crate_type) == SymbolExportLevel::Rust
158+
}) {
159+
SymbolExportLevel::Rust
160+
} else {
161+
SymbolExportLevel::C
162+
}
163+
}
164+
165+
pub fn is_below_threshold(level: SymbolExportLevel,
166+
threshold: SymbolExportLevel)
167+
-> bool {
168+
if threshold == SymbolExportLevel::Rust {
169+
// We export everything from Rust dylibs
170+
true
171+
} else {
172+
level == SymbolExportLevel::C
173+
}
174+
}
175+
176+
fn symbol_for_def_id<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>,
177+
def_id: DefId,
178+
symbol_map: &SymbolMap<'tcx>)
179+
-> String {
180+
// Just try to look things up in the symbol map. If nothing's there, we
181+
// recompute.
182+
if let Some(node_id) = scx.tcx().map.as_local_node_id(def_id) {
183+
if let Some(sym) = symbol_map.get(TransItem::Static(node_id)) {
184+
return sym.to_owned();
185+
}
186+
}
187+
188+
let instance = Instance::mono(scx, def_id);
189+
190+
symbol_map.get(TransItem::Fn(instance))
191+
.map(str::to_owned)
192+
.unwrap_or_else(|| instance.symbol_name(scx))
193+
}

‎src/librustc_trans/back/write.rs

+11-8
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
use back::lto;
1212
use back::link::{get_linker, remove};
13+
use back::symbol_export::ExportedSymbols;
1314
use rustc_incremental::{save_trans_partition, in_incr_comp_dir};
1415
use session::config::{OutputFilenames, OutputTypes, Passes, SomePasses, AllPasses};
1516
use session::Session;
@@ -328,7 +329,7 @@ impl ModuleConfig {
328329
struct CodegenContext<'a> {
329330
// Extra resources used for LTO: (sess, reachable). This will be `None`
330331
// when running in a worker thread.
331-
lto_ctxt: Option<(&'a Session, &'a [String])>,
332+
lto_ctxt: Option<(&'a Session, &'a ExportedSymbols)>,
332333
// Handler to use for diagnostics produced during codegen.
333334
handler: &'a Handler,
334335
// LLVM passes added by plugins.
@@ -343,9 +344,11 @@ struct CodegenContext<'a> {
343344
}
344345

345346
impl<'a> CodegenContext<'a> {
346-
fn new_with_session(sess: &'a Session, reachable: &'a [String]) -> CodegenContext<'a> {
347+
fn new_with_session(sess: &'a Session,
348+
exported_symbols: &'a ExportedSymbols)
349+
-> CodegenContext<'a> {
347350
CodegenContext {
348-
lto_ctxt: Some((sess, reachable)),
351+
lto_ctxt: Some((sess, exported_symbols)),
349352
handler: sess.diagnostic(),
350353
plugin_passes: sess.plugin_llvm_passes.borrow().clone(),
351354
remark: sess.opts.cg.remark.clone(),
@@ -516,14 +519,14 @@ unsafe fn optimize_and_codegen(cgcx: &CodegenContext,
516519
llvm::LLVMDisposePassManager(mpm);
517520

518521
match cgcx.lto_ctxt {
519-
Some((sess, reachable)) if sess.lto() => {
522+
Some((sess, exported_symbols)) if sess.lto() => {
520523
time(sess.time_passes(), "all lto passes", || {
521524
let temp_no_opt_bc_filename =
522525
output_names.temp_path_ext("no-opt.lto.bc", module_name);
523526
lto::run(sess,
524527
llmod,
525528
tm,
526-
reachable,
529+
exported_symbols,
527530
&config,
528531
&temp_no_opt_bc_filename);
529532
});
@@ -753,7 +756,7 @@ pub fn run_passes(sess: &Session,
753756
// potentially create hundreds of them).
754757
let num_workers = work_items.len() - 1;
755758
if num_workers == 1 {
756-
run_work_singlethreaded(sess, &trans.reachable, work_items);
759+
run_work_singlethreaded(sess, &trans.exported_symbols, work_items);
757760
} else {
758761
run_work_multithreaded(sess, work_items, num_workers);
759762
}
@@ -997,9 +1000,9 @@ fn execute_work_item(cgcx: &CodegenContext,
9971000
}
9981001

9991002
fn run_work_singlethreaded(sess: &Session,
1000-
reachable: &[String],
1003+
exported_symbols: &ExportedSymbols,
10011004
work_items: Vec<WorkItem>) {
1002-
let cgcx = CodegenContext::new_with_session(sess, reachable);
1005+
let cgcx = CodegenContext::new_with_session(sess, exported_symbols);
10031006

10041007
// Since we're running single-threaded, we can pass the session to
10051008
// the proc, allowing `optimize_and_codegen` to perform LTO.

‎src/librustc_trans/base.rs

+103-123
Large diffs are not rendered by default.

‎src/librustc_trans/context.rs

+8-8
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ pub struct SharedCrateContext<'a, 'tcx: 'a> {
6767
metadata_llcx: ContextRef,
6868

6969
export_map: ExportMap,
70-
reachable: NodeSet,
70+
exported_symbols: NodeSet,
7171
link_meta: LinkMeta,
7272
tcx: TyCtxt<'a, 'tcx, 'tcx>,
7373
stats: Stats,
@@ -437,7 +437,7 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> {
437437
pub fn new(tcx: TyCtxt<'b, 'tcx, 'tcx>,
438438
export_map: ExportMap,
439439
link_meta: LinkMeta,
440-
reachable: NodeSet,
440+
exported_symbols: NodeSet,
441441
check_overflow: bool)
442442
-> SharedCrateContext<'b, 'tcx> {
443443
let (metadata_llcx, metadata_llmod) = unsafe {
@@ -454,7 +454,7 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> {
454454
// they're not available to be linked against. This poses a few problems
455455
// for the compiler, some of which are somewhat fundamental, but we use
456456
// the `use_dll_storage_attrs` variable below to attach the `dllexport`
457-
// attribute to all LLVM functions that are reachable (e.g. they're
457+
// attribute to all LLVM functions that are exported e.g. they're
458458
// already tagged with external linkage). This is suboptimal for a few
459459
// reasons:
460460
//
@@ -493,7 +493,7 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> {
493493
metadata_llmod: metadata_llmod,
494494
metadata_llcx: metadata_llcx,
495495
export_map: export_map,
496-
reachable: reachable,
496+
exported_symbols: exported_symbols,
497497
link_meta: link_meta,
498498
tcx: tcx,
499499
stats: Stats {
@@ -527,8 +527,8 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> {
527527
&self.export_map
528528
}
529529

530-
pub fn reachable<'a>(&'a self) -> &'a NodeSet {
531-
&self.reachable
530+
pub fn exported_symbols<'a>(&'a self) -> &'a NodeSet {
531+
&self.exported_symbols
532532
}
533533

534534
pub fn trait_cache(&self) -> &RefCell<DepTrackingMap<TraitSelectionCache<'tcx>>> {
@@ -768,8 +768,8 @@ impl<'b, 'tcx> CrateContext<'b, 'tcx> {
768768
&self.shared.export_map
769769
}
770770

771-
pub fn reachable<'a>(&'a self) -> &'a NodeSet {
772-
&self.shared.reachable
771+
pub fn exported_symbols<'a>(&'a self) -> &'a NodeSet {
772+
&self.shared.exported_symbols
773773
}
774774

775775
pub fn link_meta<'a>(&'a self) -> &'a LinkMeta {

‎src/librustc_trans/debuginfo/utils.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ pub fn is_node_local_to_unit(cx: &CrateContext, node_id: ast::NodeId) -> bool
3434
// visible). It might better to use the `exported_items` set from
3535
// `driver::CrateAnalysis` in the future, but (atm) this set is not
3636
// available in the translation pass.
37-
!cx.reachable().contains(&node_id)
37+
!cx.exported_symbols().contains(&node_id)
3838
}
3939

4040
#[allow(non_snake_case)]

‎src/librustc_trans/declare.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ fn declare_raw_fn(ccx: &CrateContext, name: &str, callconv: llvm::CallConv, ty:
7878
// don't want the symbols to get exported.
7979
if attr::contains_name(ccx.tcx().map.krate_attrs(), "compiler_builtins") {
8080
unsafe {
81-
llvm::LLVMSetVisibility(llfn, llvm::Visibility::Hidden);
81+
llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden);
8282
}
8383
}
8484

‎src/librustc_trans/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ pub mod back {
7878
pub mod linker;
7979
pub mod link;
8080
pub mod lto;
81+
pub mod symbol_export;
8182
pub mod symbol_names;
8283
pub mod write;
8384
pub mod msvc;
@@ -169,7 +170,7 @@ pub struct CrateTranslation {
169170
pub metadata_module: ModuleTranslation,
170171
pub link: middle::cstore::LinkMeta,
171172
pub metadata: Vec<u8>,
172-
pub reachable: Vec<String>,
173+
pub exported_symbols: back::symbol_export::ExportedSymbols,
173174
pub no_builtins: bool,
174175
pub windows_subsystem: Option<String>,
175176
pub linker_info: back::linker::LinkerInfo

‎src/rustllvm/RustWrapper.cpp

+42
Original file line numberDiff line numberDiff line change
@@ -1408,3 +1408,45 @@ extern "C" void LLVMRustSetLinkage(LLVMValueRef V, LLVMRustLinkage RustLinkage)
14081408
extern "C" LLVMContextRef LLVMRustGetValueContext(LLVMValueRef V) {
14091409
return wrap(&unwrap(V)->getContext());
14101410
}
1411+
1412+
enum class LLVMRustVisibility {
1413+
Default = 0,
1414+
Hidden = 1,
1415+
Protected = 2,
1416+
};
1417+
1418+
static LLVMRustVisibility to_rust(LLVMVisibility vis) {
1419+
switch (vis) {
1420+
case LLVMDefaultVisibility:
1421+
return LLVMRustVisibility::Default;
1422+
case LLVMHiddenVisibility:
1423+
return LLVMRustVisibility::Hidden;
1424+
case LLVMProtectedVisibility:
1425+
return LLVMRustVisibility::Protected;
1426+
1427+
default:
1428+
llvm_unreachable("Invalid LLVMRustVisibility value!");
1429+
}
1430+
}
1431+
1432+
static LLVMVisibility from_rust(LLVMRustVisibility vis) {
1433+
switch (vis) {
1434+
case LLVMRustVisibility::Default:
1435+
return LLVMDefaultVisibility;
1436+
case LLVMRustVisibility::Hidden:
1437+
return LLVMHiddenVisibility;
1438+
case LLVMRustVisibility::Protected:
1439+
return LLVMProtectedVisibility;
1440+
1441+
default:
1442+
llvm_unreachable("Invalid LLVMRustVisibility value!");
1443+
}
1444+
}
1445+
1446+
extern "C" LLVMRustVisibility LLVMRustGetVisibility(LLVMValueRef V) {
1447+
return to_rust(LLVMGetVisibility(V));
1448+
}
1449+
1450+
extern "C" void LLVMRustSetVisibility(LLVMValueRef V, LLVMRustVisibility RustVisibility) {
1451+
LLVMSetVisibility(V, from_rust(RustVisibility));
1452+
}

‎src/test/run-make/sepcomp-inlining/Makefile

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@ all:
1010
$(RUSTC) foo.rs --emit=llvm-ir -C codegen-units=3
1111
[ "$$(cat "$(TMPDIR)"/foo.?.ll | grep -c define\ i32\ .*inlined)" -eq "0" ]
1212
[ "$$(cat "$(TMPDIR)"/foo.?.ll | grep -c define\ internal\ i32\ .*inlined)" -eq "2" ]
13-
[ "$$(cat "$(TMPDIR)"/foo.?.ll | grep -c define\ i32\ .*normal)" -eq "1" ]
14-
[ "$$(cat "$(TMPDIR)"/foo.?.ll | grep -c declare\ i32\ .*normal)" -eq "2" ]
13+
[ "$$(cat "$(TMPDIR)"/foo.?.ll | grep -c define\ hidden\ i32\ .*normal)" -eq "1" ]
14+
[ "$$(cat "$(TMPDIR)"/foo.?.ll | grep -c declare\ hidden\ i32\ .*normal)" -eq "2" ]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
include ../tools.mk
2+
3+
ifdef IS_WINDOWS
4+
# Do nothing on MSVC.
5+
# On MINGW the --version-script, --dynamic-list, and --retain-symbol args don't
6+
# seem to work reliably.
7+
all:
8+
exit 0
9+
else
10+
11+
NM=nm -D
12+
DYLIB_EXT=so
13+
CDYLIB_NAME=liba_cdylib.so
14+
RDYLIB_NAME=liba_rust_dylib.so
15+
EXE_NAME=an_executable
16+
17+
ifeq ($(UNAME),Darwin)
18+
NM=nm -gU
19+
DYLIB_EXT=dylib
20+
CDYLIB_NAME=liba_cdylib.dylib
21+
RDYLIB_NAME=liba_rust_dylib.dylib
22+
EXE_NAME=an_executable
23+
endif
24+
25+
all:
26+
$(RUSTC) an_rlib.rs
27+
$(RUSTC) a_cdylib.rs
28+
$(RUSTC) a_rust_dylib.rs
29+
$(RUSTC) an_executable.rs
30+
31+
# Check that a cdylib exports its public #[no_mangle] functions
32+
[ "$$($(NM) $(TMPDIR)/$(CDYLIB_NAME) | grep -c public_c_function_from_cdylib)" -eq "1" ]
33+
# Check that a cdylib exports the public #[no_mangle] functions of dependencies
34+
[ "$$($(NM) $(TMPDIR)/$(CDYLIB_NAME) | grep -c public_c_function_from_rlib)" -eq "1" ]
35+
# Check that a cdylib DOES NOT export any public Rust functions
36+
[ "$$($(NM) $(TMPDIR)/$(CDYLIB_NAME) | grep -c _ZN.*h.*E)" -eq "0" ]
37+
38+
# Check that a Rust dylib exports its monomorphic functions
39+
[ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c public_c_function_from_rust_dylib)" -eq "1" ]
40+
[ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c _ZN.*public_rust_function_from_rust_dylib.*E)" -eq "1" ]
41+
42+
# Check that a Rust dylib exports the monomorphic functions from its dependencies
43+
[ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c public_c_function_from_rlib)" -eq "1" ]
44+
[ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -c public_rust_function_from_rlib)" -eq "1" ]
45+
46+
# Check that an executable does not export any dynamic symbols
47+
[ "$$($(NM) $(TMPDIR)/$(EXE_NAME) | grep -c public_c_function_from_rlib)" -eq "0" ]
48+
[ "$$($(NM) $(TMPDIR)/$(EXE_NAME) | grep -c public_rust_function_from_exe)" -eq "0" ]
49+
50+
endif
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![crate_type="cdylib"]
12+
13+
extern crate an_rlib;
14+
15+
// This should not be exported
16+
pub fn public_rust_function_from_cdylib() {}
17+
18+
// This should be exported
19+
#[no_mangle]
20+
pub extern "C" fn public_c_function_from_cdylib() {
21+
an_rlib::public_c_function_from_rlib();
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![crate_type="dylib"]
12+
13+
extern crate an_rlib;
14+
15+
// This should be exported
16+
pub fn public_rust_function_from_rust_dylib() {}
17+
18+
// This should be exported
19+
#[no_mangle]
20+
pub extern "C" fn public_c_function_from_rust_dylib() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![crate_type="bin"]
12+
13+
extern crate an_rlib;
14+
15+
pub fn public_rust_function_from_exe() {}
16+
17+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![crate_type="rlib"]
12+
13+
pub fn public_rust_function_from_rlib() {}
14+
15+
#[no_mangle]
16+
pub extern "C" fn public_c_function_from_rlib() {}

0 commit comments

Comments
 (0)
Please sign in to comment.