Skip to content

Commit 257ac1b

Browse files
committed
Improve error when an .rlib can't be parsed
This usually describes either an error in the compiler itself or some sort of IO error. Either way, we should report it to the user rather than just saying "crate not found". This only gives an error if the crate couldn't be loaded at all - if the compiler finds another .rlib or .rmeta file which was valid, it will continue to compile the crate. Example output: ``` error[E0785]: found invalid metadata files for crate `foo` --> bar.rs:3:24 | 3 | println!("{}", foo::FOO_11_49[0]); | ^^^ | = warning: failed to parse rlib '/home/joshua/test-rustdoc/libfoo.rlib': Invalid archive extended name offset ```
1 parent 90a273b commit 257ac1b

File tree

9 files changed

+118
-16
lines changed

9 files changed

+118
-16
lines changed

compiler/rustc_error_codes/src/error_codes.rs

+1
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,7 @@ E0782: include_str!("./error_codes/E0782.md"),
484484
E0783: include_str!("./error_codes/E0783.md"),
485485
E0784: include_str!("./error_codes/E0784.md"),
486486
E0785: include_str!("./error_codes/E0785.md"),
487+
E0786: include_str!("./error_codes/E0786.md"),
487488
;
488489
// E0006, // merged with E0005
489490
// E0008, // cannot bind by-move into a pattern guard
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
A metadata file was invalid.
2+
3+
Erroneous code example:
4+
5+
```ignore (needs extern files)
6+
use ::foo; // error: found invalid metadata files for crate `foo`
7+
```
8+
9+
When loading crates, each crate must have a valid metadata file.
10+
Invalid files could be caused by filesystem corruption,
11+
an IO error while reading the file, or (rarely) a bug in the compiler itself.
12+
13+
Consider deleting the file and recreating it,
14+
or reporting a bug against the compiler.

compiler/rustc_metadata/src/locator.rs

+75-15
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,7 @@ impl<'a> CrateLocator<'a> {
350350
self.crate_rejections.via_kind.clear();
351351
self.crate_rejections.via_version.clear();
352352
self.crate_rejections.via_filename.clear();
353+
self.crate_rejections.via_invalid.clear();
353354
}
354355

355356
crate fn maybe_load_library_crate(&mut self) -> Result<Option<Library>, CrateError> {
@@ -548,7 +549,17 @@ impl<'a> CrateLocator<'a> {
548549
continue;
549550
}
550551
}
551-
Err(err) => {
552+
Err(MetadataError::LoadFailure(err)) => {
553+
info!("no metadata found: {}", err);
554+
// The file was present and created by the same compiler version, but we
555+
// couldn't load it for some reason. Give a hard error instead of silently
556+
// ignoring it, but only if we would have given an error anyway.
557+
self.crate_rejections
558+
.via_invalid
559+
.push(CrateMismatch { path: lib, got: err });
560+
continue;
561+
}
562+
Err(err @ MetadataError::NotPresent(_)) => {
552563
info!("no metadata found: {}", err);
553564
continue;
554565
}
@@ -726,25 +737,28 @@ impl<'a> CrateLocator<'a> {
726737
fn get_metadata_section(
727738
target: &Target,
728739
flavor: CrateFlavor,
729-
filename: &Path,
740+
filename: &'p Path,
730741
loader: &dyn MetadataLoader,
731-
) -> Result<MetadataBlob, String> {
742+
) -> Result<MetadataBlob, MetadataError<'p>> {
732743
if !filename.exists() {
733-
return Err(format!("no such file: '{}'", filename.display()));
744+
return Err(MetadataError::NotPresent(filename));
734745
}
735746
let raw_bytes: MetadataRef = match flavor {
736-
CrateFlavor::Rlib => loader.get_rlib_metadata(target, filename)?,
747+
CrateFlavor::Rlib => {
748+
loader.get_rlib_metadata(target, filename).map_err(MetadataError::LoadFailure)?
749+
}
737750
CrateFlavor::Dylib => {
738-
let buf = loader.get_dylib_metadata(target, filename)?;
751+
let buf =
752+
loader.get_dylib_metadata(target, filename).map_err(MetadataError::LoadFailure)?;
739753
// The header is uncompressed
740754
let header_len = METADATA_HEADER.len();
741755
debug!("checking {} bytes of metadata-version stamp", header_len);
742756
let header = &buf[..cmp::min(header_len, buf.len())];
743757
if header != METADATA_HEADER {
744-
return Err(format!(
745-
"incompatible metadata version found: '{}'",
758+
return Err(MetadataError::LoadFailure(format!(
759+
"invalid metadata version found: {}",
746760
filename.display()
747-
));
761+
)));
748762
}
749763

750764
// Header is okay -> inflate the actual metadata
@@ -756,17 +770,28 @@ fn get_metadata_section(
756770
match FrameDecoder::new(compressed_bytes).read_to_end(&mut inflated) {
757771
Ok(_) => rustc_erase_owner!(OwningRef::new(inflated).map_owner_box()),
758772
Err(_) => {
759-
return Err(format!("failed to decompress metadata: {}", filename.display()));
773+
return Err(MetadataError::LoadFailure(format!(
774+
"failed to decompress metadata: {}",
775+
filename.display()
776+
)));
760777
}
761778
}
762779
}
763780
CrateFlavor::Rmeta => {
764781
// mmap the file, because only a small fraction of it is read.
765-
let file = std::fs::File::open(filename)
766-
.map_err(|_| format!("failed to open rmeta metadata: '{}'", filename.display()))?;
782+
let file = std::fs::File::open(filename).map_err(|_| {
783+
MetadataError::LoadFailure(format!(
784+
"failed to open rmeta metadata: '{}'",
785+
filename.display()
786+
))
787+
})?;
767788
let mmap = unsafe { Mmap::map(file) };
768-
let mmap = mmap
769-
.map_err(|_| format!("failed to mmap rmeta metadata: '{}'", filename.display()))?;
789+
let mmap = mmap.map_err(|_| {
790+
MetadataError::LoadFailure(format!(
791+
"failed to mmap rmeta metadata: '{}'",
792+
filename.display()
793+
))
794+
})?;
770795

771796
rustc_erase_owner!(OwningRef::new(mmap).map_owner_box())
772797
}
@@ -775,7 +800,10 @@ fn get_metadata_section(
775800
if blob.is_compatible() {
776801
Ok(blob)
777802
} else {
778-
Err(format!("incompatible metadata version found: '{}'", filename.display()))
803+
Err(MetadataError::LoadFailure(format!(
804+
"invalid metadata version found: {}",
805+
filename.display()
806+
)))
779807
}
780808
}
781809

@@ -854,6 +882,7 @@ struct CrateRejections {
854882
via_kind: Vec<CrateMismatch>,
855883
via_version: Vec<CrateMismatch>,
856884
via_filename: Vec<CrateMismatch>,
885+
via_invalid: Vec<CrateMismatch>,
857886
}
858887

859888
/// Candidate rejection reasons collected during crate search.
@@ -883,6 +912,24 @@ crate enum CrateError {
883912
NonDylibPlugin(Symbol),
884913
}
885914

915+
enum MetadataError<'a> {
916+
/// The file was missing.
917+
NotPresent(&'a Path),
918+
/// The file was present and invalid.
919+
LoadFailure(String),
920+
}
921+
922+
impl fmt::Display for MetadataError<'_> {
923+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
924+
match self {
925+
MetadataError::NotPresent(filename) => {
926+
f.write_str(&format!("no such file: '{}'", filename.display()))
927+
}
928+
MetadataError::LoadFailure(msg) => f.write_str(msg),
929+
}
930+
}
931+
}
932+
886933
impl CrateError {
887934
crate fn report(self, sess: &Session, span: Span, missing_core: bool) -> ! {
888935
let mut err = match self {
@@ -1064,6 +1111,19 @@ impl CrateError {
10641111
}
10651112
err.note(&msg);
10661113
err
1114+
} else if !locator.crate_rejections.via_invalid.is_empty() {
1115+
let mut err = struct_span_err!(
1116+
sess,
1117+
span,
1118+
E0786,
1119+
"found invalid metadata files for crate `{}`{}",
1120+
crate_name,
1121+
add,
1122+
);
1123+
for CrateMismatch { path: _, got } in locator.crate_rejections.via_invalid {
1124+
err.note(&got);
1125+
}
1126+
err
10671127
} else {
10681128
let mut err = struct_span_err!(
10691129
sess,

src/test/run-make-fulldeps/invalid-library/Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
all:
44
touch $(TMPDIR)/lib.rmeta
55
$(AR) crus $(TMPDIR)/libfoo-ffffffff-1.0.rlib $(TMPDIR)/lib.rmeta
6-
$(RUSTC) foo.rs 2>&1 | $(CGREP) "can't find crate for"
6+
$(RUSTC) foo.rs 2>&1 | $(CGREP) "found invalid metadata"

src/test/run-make/invalid-so/Makefile

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
include ../../run-make-fulldeps/tools.mk
2+
3+
DYLIB_NAME := $(shell echo | $(RUSTC) --crate-name foo --crate-type dylib --print file-names -)
4+
5+
all:
6+
echo >> $(TMPDIR)/$(DYLIB_NAME)
7+
$(RUSTC) --crate-type lib --extern foo=$(TMPDIR)/$(DYLIB_NAME) bar.rs 2>&1 | $(CGREP) 'invalid metadata files for crate `foo`'

src/test/run-make/invalid-so/bar.rs

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
extern crate foo;

src/test/ui/crate-loading/auxiliary/libfoo.rlib

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// compile-flags: --crate-type lib --extern foo={{src-base}}/crate-loading/auxiliary/libfoo.rlib
2+
// normalize-stderr-test: "failed to mmap file '.*auxiliary/libfoo.rlib':.*" -> "failed to mmap file 'auxiliary/libfoo.rlib'"
3+
// don't emit warn logging, it's basically the same as the errors and it's annoying to normalize
4+
// rustc-env:RUSTC_LOG=error
5+
// edition:2018
6+
#![no_std]
7+
use ::foo; //~ ERROR invalid metadata files for crate `foo`
8+
//~| NOTE failed to mmap file
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error[E0786]: found invalid metadata files for crate `foo`
2+
--> $DIR/invalid-rlib.rs:7:7
3+
|
4+
LL | use ::foo;
5+
| ^^^
6+
|
7+
= note: failed to mmap file 'auxiliary/libfoo.rlib'
8+
9+
error: aborting due to previous error
10+
11+
For more information about this error, try `rustc --explain E0786`.

0 commit comments

Comments
 (0)