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