Skip to content
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

Improve error when an .rlib can't be parsed #88368

Merged
merged 1 commit into from
Nov 8, 2021
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
1 change: 1 addition & 0 deletions compiler/rustc_error_codes/src/error_codes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -484,6 +484,7 @@ E0782: include_str!("./error_codes/E0782.md"),
E0783: include_str!("./error_codes/E0783.md"),
E0784: include_str!("./error_codes/E0784.md"),
E0785: include_str!("./error_codes/E0785.md"),
E0786: include_str!("./error_codes/E0786.md"),
;
// E0006, // merged with E0005
// E0008, // cannot bind by-move into a pattern guard
Expand Down
14 changes: 14 additions & 0 deletions compiler/rustc_error_codes/src/error_codes/E0786.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
A metadata file was invalid.

Erroneous code example:

```ignore (needs extern files)
use ::foo; // error: found invalid metadata files for crate `foo`
```

When loading crates, each crate must have a valid metadata file.
Invalid files could be caused by filesystem corruption,
an IO error while reading the file, or (rarely) a bug in the compiler itself.

Consider deleting the file and recreating it,
or reporting a bug against the compiler.
90 changes: 75 additions & 15 deletions compiler/rustc_metadata/src/locator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@ impl<'a> CrateLocator<'a> {
self.crate_rejections.via_kind.clear();
self.crate_rejections.via_version.clear();
self.crate_rejections.via_filename.clear();
self.crate_rejections.via_invalid.clear();
}

crate fn maybe_load_library_crate(&mut self) -> Result<Option<Library>, CrateError> {
Expand Down Expand Up @@ -548,7 +549,17 @@ impl<'a> CrateLocator<'a> {
continue;
}
}
Err(err) => {
Err(MetadataError::LoadFailure(err)) => {
info!("no metadata found: {}", err);
// The file was present and created by the same compiler version, but we
// couldn't load it for some reason. Give a hard error instead of silently
// ignoring it, but only if we would have given an error anyway.
bjorn3 marked this conversation as resolved.
Show resolved Hide resolved
self.crate_rejections
.via_invalid
.push(CrateMismatch { path: lib, got: err });
continue;
}
Err(err @ MetadataError::NotPresent(_)) => {
info!("no metadata found: {}", err);
continue;
}
Expand Down Expand Up @@ -726,25 +737,28 @@ impl<'a> CrateLocator<'a> {
fn get_metadata_section(
target: &Target,
flavor: CrateFlavor,
filename: &Path,
filename: &'p Path,
loader: &dyn MetadataLoader,
) -> Result<MetadataBlob, String> {
) -> Result<MetadataBlob, MetadataError<'p>> {
if !filename.exists() {
return Err(format!("no such file: '{}'", filename.display()));
return Err(MetadataError::NotPresent(filename));
}
let raw_bytes: MetadataRef = match flavor {
CrateFlavor::Rlib => loader.get_rlib_metadata(target, filename)?,
CrateFlavor::Rlib => {
loader.get_rlib_metadata(target, filename).map_err(MetadataError::LoadFailure)?
}
CrateFlavor::Dylib => {
let buf = loader.get_dylib_metadata(target, filename)?;
let buf =
loader.get_dylib_metadata(target, filename).map_err(MetadataError::LoadFailure)?;
// The header is uncompressed
let header_len = METADATA_HEADER.len();
debug!("checking {} bytes of metadata-version stamp", header_len);
let header = &buf[..cmp::min(header_len, buf.len())];
if header != METADATA_HEADER {
return Err(format!(
"incompatible metadata version found: '{}'",
return Err(MetadataError::LoadFailure(format!(
"invalid metadata version found: {}",
filename.display()
));
)));
}

// Header is okay -> inflate the actual metadata
Expand All @@ -756,17 +770,28 @@ fn get_metadata_section(
match FrameDecoder::new(compressed_bytes).read_to_end(&mut inflated) {
Ok(_) => rustc_erase_owner!(OwningRef::new(inflated).map_owner_box()),
Err(_) => {
return Err(format!("failed to decompress metadata: {}", filename.display()));
return Err(MetadataError::LoadFailure(format!(
"failed to decompress metadata: {}",
filename.display()
)));
}
}
}
CrateFlavor::Rmeta => {
// mmap the file, because only a small fraction of it is read.
let file = std::fs::File::open(filename)
.map_err(|_| format!("failed to open rmeta metadata: '{}'", filename.display()))?;
let file = std::fs::File::open(filename).map_err(|_| {
MetadataError::LoadFailure(format!(
"failed to open rmeta metadata: '{}'",
filename.display()
))
})?;
let mmap = unsafe { Mmap::map(file) };
let mmap = mmap
.map_err(|_| format!("failed to mmap rmeta metadata: '{}'", filename.display()))?;
let mmap = mmap.map_err(|_| {
MetadataError::LoadFailure(format!(
"failed to mmap rmeta metadata: '{}'",
filename.display()
))
})?;

rustc_erase_owner!(OwningRef::new(mmap).map_owner_box())
}
Expand All @@ -775,7 +800,10 @@ fn get_metadata_section(
if blob.is_compatible() {
Ok(blob)
} else {
Err(format!("incompatible metadata version found: '{}'", filename.display()))
Err(MetadataError::LoadFailure(format!(
"invalid metadata version found: {}",
filename.display()
)))
}
}

Expand Down Expand Up @@ -854,6 +882,7 @@ struct CrateRejections {
via_kind: Vec<CrateMismatch>,
via_version: Vec<CrateMismatch>,
via_filename: Vec<CrateMismatch>,
via_invalid: Vec<CrateMismatch>,
}

/// Candidate rejection reasons collected during crate search.
Expand Down Expand Up @@ -883,6 +912,24 @@ crate enum CrateError {
NonDylibPlugin(Symbol),
}

enum MetadataError<'a> {
/// The file was missing.
NotPresent(&'a Path),
/// The file was present and invalid.
LoadFailure(String),
}

impl fmt::Display for MetadataError<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MetadataError::NotPresent(filename) => {
f.write_str(&format!("no such file: '{}'", filename.display()))
}
MetadataError::LoadFailure(msg) => f.write_str(msg),
}
}
}

impl CrateError {
crate fn report(self, sess: &Session, span: Span, missing_core: bool) -> ! {
let mut err = match self {
Expand Down Expand Up @@ -1064,6 +1111,19 @@ impl CrateError {
}
err.note(&msg);
err
} else if !locator.crate_rejections.via_invalid.is_empty() {
let mut err = struct_span_err!(
sess,
span,
E0786,
"found invalid metadata files for crate `{}`{}",
crate_name,
add,
);
for CrateMismatch { path: _, got } in locator.crate_rejections.via_invalid {
err.note(&got);
}
err
} else {
let mut err = struct_span_err!(
sess,
Expand Down
2 changes: 1 addition & 1 deletion src/test/run-make-fulldeps/invalid-library/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
all:
touch $(TMPDIR)/lib.rmeta
$(AR) crus $(TMPDIR)/libfoo-ffffffff-1.0.rlib $(TMPDIR)/lib.rmeta
$(RUSTC) foo.rs 2>&1 | $(CGREP) "can't find crate for"
$(RUSTC) foo.rs 2>&1 | $(CGREP) "found invalid metadata"
7 changes: 7 additions & 0 deletions src/test/run-make/invalid-so/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
include ../../run-make-fulldeps/tools.mk

DYLIB_NAME := $(shell echo | $(RUSTC) --crate-name foo --crate-type dylib --print file-names -)

all:
echo >> $(TMPDIR)/$(DYLIB_NAME)
$(RUSTC) --crate-type lib --extern foo=$(TMPDIR)/$(DYLIB_NAME) bar.rs 2>&1 | $(CGREP) 'invalid metadata files for crate `foo`'
1 change: 1 addition & 0 deletions src/test/run-make/invalid-so/bar.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
extern crate foo;
Empty file.
8 changes: 8 additions & 0 deletions src/test/ui/crate-loading/invalid-rlib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// compile-flags: --crate-type lib --extern foo={{src-base}}/crate-loading/auxiliary/libfoo.rlib
// normalize-stderr-test: "failed to mmap file '.*auxiliary/libfoo.rlib':.*" -> "failed to mmap file 'auxiliary/libfoo.rlib'"
// don't emit warn logging, it's basically the same as the errors and it's annoying to normalize
// rustc-env:RUSTC_LOG=error
// edition:2018
#![no_std]
use ::foo; //~ ERROR invalid metadata files for crate `foo`
//~| NOTE failed to mmap file
11 changes: 11 additions & 0 deletions src/test/ui/crate-loading/invalid-rlib.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
error[E0786]: found invalid metadata files for crate `foo`
--> $DIR/invalid-rlib.rs:7:7
|
LL | use ::foo;
| ^^^
|
= note: failed to mmap file 'auxiliary/libfoo.rlib'

error: aborting due to previous error

For more information about this error, try `rustc --explain E0786`.