|
| 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 | +//! The Rust Linkage Model and Symbol Names |
| 12 | +//! ======================================= |
| 13 | +//! |
| 14 | +//! The semantic model of Rust linkage is, broadly, that "there's no global |
| 15 | +//! namespace" between crates. Our aim is to preserve the illusion of this |
| 16 | +//! model despite the fact that it's not *quite* possible to implement on |
| 17 | +//! modern linkers. We initially didn't use system linkers at all, but have |
| 18 | +//! been convinced of their utility. |
| 19 | +//! |
| 20 | +//! There are a few issues to handle: |
| 21 | +//! |
| 22 | +//! - Linkers operate on a flat namespace, so we have to flatten names. |
| 23 | +//! We do this using the C++ namespace-mangling technique. Foo::bar |
| 24 | +//! symbols and such. |
| 25 | +//! |
| 26 | +//! - Symbols for distinct items with the same *name* need to get different |
| 27 | +//! linkage-names. Examples of this are monomorphizations of functions or |
| 28 | +//! items within anonymous scopes that end up having the same path. |
| 29 | +//! |
| 30 | +//! - Symbols in different crates but with same names "within" the crate need |
| 31 | +//! to get different linkage-names. |
| 32 | +//! |
| 33 | +//! - Symbol names should be deterministic: Two consecutive runs of the |
| 34 | +//! compiler over the same code base should produce the same symbol names for |
| 35 | +//! the same items. |
| 36 | +//! |
| 37 | +//! - Symbol names should not depend on any global properties of the code base, |
| 38 | +//! so that small modifications to the code base do not result in all symbols |
| 39 | +//! changing. In previous versions of the compiler, symbol names incorporated |
| 40 | +//! the SVH (Stable Version Hash) of the crate. This scheme turned out to be |
| 41 | +//! infeasible when used in conjunction with incremental compilation because |
| 42 | +//! small code changes would invalidate all symbols generated previously. |
| 43 | +//! |
| 44 | +//! - Even symbols from different versions of the same crate should be able to |
| 45 | +//! live next to each other without conflict. |
| 46 | +//! |
| 47 | +//! In order to fulfill the above requirements the following scheme is used by |
| 48 | +//! the compiler: |
| 49 | +//! |
| 50 | +//! The main tool for avoiding naming conflicts is the incorporation of a 64-bit |
| 51 | +//! hash value into every exported symbol name. Anything that makes a difference |
| 52 | +//! to the symbol being named, but does not show up in the regular path needs to |
| 53 | +//! be fed into this hash: |
| 54 | +//! |
| 55 | +//! - Different monomorphizations of the same item have the same path but differ |
| 56 | +//! in their concrete type parameters, so these parameters are part of the |
| 57 | +//! data being digested for the symbol hash. |
| 58 | +//! |
| 59 | +//! - Rust allows items to be defined in anonymous scopes, such as in |
| 60 | +//! `fn foo() { { fn bar() {} } { fn bar() {} } }`. Both `bar` functions have |
| 61 | +//! the path `foo::bar`, since the anonymous scopes do not contribute to the |
| 62 | +//! path of an item. The compiler already handles this case via so-called |
| 63 | +//! disambiguating `DefPaths` which use indices to distinguish items with the |
| 64 | +//! same name. The DefPaths of the functions above are thus `foo[0]::bar[0]` |
| 65 | +//! and `foo[0]::bar[1]`. In order to incorporate this disambiguation |
| 66 | +//! information into the symbol name too, these indices are fed into the |
| 67 | +//! symbol hash, so that the above two symbols would end up with different |
| 68 | +//! hash values. |
| 69 | +//! |
| 70 | +//! The two measures described above suffice to avoid intra-crate conflicts. In |
| 71 | +//! order to also avoid inter-crate conflicts two more measures are taken: |
| 72 | +//! |
| 73 | +//! - The name of the crate containing the symbol is prepended to the symbol |
| 74 | +//! name, i.e. symbols are "crate qualified". For example, a function `foo` in |
| 75 | +//! module `bar` in crate `baz` would get a symbol name like |
| 76 | +//! `baz::bar::foo::{hash}` instead of just `bar::foo::{hash}`. This avoids |
| 77 | +//! simple conflicts between functions from different crates. |
| 78 | +//! |
| 79 | +//! - In order to be able to also use symbols from two versions of the same |
| 80 | +//! crate (which naturally also have the same name), a stronger measure is |
| 81 | +//! required: The compiler accepts an arbitrary "salt" value via the |
| 82 | +//! `-C metadata` commandline argument. This salt is then fed into the symbol |
| 83 | +//! hash of every exported item. Consequently, the symbols in two identical |
| 84 | +//! crates but with different salts are not in conflict with each other. This |
| 85 | +//! facility is mainly intended to be used by build tools like Cargo. |
| 86 | +//! |
| 87 | +//! A note on symbol name stability |
| 88 | +//! ------------------------------- |
| 89 | +//! Previous versions of the compiler resorted to feeding NodeIds into the |
| 90 | +//! symbol hash in order to disambiguate between items with the same path. The |
| 91 | +//! current version of the name generation algorithm takes great care not to do |
| 92 | +//! that, since NodeIds are notoriously unstable: A small change to the |
| 93 | +//! code base will offset all NodeIds after the change and thus, much as using |
| 94 | +//! the SVH in the hash, invalidate an unbounded number of symbol names. This |
| 95 | +//! makes re-using previously compiled code for incremental compilation |
| 96 | +//! virtually impossible. Thus, symbol hash generation exclusively relies on |
| 97 | +//! DefPaths which are much more robust in the face of changes to the code base. |
| 98 | +
|
| 99 | +use trans::{CrateContext, Instance}; |
| 100 | +use util::sha2::{Digest, Sha256}; |
| 101 | + |
| 102 | +use rustc::middle::cstore; |
| 103 | +use rustc::middle::def_id::DefId; |
| 104 | +use rustc::middle::ty::{self, TypeFoldable}; |
| 105 | +use rustc::front::map::definitions::DefPath; |
| 106 | + |
| 107 | +use std::fmt::Write; |
| 108 | +use syntax::ast; |
| 109 | +use syntax::parse::token; |
| 110 | +use serialize::hex::ToHex; |
| 111 | +use super::link; |
| 112 | + |
| 113 | +pub fn def_id_to_string<'tcx>(tcx: &ty::TyCtxt<'tcx>, def_id: DefId) -> String { |
| 114 | + |
| 115 | + let def_path = tcx.def_path(def_id); |
| 116 | + let mut s = String::with_capacity(def_path.len() * 16); |
| 117 | + |
| 118 | + let def_path = if def_id.is_local() { |
| 119 | + s.push_str(&tcx.crate_name[..]); |
| 120 | + s.push_str("/"); |
| 121 | + s.push_str(&tcx.sess.crate_disambiguator.borrow()[..]); |
| 122 | + &def_path[..] |
| 123 | + } else { |
| 124 | + s.push_str(&tcx.sess.cstore.crate_name(def_id.krate)[..]); |
| 125 | + s.push_str("/"); |
| 126 | + s.push_str(&tcx.sess.cstore.crate_disambiguator(def_id.krate)); |
| 127 | + &def_path[1..] |
| 128 | + }; |
| 129 | + |
| 130 | + for component in def_path { |
| 131 | + write!(s, |
| 132 | + "::{}[{}]", |
| 133 | + component.data.as_interned_str(), |
| 134 | + component.disambiguator) |
| 135 | + .unwrap(); |
| 136 | + } |
| 137 | + |
| 138 | + s |
| 139 | +} |
| 140 | + |
| 141 | +fn get_symbol_hash<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, |
| 142 | + def_path: &DefPath, |
| 143 | + originating_crate: ast::CrateNum, |
| 144 | + parameters: &[ty::Ty<'tcx>]) |
| 145 | + -> String { |
| 146 | + let tcx = ccx.tcx(); |
| 147 | + |
| 148 | + let mut hash_state = ccx.symbol_hasher().borrow_mut(); |
| 149 | + |
| 150 | + hash_state.reset(); |
| 151 | + |
| 152 | + if originating_crate == cstore::LOCAL_CRATE { |
| 153 | + hash_state.input_str(&tcx.sess.crate_disambiguator.borrow()[..]); |
| 154 | + } else { |
| 155 | + hash_state.input_str(&tcx.sess.cstore.crate_disambiguator(originating_crate)); |
| 156 | + } |
| 157 | + |
| 158 | + for component in def_path { |
| 159 | + let disambiguator_bytes = [(component.disambiguator >> 0) as u8, |
| 160 | + (component.disambiguator >> 8) as u8, |
| 161 | + (component.disambiguator >> 16) as u8, |
| 162 | + (component.disambiguator >> 24) as u8]; |
| 163 | + hash_state.input(&disambiguator_bytes); |
| 164 | + } |
| 165 | + |
| 166 | + for t in parameters { |
| 167 | + assert!(!t.has_erasable_regions()); |
| 168 | + assert!(!t.needs_subst()); |
| 169 | + let encoded_type = tcx.sess.cstore.encode_type(tcx, t, def_id_to_string); |
| 170 | + hash_state.input(&encoded_type[..]); |
| 171 | + } |
| 172 | + |
| 173 | + return format!("h{}", truncated_hash_result(&mut *hash_state)); |
| 174 | + |
| 175 | + fn truncated_hash_result(symbol_hasher: &mut Sha256) -> String { |
| 176 | + let output = symbol_hasher.result_bytes(); |
| 177 | + // 64 bits should be enough to avoid collisions. |
| 178 | + output[.. 8].to_hex() |
| 179 | + } |
| 180 | +} |
| 181 | + |
| 182 | +fn exported_name_with_opt_suffix<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, |
| 183 | + instance: &Instance<'tcx>, |
| 184 | + suffix: Option<&str>) |
| 185 | + -> String { |
| 186 | + let &Instance { def: mut def_id, params: parameters } = instance; |
| 187 | + |
| 188 | + if let Some(node_id) = ccx.tcx().map.as_local_node_id(def_id) { |
| 189 | + if let Some(&src_def_id) = ccx.external_srcs().borrow().get(&node_id) { |
| 190 | + def_id = src_def_id; |
| 191 | + } |
| 192 | + } |
| 193 | + |
| 194 | + let def_path = ccx.tcx().def_path(def_id); |
| 195 | + let hash = get_symbol_hash(ccx, &def_path, def_id.krate, parameters.as_slice()); |
| 196 | + |
| 197 | + let mut path = Vec::with_capacity(16); |
| 198 | + |
| 199 | + if def_id.is_local() { |
| 200 | + path.push(ccx.tcx().crate_name.clone()); |
| 201 | + } |
| 202 | + |
| 203 | + path.extend(def_path.into_iter().map(|e| e.data.as_interned_str())); |
| 204 | + |
| 205 | + if let Some(suffix) = suffix { |
| 206 | + path.push(token::intern_and_get_ident(suffix)); |
| 207 | + } |
| 208 | + |
| 209 | + link::mangle(path.into_iter(), Some(&hash[..])) |
| 210 | +} |
| 211 | + |
| 212 | +pub fn exported_name<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, |
| 213 | + instance: &Instance<'tcx>) |
| 214 | + -> String { |
| 215 | + exported_name_with_opt_suffix(ccx, instance, None) |
| 216 | +} |
| 217 | + |
| 218 | +pub fn exported_name_with_suffix<'a, 'tcx>(ccx: &CrateContext<'a, 'tcx>, |
| 219 | + instance: &Instance<'tcx>, |
| 220 | + suffix: &str) |
| 221 | + -> String { |
| 222 | + exported_name_with_opt_suffix(ccx, instance, Some(suffix)) |
| 223 | +} |
0 commit comments