Skip to content

More robust metadata lock - beta edition #34604

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 2, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/librustc_metadata/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,3 +252,7 @@ pub fn rustc_version() -> String {
}

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];
23 changes: 2 additions & 21 deletions src/librustc_metadata/creader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -234,25 +233,6 @@ impl<'a> CrateReader<'a> {
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) {
Expand Down Expand Up @@ -294,7 +274,6 @@ impl<'a> CrateReader<'a> {
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
Expand Down Expand Up @@ -379,6 +358,7 @@ impl<'a> CrateReader<'a> {
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) {
Expand Down Expand Up @@ -506,6 +486,7 @@ impl<'a> CrateReader<'a> {
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(|| {
Expand Down
3 changes: 2 additions & 1 deletion src/librustc_metadata/csearch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
// except according to those terms.

use cstore;
use common;
use decoder;
use encoder;
use loader;
Expand Down Expand Up @@ -587,7 +588,7 @@ impl<'tcx> CrateStore<'tcx> for cstore::CStore {

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
Expand Down
26 changes: 16 additions & 10 deletions src/librustc_metadata/cstore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

pub use self::MetadataBlob::*;

use common;
use creader;
use decoder;
use index;
Expand Down Expand Up @@ -311,20 +312,25 @@ impl crate_metadata {
}

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
}
Expand Down
29 changes: 19 additions & 10 deletions src/librustc_metadata/encoder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1851,10 +1851,6 @@ fn encode_panic_strategy(rbml_w: &mut Encoder, ecx: &EncodeContext) {
}
}

// 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());

Expand Down Expand Up @@ -1888,12 +1884,25 @@ pub fn encode_metadata(ecx: EncodeContext, krate: &hir::Crate) -> Vec<u8> {
// 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,
Expand Down
61 changes: 54 additions & 7 deletions src/librustc_metadata/loader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -260,6 +260,7 @@ pub struct Context<'a> {
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,
}

Expand Down Expand Up @@ -336,6 +337,10 @@ impl<'a> Context<'a> {
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 `{}`{}",
Expand All @@ -350,7 +355,7 @@ impl<'a> Context<'a> {
}
}
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 #{}: {}",
Expand All @@ -367,13 +372,22 @@ impl<'a> Context<'a> {
}
}
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();
Expand Down Expand Up @@ -591,6 +605,17 @@ impl<'a> Context<'a> {
}

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 => {}
Expand Down Expand Up @@ -742,6 +767,21 @@ impl ArchiveMetadata {
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> {
Expand Down Expand Up @@ -772,7 +812,10 @@ fn get_metadata_section_imp(target: &Target, flavor: CrateFlavor, filename: &Pat
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 {
Expand Down Expand Up @@ -801,12 +844,12 @@ fn get_metadata_section_imp(target: &Target, flavor: CrateFlavor, filename: &Pat
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())));
Expand All @@ -817,7 +860,11 @@ fn get_metadata_section_imp(target: &Target, flavor: CrateFlavor, filename: &Pat
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(_) => {}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/test/compile-fail/changing-crates.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
2 changes: 1 addition & 1 deletion src/test/compile-fail/svh-change-lit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
2 changes: 1 addition & 1 deletion src/test/compile-fail/svh-change-significant-cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
2 changes: 1 addition & 1 deletion src/test/compile-fail/svh-change-trait-bound.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
2 changes: 1 addition & 1 deletion src/test/compile-fail/svh-change-type-arg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
2 changes: 1 addition & 1 deletion src/test/compile-fail/svh-change-type-ret.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
2 changes: 1 addition & 1 deletion src/test/compile-fail/svh-change-type-static.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
2 changes: 1 addition & 1 deletion src/test/compile-fail/svh-use-trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
2 changes: 1 addition & 1 deletion src/test/run-make/many-crates-but-no-match/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ all:
# 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)