rustc: Add the concept of a Strict Version Hash

This new SVH is used to uniquely identify all crates as a snapshot in time of
their ABI/API/publicly reachable state. This current calculation is just a hash
of the entire crate's AST. This is obviously incorrect, but it is currently the
reality for today.

This change threads through the new Svh structure which originates from crate
dependencies. The concept of crate id hash is preserved to provide efficient
matching on filenames for crate loading. The inspected hash once crate metadata
is opened has been changed to use the new Svh.

The goal of this hash is to identify when upstream crates have changed but
downstream crates have not been recompiled. This will prevent the def-id drift
problem where upstream crates were recompiled, thereby changing their metadata,
but downstream crates were not recompiled.

In the future this hash can be expanded to exclude contents of the AST like doc
comments, but limitations in the compiler prevent this change from being made at
this time.

Closes #10207
This commit is contained in:
Alex Crichton 2014-02-24 19:45:20 -08:00
parent 8213e18447
commit ec57db083f
19 changed files with 345 additions and 139 deletions

View File

@ -58,6 +58,7 @@ clean-generic-$(2)-$(1):
-name '*.[odasS]' -o \
-name '*.so' -o \
-name '*.dylib' -o \
-name '*.rlib' -o \
-name 'stamp.*' -o \
-name '*.lib' -o \
-name '*.dll' -o \

View File

@ -8,9 +8,9 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use back::archive::{Archive, METADATA_FILENAME};
use back::rpath;
use back::svh::Svh;
use driver::driver::{CrateTranslation, OutputFilenames};
use driver::session::Session;
use driver::session;
@ -499,30 +499,33 @@ pub mod write {
* system linkers understand.
*/
pub fn build_link_meta(attrs: &[ast::Attribute],
output: &OutputFilenames,
symbol_hasher: &mut Sha256)
-> LinkMeta {
// This calculates CMH as defined above
fn crate_hash(symbol_hasher: &mut Sha256, crateid: &CrateId) -> ~str {
symbol_hasher.reset();
symbol_hasher.input_str(crateid.to_str());
truncated_hash_result(symbol_hasher)
}
let crateid = match attr::find_crateid(attrs) {
pub fn find_crate_id(attrs: &[ast::Attribute],
output: &OutputFilenames) -> CrateId {
match attr::find_crateid(attrs) {
None => from_str(output.out_filestem).unwrap(),
Some(s) => s,
};
let hash = crate_hash(symbol_hasher, &crateid);
LinkMeta {
crateid: crateid,
crate_hash: hash,
}
}
pub fn crate_id_hash(crate_id: &CrateId) -> ~str {
// This calculates CMH as defined above. Note that we don't use the path of
// the crate id in the hash because lookups are only done by (name/vers),
// not by path.
let mut s = Sha256::new();
s.input_str(crate_id.short_name_with_version());
truncated_hash_result(&mut s).slice_to(8).to_owned()
}
pub fn build_link_meta(krate: &ast::Crate,
output: &OutputFilenames) -> LinkMeta {
let r = LinkMeta {
crateid: find_crate_id(krate.attrs, output),
crate_hash: Svh::calculate(krate),
};
info!("{}", r);
return r;
}
fn truncated_hash_result(symbol_hasher: &mut Sha256) -> ~str {
let output = symbol_hasher.result_bytes();
// 64 bits should be enough to avoid collisions.
@ -539,7 +542,7 @@ fn symbol_hash(tcx: ty::ctxt, symbol_hasher: &mut Sha256,
symbol_hasher.reset();
symbol_hasher.input_str(link_meta.crateid.name);
symbol_hasher.input_str("-");
symbol_hasher.input_str(link_meta.crate_hash);
symbol_hasher.input_str(link_meta.crate_hash.as_str());
symbol_hasher.input_str("-");
symbol_hasher.input_str(encoder::encoded_ty(tcx, t));
let mut hash = truncated_hash_result(symbol_hasher);
@ -712,11 +715,8 @@ pub fn mangle_internal_name_by_path_and_seq(path: PathElems, flav: &str) -> ~str
mangle(path.chain(Some(gensym_name(flav)).move_iter()), None, None)
}
pub fn output_lib_filename(lm: &LinkMeta) -> ~str {
format!("{}-{}-{}",
lm.crateid.name,
lm.crate_hash.slice_chars(0, 8),
lm.crateid.version_or_default())
pub fn output_lib_filename(id: &CrateId) -> ~str {
format!("{}-{}-{}", id.name, crate_id_hash(id), id.version_or_default())
}
pub fn get_cc_prog(sess: Session) -> ~str {
@ -779,11 +779,11 @@ fn remove(sess: Session, path: &Path) {
pub fn link_binary(sess: Session,
trans: &CrateTranslation,
outputs: &OutputFilenames,
lm: &LinkMeta) -> ~[Path] {
id: &CrateId) -> ~[Path] {
let mut out_filenames = ~[];
let crate_types = sess.crate_types.borrow();
for &crate_type in crate_types.get().iter() {
let out_file = link_binary_output(sess, trans, crate_type, outputs, lm);
let out_file = link_binary_output(sess, trans, crate_type, outputs, id);
out_filenames.push(out_file);
}
@ -807,8 +807,8 @@ fn is_writeable(p: &Path) -> bool {
}
pub fn filename_for_input(sess: &Session, crate_type: session::CrateType,
lm: &LinkMeta, out_filename: &Path) -> Path {
let libname = output_lib_filename(lm);
id: &CrateId, out_filename: &Path) -> Path {
let libname = output_lib_filename(id);
match crate_type {
session::CrateTypeRlib => {
out_filename.with_filename(format!("lib{}.rlib", libname))
@ -834,13 +834,13 @@ fn link_binary_output(sess: Session,
trans: &CrateTranslation,
crate_type: session::CrateType,
outputs: &OutputFilenames,
lm: &LinkMeta) -> Path {
id: &CrateId) -> Path {
let obj_filename = outputs.temp_path(OutputTypeObject);
let out_filename = match outputs.single_output_file {
Some(ref file) => file.clone(),
None => {
let out_filename = outputs.path(OutputTypeExe);
filename_for_input(&sess, crate_type, lm, &out_filename)
filename_for_input(&sess, crate_type, id, &out_filename)
}
};

112
src/librustc/back/svh.rs Normal file
View File

@ -0,0 +1,112 @@
// Copyright 2012-2014 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.
//! Calculation and management of a Strict Version Hash for crates
//!
//! # Today's ABI problem
//!
//! In today's implementation of rustc, it is incredibly difficult to achieve
//! forward binary compatibility without resorting to C-like interfaces. Within
//! rust code itself, abi details such as symbol names suffer from a variety of
//! unrelated factors to code changing such as the "def id drift" problem. This
//! ends up yielding confusing error messages about metadata mismatches and
//! such.
//!
//! The core of this problem is when when an upstream dependency changes and
//! downstream dependants are not recompiled. This causes compile errors because
//! the upstream crate's metadata has changed but the downstream crates are
//! still referencing the older crate's metadata.
//!
//! This problem exists for many reasons, the primary of which is that rust does
//! not currently support forwards ABI compatibility (in place upgrades of a
//! crate).
//!
//! # SVH and how it alleviates the problem
//!
//! With all of this knowledge on hand, this module contains the implementation
//! of a notion of a "Strict Version Hash" for a crate. This is essentially a
//! hash of all contents of a crate which can somehow be exposed to downstream
//! crates.
//!
//! This hash is currently calculated by just hashing the AST, but this is
//! obviously wrong (doc changes should not result in an incompatible ABI).
//! Implementation-wise, this is required at this moment in time.
//!
//! By encoding this strict version hash into all crate's metadata, stale crates
//! can be detected immediately and error'd about by rustc itself.
//!
//! # Relevant links
//!
//! Original issue: https://github.com/mozilla/rust/issues/10207
use std::fmt;
use std::hash::Hash;
use std::hash::sip::SipState;
use std::iter::range_step;
use syntax::ast;
#[deriving(Clone, Eq)]
pub struct Svh {
priv hash: ~str,
}
impl Svh {
pub fn new(hash: &str) -> Svh {
assert!(hash.len() == 16);
Svh { hash: hash.to_owned() }
}
pub fn as_str<'a>(&'a self) -> &'a str {
self.hash.as_slice()
}
pub fn calculate(krate: &ast::Crate) -> Svh {
// FIXME: see above for why this is wrong, it shouldn't just hash the
// crate. Fixing this would require more in-depth analysis in
// this function about what portions of the crate are reachable
// in tandem with bug fixes throughout the rest of the compiler.
//
// Note that for now we actually exclude some top-level things
// from the crate like the CrateConfig/span. The CrateConfig
// contains command-line `--cfg` flags, so this means that the
// stage1/stage2 AST for libstd and such is different hash-wise
// when it's actually the exact same representation-wise.
//
// As a first stab at only hashing the relevant parts of the
// AST, this only hashes the module/attrs, not the CrateConfig
// field.
//
// FIXME: this should use SHA1, not SipHash. SipHash is not built to
// avoid collisions.
let mut state = SipState::new();
krate.module.hash(&mut state);
krate.attrs.hash(&mut state);
let hash = state.result();
return Svh {
hash: range_step(0, 64, 4).map(|i| hex(hash >> i)).collect()
};
fn hex(b: u64) -> char {
let b = (b & 0xf) as u8;
let b = match b {
0 .. 9 => '0' as u8 + b,
_ => 'a' as u8 + b - 10,
};
b as char
}
}
}
impl fmt::Show for Svh {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.pad(self.as_str())
}
}

View File

@ -433,7 +433,7 @@ pub fn phase_6_link_output(sess: Session,
link::link_binary(sess,
trans,
outputs,
&trans.link));
&trans.link.crateid));
}
pub fn stop_after_phase_3(sess: Session) -> bool {
@ -472,8 +472,7 @@ fn write_out_deps(sess: Session,
input: &Input,
outputs: &OutputFilenames,
krate: &ast::Crate) -> io::IoResult<()> {
let lm = link::build_link_meta(krate.attrs, outputs,
&mut ::util::sha2::Sha256::new());
let id = link::find_crate_id(krate.attrs, outputs);
let mut out_filenames = ~[];
for output_type in sess.opts.output_types.iter() {
@ -482,7 +481,7 @@ fn write_out_deps(sess: Session,
link::OutputTypeExe => {
let crate_types = sess.crate_types.borrow();
for output in crate_types.get().iter() {
let p = link::filename_for_input(&sess, *output, &lm, &file);
let p = link::filename_for_input(&sess, *output, &id, &file);
out_filenames.push(p);
}
}

View File

@ -55,7 +55,6 @@ use std::str;
use std::task;
use std::vec;
use syntax::ast;
use syntax::attr;
use syntax::diagnostic::Emitter;
use syntax::diagnostic;
use syntax::parse;
@ -104,16 +103,17 @@ pub mod front {
}
pub mod back {
pub mod archive;
pub mod link;
pub mod abi;
pub mod archive;
pub mod arm;
pub mod link;
pub mod lto;
pub mod mips;
pub mod rpath;
pub mod svh;
pub mod target_strs;
pub mod x86;
pub mod x86_64;
pub mod rpath;
pub mod target_strs;
pub mod lto;
}
pub mod metadata;
@ -312,28 +312,18 @@ pub fn run_compiler(args: &[~str]) {
let attrs = parse_crate_attrs(sess, &input);
let t_outputs = d::build_output_filenames(&input, &odir, &ofile,
attrs, sess);
if crate_id || crate_name {
let crateid = match attr::find_crateid(attrs) {
Some(crateid) => crateid,
None => {
sess.fatal("No crate_id and --crate-id or \
--crate-name requested")
}
};
if crate_id {
println!("{}", crateid.to_str());
}
if crate_name {
println!("{}", crateid.name);
}
}
let id = link::find_crate_id(attrs, &t_outputs);
if crate_id {
println!("{}", id.to_str());
}
if crate_name {
println!("{}", id.name);
}
if crate_file_name {
let lm = link::build_link_meta(attrs, &t_outputs,
&mut ::util::sha2::Sha256::new());
let crate_types = session::collect_crate_types(&sess, attrs);
for &style in crate_types.iter() {
let fname = link::filename_for_input(&sess, style, &lm,
let fname = link::filename_for_input(&sess, style, &id,
&t_outputs.with_extension(""));
println!("{}", fname.filename_display());
}

View File

@ -12,6 +12,7 @@
use std::cast;
use syntax::crateid::CrateId;
use back::svh::Svh;
// EBML enum definitions and utils shared by the encoder and decoder
@ -207,8 +208,8 @@ pub static tag_macro_registrar_fn: uint = 0x63;
pub static tag_exported_macros: uint = 0x64;
pub static tag_macro_def: uint = 0x65;
#[deriving(Clone)]
#[deriving(Clone, Show)]
pub struct LinkMeta {
crateid: CrateId,
crate_hash: ~str,
crate_hash: Svh,
}

View File

@ -12,6 +12,8 @@
//! Validates all used crates and extern libraries and loads their metadata
use back::link;
use back::svh::Svh;
use driver::{driver, session};
use driver::session::Session;
use metadata::csearch;
@ -78,7 +80,7 @@ impl<'a> visit::Visitor<()> for ReadCrateVisitor<'a> {
struct cache_entry {
cnum: ast::CrateNum,
span: Span,
hash: ~str,
hash: Svh,
crate_id: CrateId,
}
@ -148,7 +150,7 @@ fn visit_view_item(e: &mut Env, i: &ast::ViewItem) {
match extract_crate_info(e, i) {
Some(info) => {
let cnum = resolve_crate(e, None, info.ident, &info.crate_id, "",
let cnum = resolve_crate(e, None, info.ident, &info.crate_id, None,
i.span);
e.sess.cstore.add_extern_mod_stmt_cnum(info.id, cnum);
}
@ -276,12 +278,13 @@ fn visit_item(e: &Env, i: &ast::Item) {
}
fn existing_match(e: &Env, crate_id: &CrateId,
hash: &str) -> Option<ast::CrateNum> {
hash: Option<&Svh>) -> Option<ast::CrateNum> {
let crate_cache = e.crate_cache.borrow();
for c in crate_cache.get().iter() {
if crate_id.matches(&c.crate_id) &&
(hash.is_empty() || hash == c.hash.as_slice()) {
return Some(c.cnum)
if !crate_id.matches(&c.crate_id) { continue }
match hash {
Some(hash) if *hash != c.hash => {}
Some(..) | None => return Some(c.cnum)
}
}
None
@ -291,19 +294,22 @@ fn resolve_crate(e: &mut Env,
root_ident: Option<&str>,
ident: &str,
crate_id: &CrateId,
hash: &str,
hash: Option<&Svh>,
span: Span)
-> ast::CrateNum {
match existing_match(e, crate_id, hash) {
None => {
let load_ctxt = loader::Context {
let id_hash = link::crate_id_hash(crate_id);
let mut load_ctxt = loader::Context {
sess: e.sess,
span: span,
ident: ident,
crate_id: crate_id,
hash: hash,
id_hash: id_hash,
hash: hash.map(|a| &*a),
os: e.os,
intr: e.intr
intr: e.intr,
rejected_via_hash: false,
};
let loader::Library {
dylib, rlib, metadata
@ -375,7 +381,7 @@ fn resolve_crate_deps(e: &mut Env,
let local_cnum = resolve_crate(e, root_ident,
dep.crate_id.name.as_slice(),
&dep.crate_id,
dep.hash,
Some(&dep.hash),
span);
cnum_map.insert(extrn_cnum, local_cnum);
}
@ -406,7 +412,7 @@ impl CrateLoader for Loader {
fn load_crate(&mut self, krate: &ast::ViewItem) -> MacroCrate {
let info = extract_crate_info(&self.env, krate).unwrap();
let cnum = resolve_crate(&mut self.env, None, info.ident,
&info.crate_id, "", krate.span);
&info.crate_id, None, krate.span);
let library = self.env.sess.cstore.get_used_crate_source(cnum).unwrap();
MacroCrate {
lib: library.dylib,

View File

@ -13,6 +13,7 @@
// The crate store - a central repo for information collected about external
// crates and libraries
use back::svh::Svh;
use metadata::decoder;
use metadata::loader;
@ -92,7 +93,7 @@ impl CStore {
*metas.get().get(&cnum)
}
pub fn get_crate_hash(&self, cnum: ast::CrateNum) -> ~str {
pub fn get_crate_hash(&self, cnum: ast::CrateNum) -> Svh {
let cdata = self.get_crate_data(cnum);
decoder::get_crate_hash(cdata.data())
}

View File

@ -12,6 +12,7 @@
#[allow(non_camel_case_types)];
use back::svh::Svh;
use metadata::cstore::crate_metadata;
use metadata::common::*;
use metadata::csearch::StaticMethodInfo;
@ -1089,9 +1090,9 @@ fn get_attributes(md: ebml::Doc) -> ~[ast::Attribute] {
return attrs;
}
fn list_crate_attributes(md: ebml::Doc, hash: &str,
fn list_crate_attributes(md: ebml::Doc, hash: &Svh,
out: &mut io::Writer) -> io::IoResult<()> {
try!(write!(out, "=Crate Attributes ({})=\n", hash));
try!(write!(out, "=Crate Attributes ({})=\n", *hash));
let r = get_attributes(md);
for attr in r.iter() {
@ -1109,7 +1110,7 @@ pub fn get_crate_attributes(data: &[u8]) -> ~[ast::Attribute] {
pub struct CrateDep {
cnum: ast::CrateNum,
crate_id: CrateId,
hash: ~str,
hash: Svh,
}
pub fn get_crate_deps(data: &[u8]) -> ~[CrateDep] {
@ -1123,7 +1124,7 @@ pub fn get_crate_deps(data: &[u8]) -> ~[CrateDep] {
}
reader::tagged_docs(depsdoc, tag_crate_dep, |depdoc| {
let crate_id = from_str(docstr(depdoc, tag_crate_dep_crateid)).unwrap();
let hash = docstr(depdoc, tag_crate_dep_hash);
let hash = Svh::new(docstr(depdoc, tag_crate_dep_hash));
deps.push(CrateDep {
cnum: crate_num,
crate_id: crate_id,
@ -1144,10 +1145,10 @@ fn list_crate_deps(data: &[u8], out: &mut io::Writer) -> io::IoResult<()> {
Ok(())
}
pub fn get_crate_hash(data: &[u8]) -> ~str {
pub fn get_crate_hash(data: &[u8]) -> Svh {
let cratedoc = reader::Doc(data);
let hashdoc = reader::get_doc(cratedoc, tag_crate_hash);
hashdoc.as_str_slice().to_str()
Svh::new(hashdoc.as_str_slice())
}
pub fn get_crate_id(data: &[u8]) -> CrateId {
@ -1159,7 +1160,7 @@ pub fn get_crate_id(data: &[u8]) -> CrateId {
pub fn list_crate_metadata(bytes: &[u8], out: &mut io::Writer) -> io::IoResult<()> {
let hash = get_crate_hash(bytes);
let md = reader::Doc(bytes);
try!(list_crate_attributes(md, hash, out));
try!(list_crate_attributes(md, &hash, out));
list_crate_deps(bytes, out)
}

View File

@ -13,6 +13,7 @@
#[allow(unused_must_use)]; // everything is just a MemWriter, can't fail
#[allow(non_camel_case_types)];
use back::svh::Svh;
use metadata::common::*;
use metadata::cstore;
use metadata::decoder;
@ -1733,14 +1734,14 @@ fn encode_crate_dep(ebml_w: &mut writer::Encoder,
ebml_w.writer.write(dep.crate_id.to_str().as_bytes());
ebml_w.end_tag();
ebml_w.start_tag(tag_crate_dep_hash);
ebml_w.writer.write(dep.hash.as_bytes());
ebml_w.writer.write(dep.hash.as_str().as_bytes());
ebml_w.end_tag();
ebml_w.end_tag();
}
fn encode_hash(ebml_w: &mut writer::Encoder, hash: &str) {
fn encode_hash(ebml_w: &mut writer::Encoder, hash: &Svh) {
ebml_w.start_tag(tag_crate_hash);
ebml_w.writer.write(hash.as_bytes());
ebml_w.writer.write(hash.as_str().as_bytes());
ebml_w.end_tag();
}
@ -1809,7 +1810,7 @@ fn encode_metadata_inner(wr: &mut MemWriter, parms: EncodeParams, krate: &Crate)
let mut ebml_w = writer::Encoder(wr);
encode_crate_id(&mut ebml_w, &ecx.link_meta.crateid);
encode_hash(&mut ebml_w, ecx.link_meta.crate_hash);
encode_hash(&mut ebml_w, &ecx.link_meta.crate_hash);
let mut i = ebml_w.writer.tell().unwrap();
let crate_attrs = synthesize_crate_attrs(&ecx, krate);

View File

@ -11,6 +11,7 @@
//! Finds crate binaries and loads their metadata
use back::archive::{ArchiveRO, METADATA_FILENAME};
use back::svh::Svh;
use driver::session::Session;
use lib::llvm::{False, llvm, ObjectFile, mk_section_iter};
use metadata::cstore::{MetadataBlob, MetadataVec, MetadataArchive};
@ -21,7 +22,6 @@ use syntax::codemap::Span;
use syntax::diagnostic::SpanHandler;
use syntax::parse::token::IdentInterner;
use syntax::crateid::CrateId;
use syntax::attr;
use syntax::attr::AttrMetaMethods;
use std::c_str::ToCStr;
@ -49,9 +49,11 @@ pub struct Context<'a> {
span: Span,
ident: &'a str,
crate_id: &'a CrateId,
hash: &'a str,
id_hash: &'a str,
hash: Option<&'a Svh>,
os: Os,
intr: @IdentInterner
intr: @IdentInterner,
rejected_via_hash: bool,
}
pub struct Library {
@ -79,23 +81,34 @@ fn realpath(p: &Path) -> Path {
}
impl<'a> Context<'a> {
pub fn load_library_crate(&self, root_ident: Option<&str>) -> Library {
pub fn load_library_crate(&mut self, root_ident: Option<&str>) -> Library {
match self.find_library_crate() {
Some(t) => t,
None => {
self.sess.abort_if_errors();
let message = match root_ident {
None => format!("can't find crate for `{}`", self.ident),
Some(c) => format!("can't find crate for `{}` which `{}` depends on",
self.ident,
c)
let message = if self.rejected_via_hash {
format!("found possibly newer version of crate `{}`",
self.ident)
} else {
format!("can't find crate for `{}`", self.ident)
};
self.sess.span_fatal(self.span, message);
let message = match root_ident {
None => message,
Some(c) => format!("{} which `{}` depends on", message, c),
};
self.sess.span_err(self.span, message);
if self.rejected_via_hash {
self.sess.span_note(self.span, "perhaps this crate needs \
to be recompiled?");
}
self.sess.abort_if_errors();
unreachable!()
}
}
}
fn find_library_crate(&self) -> Option<Library> {
fn find_library_crate(&mut self) -> Option<Library> {
let filesearch = self.sess.filesearch;
let (dyprefix, dysuffix) = self.dylibname();
@ -212,13 +225,8 @@ impl<'a> Context<'a> {
None => {}
}
let data = lib.metadata.as_slice();
let attrs = decoder::get_crate_attributes(data);
match attr::find_crateid(attrs) {
None => {}
Some(crateid) => {
note_crateid_attr(self.sess.diagnostic(), &crateid);
}
}
let crate_id = decoder::get_crate_id(data);
note_crateid_attr(self.sess.diagnostic(), &crate_id);
}
None
}
@ -240,18 +248,21 @@ impl<'a> Context<'a> {
debug!("matching -- {}, middle: {}", file, middle);
let mut parts = middle.splitn('-', 1);
let hash = match parts.next() { Some(h) => h, None => return None };
debug!("matching -- {}, hash: {}", file, hash);
debug!("matching -- {}, hash: {} (want {})", file, hash, self.id_hash);
let vers = match parts.next() { Some(v) => v, None => return None };
debug!("matching -- {}, vers: {}", file, vers);
debug!("matching -- {}, vers: {} (want {})", file, vers,
self.crate_id.version);
match self.crate_id.version {
Some(ref version) if version.as_slice() != vers => return None,
Some(..) | None => {}
Some(..) => {} // check the hash
// hash is irrelevant, no version specified
None => return Some(hash.to_owned())
}
debug!("matching -- {}, vers ok (requested {})", file,
self.crate_id.version);
debug!("matching -- {}, vers ok", file);
// hashes in filenames are prefixes of the "true hash"
if self.hash.is_empty() || self.hash.starts_with(hash) {
debug!("matching -- {}, hash ok (requested {})", file, self.hash);
if self.id_hash == hash.as_slice() {
debug!("matching -- {}, hash ok", file);
Some(hash.to_owned())
} else {
None
@ -270,7 +281,7 @@ impl<'a> Context<'a> {
// FIXME(#10786): for an optimization, we only read one of the library's
// metadata sections. In theory we should read both, but
// reading dylib metadata is quite slow.
fn extract_one(&self, m: HashSet<Path>, flavor: &str,
fn extract_one(&mut self, m: HashSet<Path>, flavor: &str,
slot: &mut Option<MetadataBlob>) -> Option<Path> {
if m.len() == 0 { return None }
if m.len() > 1 {
@ -290,7 +301,7 @@ impl<'a> Context<'a> {
info!("{} reading meatadata from: {}", flavor, lib.display());
match get_metadata_section(self.os, &lib) {
Some(blob) => {
if crate_matches(blob.as_slice(), self.crate_id, self.hash){
if self.crate_matches(blob.as_slice()) {
*slot = Some(blob);
} else {
info!("metadata mismatch");
@ -306,6 +317,22 @@ impl<'a> Context<'a> {
return Some(lib);
}
fn crate_matches(&mut self, crate_data: &[u8]) -> bool {
let other_id = decoder::get_crate_id(crate_data);
if !self.crate_id.matches(&other_id) { return false }
match self.hash {
None => true,
Some(hash) => {
if *hash != decoder::get_crate_hash(crate_data) {
self.rejected_via_hash = true;
false
} else {
true
}
}
}
}
// Returns the corresponding (prefix, suffix) that files need to have for
// dynamic libraries
fn dylibname(&self) -> (&'static str, &'static str) {
@ -323,15 +350,6 @@ pub fn note_crateid_attr(diag: @SpanHandler, crateid: &CrateId) {
diag.handler().note(format!("crate_id: {}", crateid.to_str()));
}
fn crate_matches(crate_data: &[u8], crate_id: &CrateId, hash: &str) -> bool {
let other_id = decoder::get_crate_id(crate_data);
if !crate_id.matches(&other_id) { return false }
if hash != "" && hash != decoder::get_crate_hash(crate_data).as_slice() {
return false
}
return true;
}
impl ArchiveMetadata {
fn new(ar: ArchiveRO) -> Option<ArchiveMetadata> {
let data: &'static [u8] = {

View File

@ -2453,7 +2453,8 @@ pub fn decl_crate_map(sess: session::Session, mapmeta: LinkMeta,
let sym_name = if is_top {
~"_rust_crate_map_toplevel"
} else {
symname("_rust_crate_map_" + mapmeta.crateid.name, mapmeta.crate_hash,
symname("_rust_crate_map_" + mapmeta.crateid.name,
mapmeta.crate_hash.as_str(),
mapmeta.crateid.version_or_default())
};
@ -2487,7 +2488,7 @@ pub fn fill_crate_map(ccx: @CrateContext, map: ValueRef) {
while cstore.have_crate_data(i) {
let cdata = cstore.get_crate_data(i);
let nm = symname(format!("_rust_crate_map_{}", cdata.name),
cstore.get_crate_hash(i),
cstore.get_crate_hash(i).as_str(),
cstore.get_crate_id(i).version_or_default());
let cr = nm.with_c_str(|buf| {
unsafe {
@ -2609,9 +2610,7 @@ pub fn trans_crate(sess: session::Session,
}
}
let mut symbol_hasher = Sha256::new();
let link_meta = link::build_link_meta(krate.attrs, output,
&mut symbol_hasher);
let link_meta = link::build_link_meta(&krate, output);
// Append ".rs" to crate name as LLVM module identifier.
//
@ -2621,16 +2620,16 @@ pub fn trans_crate(sess: session::Session,
// crashes if the module identifer is same as other symbols
// such as a function name in the module.
// 1. http://llvm.org/bugs/show_bug.cgi?id=11479
let llmod_id = link_meta.crateid.name.clone() + ".rs";
let llmod_id = link_meta.crateid.name + ".rs";
let ccx = @CrateContext::new(sess,
llmod_id,
analysis.ty_cx,
analysis.exp_map2,
analysis.maps,
symbol_hasher,
link_meta,
analysis.reachable);
llmod_id,
analysis.ty_cx,
analysis.exp_map2,
analysis.maps,
Sha256::new(),
link_meta,
analysis.reachable);
{
let _icx = push_ctxt("text");
trans_mod(ccx, &krate.module);

View File

@ -329,7 +329,7 @@ pub fn trans_intrinsic(ccx: @CrateContext,
let hash = ty::hash_crate_independent(
ccx.tcx,
substs.tys[0],
ccx.link_meta.crate_hash.clone());
&ccx.link_meta.crate_hash);
// NB: This needs to be kept in lockstep with the TypeId struct in
// libstd/unstable/intrinsics.rs
let val = C_named_struct(type_of::type_of(ccx, output_type), [C_u64(hash)]);

View File

@ -10,6 +10,7 @@
#[allow(non_camel_case_types)];
use back::svh::Svh;
use driver::session;
use metadata::csearch;
use metadata;
@ -4882,7 +4883,7 @@ pub fn trait_method_of_method(tcx: ctxt,
/// Creates a hash of the type `t` which will be the same no matter what crate
/// context it's calculated within. This is used by the `type_id` intrinsic.
pub fn hash_crate_independent(tcx: ctxt, t: t, local_hash: ~str) -> u64 {
pub fn hash_crate_independent(tcx: ctxt, t: t, svh: &Svh) -> u64 {
let mut state = sip::SipState::new();
macro_rules! byte( ($b:expr) => { ($b as u8).hash(&mut state) } );
macro_rules! hash( ($e:expr) => { $e.hash(&mut state) } );
@ -4913,11 +4914,11 @@ pub fn hash_crate_independent(tcx: ctxt, t: t, local_hash: ~str) -> u64 {
};
let did = |state: &mut sip::SipState, did: DefId| {
let h = if ast_util::is_local(did) {
local_hash.clone()
svh.clone()
} else {
tcx.sess.cstore.get_crate_hash(did.krate)
};
h.as_bytes().hash(state);
h.as_str().hash(state);
did.node.hash(state);
};
let mt = |state: &mut sip::SipState, mt: mt| {

View File

@ -0,0 +1,13 @@
// Copyright 2014 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_id = "a"];
pub fn foo<T>() {}

View File

@ -0,0 +1,14 @@
// Copyright 2014 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_id = "a"];
pub fn foo<T>() { println!("hello!"); }

View File

@ -0,0 +1,15 @@
// Copyright 2014 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_id = "b"];
extern crate a;
pub fn foo() { a::foo::<int>(); }

View File

@ -0,0 +1,14 @@
// Copyright 2014 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.
extern crate foo = ""; //~ ERROR: malformed crate id
extern crate bar = "#a"; //~ ERROR: malformed crate id
fn main() {}

View File

@ -0,0 +1,20 @@
// Copyright 2014 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.
// note that these aux-build directives must be in this order
// aux-build:changing-crates-a1.rs
// aux-build:changing-crates-b.rs
// aux-build:changing-crates-a2.rs
extern crate a;
extern crate b; //~ ERROR: found possibly newer version of crate `a` which `b` depends on
//~^ NOTE: perhaps this crate needs to be recompiled
fn main() {}