Merge pull request #34604 from arielb1/metadata-hash-beta
More robust metadata lock - beta edition
diff --git a/src/librustc_metadata/common.rs b/src/librustc_metadata/common.rs
index 2b972af..b3ca399 100644
--- a/src/librustc_metadata/common.rs
+++ b/src/librustc_metadata/common.rs
@@ -252,3 +252,7 @@
}
pub const tag_panic_strategy: usize = 0x114;
+
+// NB: increment this if you change the format of metadata such that
+// rustc_version can't be found.
+pub const metadata_encoding_version : &'static [u8] = &[b'r', b'u', b's', b't', 0, 0, 0, 2];
diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs
index ad6bb2d..90f4ebc 100644
--- a/src/librustc_metadata/creader.rs
+++ b/src/librustc_metadata/creader.rs
@@ -12,7 +12,6 @@
//! Validates all used crates and extern libraries and loads their metadata
-use common::rustc_version;
use cstore::{self, CStore, CrateSource, MetadataBlob};
use decoder;
use loader::{self, CratePaths};
@@ -234,25 +233,6 @@
return ret;
}
- fn verify_rustc_version(&self,
- name: &str,
- span: Span,
- metadata: &MetadataBlob) {
- let crate_rustc_version = decoder::crate_rustc_version(metadata.as_slice());
- if crate_rustc_version != Some(rustc_version()) {
- let mut err = struct_span_fatal!(self.sess, span, E0514,
- "the crate `{}` has been compiled with {}, which is \
- incompatible with this version of rustc",
- name,
- crate_rustc_version
- .as_ref().map(|s| &**s)
- .unwrap_or("an old version of rustc"));
- err.help("consider removing the compiled binaries and recompiling \
- with your current version of rustc");
- err.emit();
- }
- }
-
fn verify_no_symbol_conflicts(&self,
span: Span,
metadata: &MetadataBlob) {
@@ -294,7 +274,6 @@
explicitly_linked: bool)
-> (ast::CrateNum, Rc<cstore::crate_metadata>,
cstore::CrateSource) {
- self.verify_rustc_version(name, span, &lib.metadata);
self.verify_no_symbol_conflicts(span, &lib.metadata);
// Claim this crate number and cache it
@@ -379,6 +358,7 @@
rejected_via_hash: vec!(),
rejected_via_triple: vec!(),
rejected_via_kind: vec!(),
+ rejected_via_version: vec!(),
should_match_name: true,
};
match self.load(&mut load_ctxt) {
@@ -506,6 +486,7 @@
rejected_via_hash: vec!(),
rejected_via_triple: vec!(),
rejected_via_kind: vec!(),
+ rejected_via_version: vec!(),
should_match_name: true,
};
let library = self.load(&mut load_ctxt).or_else(|| {
diff --git a/src/librustc_metadata/csearch.rs b/src/librustc_metadata/csearch.rs
index b87b549..d1f6f7e 100644
--- a/src/librustc_metadata/csearch.rs
+++ b/src/librustc_metadata/csearch.rs
@@ -9,6 +9,7 @@
// except according to those terms.
use cstore;
+use common;
use decoder;
use encoder;
use loader;
@@ -587,7 +588,7 @@
fn metadata_encoding_version(&self) -> &[u8]
{
- encoder::metadata_encoding_version
+ common::metadata_encoding_version
}
/// Returns a map from a sufficiently visible external item (i.e. an external item that is
diff --git a/src/librustc_metadata/cstore.rs b/src/librustc_metadata/cstore.rs
index 2e1bdf2..5464f7e 100644
--- a/src/librustc_metadata/cstore.rs
+++ b/src/librustc_metadata/cstore.rs
@@ -15,6 +15,7 @@
pub use self::MetadataBlob::*;
+use common;
use creader;
use decoder;
use index;
@@ -311,20 +312,25 @@
}
impl MetadataBlob {
- pub fn as_slice<'a>(&'a self) -> &'a [u8] {
- let slice = match *self {
+ pub fn as_slice_raw<'a>(&'a self) -> &'a [u8] {
+ match *self {
MetadataVec(ref vec) => &vec[..],
MetadataArchive(ref ar) => ar.as_slice(),
- };
- if slice.len() < 4 {
+ }
+ }
+
+ pub fn as_slice<'a>(&'a self) -> &'a [u8] {
+ let slice = self.as_slice_raw();
+ let len_offset = 4 + common::metadata_encoding_version.len();
+ if slice.len() < len_offset+4 {
&[] // corrupt metadata
} else {
- let len = (((slice[0] as u32) << 24) |
- ((slice[1] as u32) << 16) |
- ((slice[2] as u32) << 8) |
- ((slice[3] as u32) << 0)) as usize;
- if len + 4 <= slice.len() {
- &slice[4.. len + 4]
+ let len = (((slice[len_offset+0] as u32) << 24) |
+ ((slice[len_offset+1] as u32) << 16) |
+ ((slice[len_offset+2] as u32) << 8) |
+ ((slice[len_offset+3] as u32) << 0)) as usize;
+ if len <= slice.len() - 4 - len_offset {
+ &slice[len_offset + 4..len_offset + len + 4]
} else {
&[] // corrupt or old metadata
}
diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs
index 9286010..e862dbb 100644
--- a/src/librustc_metadata/encoder.rs
+++ b/src/librustc_metadata/encoder.rs
@@ -1851,10 +1851,6 @@
}
}
-// NB: Increment this as you change the metadata encoding version.
-#[allow(non_upper_case_globals)]
-pub const metadata_encoding_version : &'static [u8] = &[b'r', b'u', b's', b't', 0, 0, 0, 2 ];
-
pub fn encode_metadata(ecx: EncodeContext, krate: &hir::Crate) -> Vec<u8> {
let mut wr = Cursor::new(Vec::new());
@@ -1888,12 +1884,25 @@
// the length of the metadata to the start of the metadata. Later on this
// will allow us to slice the metadata to the precise length that we just
// generated regardless of trailing bytes that end up in it.
- let len = v.len() as u32;
- v.insert(0, (len >> 0) as u8);
- v.insert(0, (len >> 8) as u8);
- v.insert(0, (len >> 16) as u8);
- v.insert(0, (len >> 24) as u8);
- return v;
+ //
+ // We also need to store the metadata encoding version here, because
+ // rlibs don't have it. To get older versions of rustc to ignore
+ // this metadata, there are 4 zero bytes at the start, which are
+ // treated as a length of 0 by old compilers.
+
+ let len = v.len();
+ let mut result = vec![];
+ result.push(0);
+ result.push(0);
+ result.push(0);
+ result.push(0);
+ result.extend(metadata_encoding_version.iter().cloned());
+ result.push((len >> 24) as u8);
+ result.push((len >> 16) as u8);
+ result.push((len >> 8) as u8);
+ result.push((len >> 0) as u8);
+ result.extend(v);
+ result
}
fn encode_metadata_inner(rbml_w: &mut Encoder,
diff --git a/src/librustc_metadata/loader.rs b/src/librustc_metadata/loader.rs
index a5b1c3d..dc10391 100644
--- a/src/librustc_metadata/loader.rs
+++ b/src/librustc_metadata/loader.rs
@@ -213,8 +213,8 @@
//! metadata::loader or metadata::creader for all the juicy details!
use cstore::{MetadataBlob, MetadataVec, MetadataArchive};
+use common::{metadata_encoding_version, rustc_version};
use decoder;
-use encoder;
use rustc::hir::svh::Svh;
use rustc::session::Session;
@@ -260,6 +260,7 @@
pub rejected_via_hash: Vec<CrateMismatch>,
pub rejected_via_triple: Vec<CrateMismatch>,
pub rejected_via_kind: Vec<CrateMismatch>,
+ pub rejected_via_version: Vec<CrateMismatch>,
pub should_match_name: bool,
}
@@ -336,6 +337,10 @@
struct_span_err!(self.sess, self.span, E0462,
"found staticlib `{}` instead of rlib or dylib{}",
self.ident, add)
+ } else if !self.rejected_via_version.is_empty() {
+ struct_span_err!(self.sess, self.span, E0514,
+ "found crate `{}` compiled by an incompatible version of rustc{}",
+ self.ident, add)
} else {
struct_span_err!(self.sess, self.span, E0463,
"can't find crate for `{}`{}",
@@ -350,7 +355,7 @@
}
}
if !self.rejected_via_hash.is_empty() {
- err.note("perhaps this crate needs to be recompiled?");
+ err.note("perhaps that crate needs to be recompiled?");
let mismatches = self.rejected_via_hash.iter();
for (i, &CrateMismatch{ ref path, .. }) in mismatches.enumerate() {
err.note(&format!("crate `{}` path #{}: {}",
@@ -367,13 +372,22 @@
}
}
if !self.rejected_via_kind.is_empty() {
- err.help("please recompile this crate using --crate-type lib");
+ err.help("please recompile that crate using --crate-type lib");
let mismatches = self.rejected_via_kind.iter();
for (i, &CrateMismatch { ref path, .. }) in mismatches.enumerate() {
err.note(&format!("crate `{}` path #{}: {}",
self.ident, i+1, path.display()));
}
}
+ if !self.rejected_via_version.is_empty() {
+ err.help(&format!("please recompile that crate using this compiler ({})",
+ rustc_version()));
+ let mismatches = self.rejected_via_version.iter();
+ for (i, &CrateMismatch { ref path, ref got }) in mismatches.enumerate() {
+ err.note(&format!("crate `{}` path #{}: {} compiled by {:?}",
+ self.ident, i+1, path.display(), got));
+ }
+ }
err.emit();
self.sess.abort_if_errors();
@@ -591,6 +605,17 @@
}
fn crate_matches(&mut self, crate_data: &[u8], libpath: &Path) -> Option<Svh> {
+ let crate_rustc_version = decoder::crate_rustc_version(crate_data);
+ if crate_rustc_version != Some(rustc_version()) {
+ let message = crate_rustc_version.unwrap_or(format!("an unknown compiler"));
+ info!("Rejecting via version: expected {} got {}", rustc_version(), message);
+ self.rejected_via_version.push(CrateMismatch {
+ path: libpath.to_path_buf(),
+ got: message
+ });
+ return None;
+ }
+
if self.should_match_name {
match decoder::maybe_get_crate_name(crate_data) {
Some(ref name) if self.crate_name == *name => {}
@@ -742,6 +767,21 @@
pub fn as_slice<'a>(&'a self) -> &'a [u8] { unsafe { &*self.data } }
}
+fn verify_decompressed_encoding_version(blob: &MetadataBlob, filename: &Path)
+ -> Result<(), String>
+{
+ let data = blob.as_slice_raw();
+ if data.len() < 4+metadata_encoding_version.len() ||
+ !<[u8]>::eq(&data[..4], &[0, 0, 0, 0]) ||
+ &data[4..4+metadata_encoding_version.len()] != metadata_encoding_version
+ {
+ Err((format!("incompatible metadata version found: '{}'",
+ filename.display())))
+ } else {
+ Ok(())
+ }
+}
+
// Just a small wrapper to time how long reading metadata takes.
fn get_metadata_section(target: &Target, flavor: CrateFlavor, filename: &Path)
-> Result<MetadataBlob, String> {
@@ -772,7 +812,10 @@
return match ArchiveMetadata::new(archive).map(|ar| MetadataArchive(ar)) {
None => Err(format!("failed to read rlib metadata: '{}'",
filename.display())),
- Some(blob) => Ok(blob)
+ Some(blob) => {
+ try!(verify_decompressed_encoding_version(&blob, filename));
+ Ok(blob)
+ }
};
}
unsafe {
@@ -801,12 +844,12 @@
let cbuf = llvm::LLVMGetSectionContents(si.llsi);
let csz = llvm::LLVMGetSectionSize(si.llsi) as usize;
let cvbuf: *const u8 = cbuf as *const u8;
- let vlen = encoder::metadata_encoding_version.len();
+ let vlen = metadata_encoding_version.len();
debug!("checking {} bytes of metadata-version stamp",
vlen);
let minsz = cmp::min(vlen, csz);
let buf0 = slice::from_raw_parts(cvbuf, minsz);
- let version_ok = buf0 == encoder::metadata_encoding_version;
+ let version_ok = buf0 == metadata_encoding_version;
if !version_ok {
return Err((format!("incompatible metadata version found: '{}'",
filename.display())));
@@ -817,7 +860,11 @@
csz - vlen);
let bytes = slice::from_raw_parts(cvbuf1, csz - vlen);
match flate::inflate_bytes(bytes) {
- Ok(inflated) => return Ok(MetadataVec(inflated)),
+ Ok(inflated) => {
+ let blob = MetadataVec(inflated);
+ try!(verify_decompressed_encoding_version(&blob, filename));
+ return Ok(blob);
+ }
Err(_) => {}
}
}
diff --git a/src/test/compile-fail/changing-crates.rs b/src/test/compile-fail/changing-crates.rs
index 0b42015..f74855a 100644
--- a/src/test/compile-fail/changing-crates.rs
+++ b/src/test/compile-fail/changing-crates.rs
@@ -17,7 +17,7 @@
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
+//~| NOTE: perhaps that crate needs to be recompiled
//~| NOTE: crate `a` path #1:
//~| NOTE: crate `b` path #1:
diff --git a/src/test/compile-fail/svh-change-lit.rs b/src/test/compile-fail/svh-change-lit.rs
index eb92bcf..1638caa 100644
--- a/src/test/compile-fail/svh-change-lit.rs
+++ b/src/test/compile-fail/svh-change-lit.rs
@@ -17,7 +17,7 @@
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
+//~| NOTE: perhaps that crate needs to be recompiled
//~| NOTE: crate `a` path #1:
//~| NOTE: crate `b` path #1:
diff --git a/src/test/compile-fail/svh-change-significant-cfg.rs b/src/test/compile-fail/svh-change-significant-cfg.rs
index 7c9e0d3..99523ca 100644
--- a/src/test/compile-fail/svh-change-significant-cfg.rs
+++ b/src/test/compile-fail/svh-change-significant-cfg.rs
@@ -17,7 +17,7 @@
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
+//~| NOTE: perhaps that crate needs to be recompiled
//~| NOTE: crate `a` path #1:
//~| NOTE: crate `b` path #1:
diff --git a/src/test/compile-fail/svh-change-trait-bound.rs b/src/test/compile-fail/svh-change-trait-bound.rs
index 1e6a723..dcf4859 100644
--- a/src/test/compile-fail/svh-change-trait-bound.rs
+++ b/src/test/compile-fail/svh-change-trait-bound.rs
@@ -17,7 +17,7 @@
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
+//~| NOTE: perhaps that crate needs to be recompiled
//~| NOTE: crate `a` path #1:
//~| NOTE: crate `b` path #1:
diff --git a/src/test/compile-fail/svh-change-type-arg.rs b/src/test/compile-fail/svh-change-type-arg.rs
index 73c35ee..7e51ca4 100644
--- a/src/test/compile-fail/svh-change-type-arg.rs
+++ b/src/test/compile-fail/svh-change-type-arg.rs
@@ -17,7 +17,7 @@
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
+//~| NOTE: perhaps that crate needs to be recompiled
//~| NOTE: crate `a` path #1:
//~| NOTE: crate `b` path #1:
diff --git a/src/test/compile-fail/svh-change-type-ret.rs b/src/test/compile-fail/svh-change-type-ret.rs
index b8908e2..54ca87d 100644
--- a/src/test/compile-fail/svh-change-type-ret.rs
+++ b/src/test/compile-fail/svh-change-type-ret.rs
@@ -17,7 +17,7 @@
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
+//~| NOTE: perhaps that crate needs to be recompiled
//~| NOTE: crate `a` path #1:
//~| NOTE: crate `b` path #1:
diff --git a/src/test/compile-fail/svh-change-type-static.rs b/src/test/compile-fail/svh-change-type-static.rs
index 291e441..ea90faa 100644
--- a/src/test/compile-fail/svh-change-type-static.rs
+++ b/src/test/compile-fail/svh-change-type-static.rs
@@ -17,7 +17,7 @@
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
+//~| NOTE: perhaps that crate needs to be recompiled
//~| NOTE: crate `a` path #1:
//~| NOTE: crate `b` path #1:
diff --git a/src/test/compile-fail/svh-use-trait.rs b/src/test/compile-fail/svh-use-trait.rs
index ed816a9..c0a5a0a 100644
--- a/src/test/compile-fail/svh-use-trait.rs
+++ b/src/test/compile-fail/svh-use-trait.rs
@@ -22,7 +22,7 @@
extern crate uta;
extern crate utb; //~ ERROR: found possibly newer version of crate `uta` which `utb` depends
-//~| NOTE: perhaps this crate needs to be recompiled?
+//~| NOTE: perhaps that crate needs to be recompiled?
//~| NOTE: crate `uta` path #1:
//~| NOTE: crate `utb` path #1:
diff --git a/src/test/run-make/many-crates-but-no-match/Makefile b/src/test/run-make/many-crates-but-no-match/Makefile
index edf8e9d..0da4af3 100644
--- a/src/test/run-make/many-crates-but-no-match/Makefile
+++ b/src/test/run-make/many-crates-but-no-match/Makefile
@@ -28,7 +28,7 @@
# Ensure crateC fails to compile since A1 is "missing" and A2/A3 hashes do not match
$(RUSTC) -L $(A2) -L $(A3) crateC.rs >$(LOG) 2>&1 || true
grep "error: found possibly newer version of crate \`crateA\` which \`crateB\` depends on" $(LOG)
- grep "note: perhaps this crate needs to be recompiled?" $(LOG)
+ grep "note: perhaps that crate needs to be recompiled?" $(LOG)
grep "note: crate \`crateA\` path #1:" $(LOG)
grep "note: crate \`crateA\` path #2:" $(LOG)
grep "note: crate \`crateB\` path #1:" $(LOG)