]> git.lizzy.rs Git - rust.git/blob - src/librustc_trans/back/symbol_export.rs
b38dc18838923196d527ee64900398c7a4f6c8bb
[rust.git] / src / librustc_trans / back / symbol_export.rs
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 rustc::util::nodemap::FxHashMap;
14 use rustc::hir::def_id::{DefId, CrateNum, LOCAL_CRATE};
15 use rustc::session::config;
16 use rustc::ty::TyCtxt;
17 use syntax::attr;
18
19 /// The SymbolExportLevel of a symbols specifies from which kinds of crates
20 /// the symbol will be exported. `C` symbols will be exported from any
21 /// kind of crate, including cdylibs which export very few things.
22 /// `Rust` will only be exported if the crate produced is a Rust
23 /// dylib.
24 #[derive(Eq, PartialEq, Debug, Copy, Clone)]
25 pub enum SymbolExportLevel {
26     C,
27     Rust,
28 }
29
30 /// The set of symbols exported from each crate in the crate graph.
31 pub struct ExportedSymbols {
32     exports: FxHashMap<CrateNum, Vec<(String, SymbolExportLevel)>>,
33 }
34
35 impl ExportedSymbols {
36     pub fn empty() -> ExportedSymbols {
37         ExportedSymbols {
38             exports: FxHashMap(),
39         }
40     }
41
42     pub fn compute<'a, 'tcx>(scx: &SharedCrateContext<'a, 'tcx>) -> ExportedSymbols {
43         let mut local_crate: Vec<_> = scx
44             .exported_symbols()
45             .iter()
46             .map(|&node_id| {
47                 scx.tcx().hir.local_def_id(node_id)
48             })
49             .map(|def_id| {
50                 let name = scx.tcx().symbol_name(Instance::mono(scx.tcx(), def_id));
51                 let export_level = export_level(scx, def_id);
52                 debug!("EXPORTED SYMBOL (local): {} ({:?})", name, export_level);
53                 (str::to_owned(&name), export_level)
54             })
55             .collect();
56
57         if scx.sess().entry_fn.borrow().is_some() {
58             local_crate.push(("main".to_string(), SymbolExportLevel::C));
59         }
60
61         if let Some(id) = scx.sess().derive_registrar_fn.get() {
62             let def_id = scx.tcx().hir.local_def_id(id);
63             let idx = def_id.index;
64             let disambiguator = scx.sess().local_crate_disambiguator();
65             let registrar = scx.sess().generate_derive_registrar_symbol(disambiguator, idx);
66             local_crate.push((registrar, SymbolExportLevel::C));
67         }
68
69         if scx.sess().crate_types.borrow().contains(&config::CrateTypeDylib) {
70             local_crate.push((metadata_symbol_name(scx.tcx()),
71                               SymbolExportLevel::Rust));
72         }
73
74         let mut exports = FxHashMap();
75         exports.insert(LOCAL_CRATE, local_crate);
76
77         for cnum in scx.sess().cstore.crates() {
78             debug_assert!(cnum != LOCAL_CRATE);
79
80             // If this crate is a plugin and/or a custom derive crate, then
81             // we're not even going to link those in so we skip those crates.
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             // Check to see if this crate is a "special runtime crate". These
88             // crates, implementation details of the standard library, typically
89             // have a bunch of `pub extern` and `#[no_mangle]` functions as the
90             // ABI between them. We don't want their symbols to have a `C`
91             // export level, however, as they're just implementation details.
92             // Down below we'll hardwire all of the symbols to the `Rust` export
93             // level instead.
94             let special_runtime_crate =
95                 scx.tcx().is_allocator(cnum.as_def_id()) ||
96                 scx.tcx().is_panic_runtime(cnum.as_def_id()) ||
97                 scx.sess().cstore.is_compiler_builtins(cnum);
98
99             let crate_exports = scx
100                 .sess()
101                 .cstore
102                 .exported_symbols(cnum)
103                 .iter()
104                 .map(|&def_id| {
105                     let name = scx.tcx().symbol_name(Instance::mono(scx.tcx(), def_id));
106                     let export_level = if special_runtime_crate {
107                         // We can probably do better here by just ensuring that
108                         // it has hidden visibility rather than public
109                         // visibility, as this is primarily here to ensure it's
110                         // not stripped during LTO.
111                         //
112                         // In general though we won't link right if these
113                         // symbols are stripped, and LTO currently strips them.
114                         if &*name == "rust_eh_personality" ||
115                            &*name == "rust_eh_register_frames" ||
116                            &*name == "rust_eh_unregister_frames" {
117                             SymbolExportLevel::C
118                         } else {
119                             SymbolExportLevel::Rust
120                         }
121                     } else {
122                         export_level(scx, def_id)
123                     };
124                     debug!("EXPORTED SYMBOL (re-export): {} ({:?})", name, export_level);
125                     (str::to_owned(&name), export_level)
126                 })
127                 .collect();
128
129             exports.insert(cnum, crate_exports);
130         }
131
132         return ExportedSymbols {
133             exports: exports
134         };
135
136         fn export_level(scx: &SharedCrateContext,
137                         sym_def_id: DefId)
138                         -> SymbolExportLevel {
139             let attrs = scx.tcx().get_attrs(sym_def_id);
140             if attr::contains_extern_indicator(scx.sess().diagnostic(), &attrs) {
141                 SymbolExportLevel::C
142             } else {
143                 SymbolExportLevel::Rust
144             }
145         }
146     }
147
148     pub fn exported_symbols(&self,
149                             cnum: CrateNum)
150                             -> &[(String, SymbolExportLevel)] {
151         match self.exports.get(&cnum) {
152             Some(exports) => exports,
153             None => &[]
154         }
155     }
156
157     pub fn for_each_exported_symbol<F>(&self,
158                                        cnum: CrateNum,
159                                        export_threshold: SymbolExportLevel,
160                                        mut f: F)
161         where F: FnMut(&str, SymbolExportLevel)
162     {
163         for &(ref name, export_level) in self.exported_symbols(cnum) {
164             if is_below_threshold(export_level, export_threshold) {
165                 f(&name, export_level)
166             }
167         }
168     }
169 }
170
171 pub fn metadata_symbol_name(tcx: TyCtxt) -> String {
172     format!("rust_metadata_{}_{}",
173             tcx.crate_name(LOCAL_CRATE),
174             tcx.crate_disambiguator(LOCAL_CRATE))
175 }
176
177 pub fn crate_export_threshold(crate_type: config::CrateType)
178                                      -> SymbolExportLevel {
179     match crate_type {
180         config::CrateTypeExecutable |
181         config::CrateTypeStaticlib  |
182         config::CrateTypeProcMacro  |
183         config::CrateTypeCdylib     => SymbolExportLevel::C,
184         config::CrateTypeRlib       |
185         config::CrateTypeDylib      => SymbolExportLevel::Rust,
186     }
187 }
188
189 pub fn crates_export_threshold(crate_types: &[config::CrateType])
190                                       -> SymbolExportLevel {
191     if crate_types.iter().any(|&crate_type| {
192         crate_export_threshold(crate_type) == SymbolExportLevel::Rust
193     }) {
194         SymbolExportLevel::Rust
195     } else {
196         SymbolExportLevel::C
197     }
198 }
199
200 pub fn is_below_threshold(level: SymbolExportLevel,
201                           threshold: SymbolExportLevel)
202                           -> bool {
203     if threshold == SymbolExportLevel::Rust {
204         // We export everything from Rust dylibs
205         true
206     } else {
207         level == SymbolExportLevel::C
208     }
209 }