Skip to content

Commit b14fd23

Browse files
committed
Auto merge of #113695 - bjorn3:fix_rlib_cdylib_metadata_handling, r=pnkfelix,petrochenkov
Verify that all crate sources are in sync This ensures that rustc will not attempt to link against a cdylib as if it is a rust dylib when an rlib for the same crate is available. Previously rustc didn't actually check if any further formats of a crate which has been loaded are of the same version and if they are actually valid. This caused a cdylib to be interpreted as rust dylib as soon as the corresponding rlib was loaded. As cdylibs don't export any rust symbols, linking would fail if rustc decides to link against the cdylib rather than the rlib. Two crates depended on the previous behavior by separately compiling a test crate as both rlib and dylib. These have been changed to capture their original spirit to the best of my ability while still working when rustc verifies that all crates are in sync. It is unlikely that build systems depend on the current behavior and in any case we are taking a lot of measures to ensure that any change to either the source or the compilation options (including crate type) results in rustc rejecting it as incompatible. We merely didn't do this check here for now obsolete perf reasons. Fixes #10786 Fixes #82151 Fixes #82972 Closes bevy-cheatbook/bevy-cheatbook#114
2 parents c67cb3e + 8c9a8b6 commit b14fd23

File tree

17 files changed

+100
-79
lines changed

17 files changed

+100
-79
lines changed

Diff for: Cargo.lock

-1
Original file line numberDiff line numberDiff line change
@@ -3367,7 +3367,6 @@ dependencies = [
33673367
"rustc_type_ir",
33683368
"serde_json",
33693369
"smallvec",
3370-
"snap",
33713370
"tempfile",
33723371
"thorin-dwp",
33733372
"tracing",

Diff for: compiler/rustc_codegen_ssa/Cargo.toml

-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ tempfile = "3.2"
1717
thorin-dwp = "0.6"
1818
pathdiff = "0.2.0"
1919
serde_json = "1.0.59"
20-
snap = "1"
2120
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
2221
regex = "1.4"
2322

Diff for: compiler/rustc_codegen_ssa/src/back/metadata.rs

+7-13
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ use object::{
1010
ObjectSymbol, SectionFlags, SectionKind, SymbolFlags, SymbolKind, SymbolScope,
1111
};
1212

13-
use snap::write::FrameEncoder;
14-
1513
use rustc_data_structures::memmap::Mmap;
1614
use rustc_data_structures::owned_slice::{try_slice_owned, OwnedSlice};
1715
use rustc_metadata::fs::METADATA_FILENAME;
@@ -481,19 +479,15 @@ pub fn create_compressed_metadata_file(
481479
metadata: &EncodedMetadata,
482480
symbol_name: &str,
483481
) -> Vec<u8> {
484-
let mut compressed = rustc_metadata::METADATA_HEADER.to_vec();
485-
// Our length will be backfilled once we're done writing
486-
compressed.write_all(&[0; 4]).unwrap();
487-
FrameEncoder::new(&mut compressed).write_all(metadata.raw_data()).unwrap();
488-
let meta_len = rustc_metadata::METADATA_HEADER.len();
489-
let data_len = (compressed.len() - meta_len - 4) as u32;
490-
compressed[meta_len..meta_len + 4].copy_from_slice(&data_len.to_be_bytes());
482+
let mut packed_metadata = rustc_metadata::METADATA_HEADER.to_vec();
483+
packed_metadata.write_all(&(metadata.raw_data().len() as u32).to_be_bytes()).unwrap();
484+
packed_metadata.extend(metadata.raw_data());
491485

492486
let Some(mut file) = create_object_file(sess) else {
493-
return compressed.to_vec();
487+
return packed_metadata.to_vec();
494488
};
495489
if file.format() == BinaryFormat::Xcoff {
496-
return create_compressed_metadata_file_for_xcoff(file, &compressed, symbol_name);
490+
return create_compressed_metadata_file_for_xcoff(file, &packed_metadata, symbol_name);
497491
}
498492
let section = file.add_section(
499493
file.segment_name(StandardSegment::Data).to_vec(),
@@ -507,14 +501,14 @@ pub fn create_compressed_metadata_file(
507501
}
508502
_ => {}
509503
};
510-
let offset = file.append_section_data(section, &compressed, 1);
504+
let offset = file.append_section_data(section, &packed_metadata, 1);
511505

512506
// For MachO and probably PE this is necessary to prevent the linker from throwing away the
513507
// .rustc section. For ELF this isn't necessary, but it also doesn't harm.
514508
file.add_symbol(Symbol {
515509
name: symbol_name.as_bytes().to_vec(),
516510
value: offset,
517-
size: compressed.len() as u64,
511+
size: packed_metadata.len() as u64,
518512
kind: SymbolKind::Data,
519513
scope: SymbolScope::Dynamic,
520514
weak: false,

Diff for: compiler/rustc_metadata/src/locator.rs

+25-24
Original file line numberDiff line numberDiff line change
@@ -511,7 +511,7 @@ impl<'a> CrateLocator<'a> {
511511
rlib: self.extract_one(rlibs, CrateFlavor::Rlib, &mut slot)?,
512512
dylib: self.extract_one(dylibs, CrateFlavor::Dylib, &mut slot)?,
513513
};
514-
Ok(slot.map(|(svh, metadata)| (svh, Library { source, metadata })))
514+
Ok(slot.map(|(svh, metadata, _)| (svh, Library { source, metadata })))
515515
}
516516

517517
fn needs_crate_flavor(&self, flavor: CrateFlavor) -> bool {
@@ -535,11 +535,13 @@ impl<'a> CrateLocator<'a> {
535535
// read the metadata from it if `*slot` is `None`. If the metadata couldn't
536536
// be read, it is assumed that the file isn't a valid rust library (no
537537
// errors are emitted).
538+
//
539+
// The `PathBuf` in `slot` will only be used for diagnostic purposes.
538540
fn extract_one(
539541
&mut self,
540542
m: FxHashMap<PathBuf, PathKind>,
541543
flavor: CrateFlavor,
542-
slot: &mut Option<(Svh, MetadataBlob)>,
544+
slot: &mut Option<(Svh, MetadataBlob, PathBuf)>,
543545
) -> Result<Option<(PathBuf, PathKind)>, CrateError> {
544546
// If we are producing an rlib, and we've already loaded metadata, then
545547
// we should not attempt to discover further crate sources (unless we're
@@ -550,16 +552,9 @@ impl<'a> CrateLocator<'a> {
550552
//
551553
// See also #68149 which provides more detail on why emitting the
552554
// dependency on the rlib is a bad thing.
553-
//
554-
// We currently do not verify that these other sources are even in sync,
555-
// and this is arguably a bug (see #10786), but because reading metadata
556-
// is quite slow (especially from dylibs) we currently do not read it
557-
// from the other crate sources.
558555
if slot.is_some() {
559556
if m.is_empty() || !self.needs_crate_flavor(flavor) {
560557
return Ok(None);
561-
} else if m.len() == 1 {
562-
return Ok(Some(m.into_iter().next().unwrap()));
563558
}
564559
}
565560

@@ -610,8 +605,7 @@ impl<'a> CrateLocator<'a> {
610605
candidates,
611606
));
612607
}
613-
err_data = Some(vec![ret.as_ref().unwrap().0.clone()]);
614-
*slot = None;
608+
err_data = Some(vec![slot.take().unwrap().2]);
615609
}
616610
if let Some(candidates) = &mut err_data {
617611
candidates.push(lib);
@@ -644,7 +638,7 @@ impl<'a> CrateLocator<'a> {
644638
continue;
645639
}
646640
}
647-
*slot = Some((hash, metadata));
641+
*slot = Some((hash, metadata, lib.clone()));
648642
ret = Some((lib, kind));
649643
}
650644

@@ -814,19 +808,26 @@ fn get_metadata_section<'p>(
814808
let compressed_len = u32::from_be_bytes(len_bytes) as usize;
815809

816810
// Header is okay -> inflate the actual metadata
817-
let compressed_bytes = &buf[data_start..(data_start + compressed_len)];
818-
debug!("inflating {} bytes of compressed metadata", compressed_bytes.len());
819-
// Assume the decompressed data will be at least the size of the compressed data, so we
820-
// don't have to grow the buffer as much.
821-
let mut inflated = Vec::with_capacity(compressed_bytes.len());
822-
FrameDecoder::new(compressed_bytes).read_to_end(&mut inflated).map_err(|_| {
823-
MetadataError::LoadFailure(format!(
824-
"failed to decompress metadata: {}",
825-
filename.display()
826-
))
827-
})?;
811+
let compressed_bytes = buf.slice(|buf| &buf[data_start..(data_start + compressed_len)]);
812+
if &compressed_bytes[..cmp::min(METADATA_HEADER.len(), compressed_bytes.len())]
813+
== METADATA_HEADER
814+
{
815+
// The metadata was not actually compressed.
816+
compressed_bytes
817+
} else {
818+
debug!("inflating {} bytes of compressed metadata", compressed_bytes.len());
819+
// Assume the decompressed data will be at least the size of the compressed data, so we
820+
// don't have to grow the buffer as much.
821+
let mut inflated = Vec::with_capacity(compressed_bytes.len());
822+
FrameDecoder::new(&*compressed_bytes).read_to_end(&mut inflated).map_err(|_| {
823+
MetadataError::LoadFailure(format!(
824+
"failed to decompress metadata: {}",
825+
filename.display()
826+
))
827+
})?;
828828

829-
slice_owned(inflated, Deref::deref)
829+
slice_owned(inflated, Deref::deref)
830+
}
830831
}
831832
CrateFlavor::Rmeta => {
832833
// mmap the file, because only a small fraction of it is read.

Diff for: tests/run-make/extern-flag-pathless/Makefile

+21-6
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,32 @@ include ../tools.mk
33

44
# Test mixing pathless --extern with paths.
55

6+
# Test for static linking by checking that the binary runs if the dylib
7+
# is removed and test for dynamic linking by checking that the binary
8+
# fails to run if the dylib is removed.
9+
610
all:
7-
$(RUSTC) bar-static.rs --crate-name=bar --crate-type=rlib
8-
$(RUSTC) bar-dynamic.rs --crate-name=bar --crate-type=dylib -C prefer-dynamic
11+
$(RUSTC) bar.rs --crate-type=rlib --crate-type=dylib -Cprefer-dynamic
12+
913
# rlib preferred over dylib
1014
$(RUSTC) foo.rs --extern bar
11-
$(call RUN,foo) | $(CGREP) 'static'
15+
mv $(call DYLIB,bar) $(TMPDIR)/bar.tmp
16+
$(call RUN,foo)
17+
mv $(TMPDIR)/bar.tmp $(call DYLIB,bar)
18+
1219
$(RUSTC) foo.rs --extern bar=$(TMPDIR)/libbar.rlib --extern bar
13-
$(call RUN,foo) | $(CGREP) 'static'
20+
mv $(call DYLIB,bar) $(TMPDIR)/bar.tmp
21+
$(call RUN,foo)
22+
mv $(TMPDIR)/bar.tmp $(call DYLIB,bar)
23+
1424
# explicit --extern overrides pathless
1525
$(RUSTC) foo.rs --extern bar=$(call DYLIB,bar) --extern bar
16-
$(call RUN,foo) | $(CGREP) 'dynamic'
26+
mv $(call DYLIB,bar) $(TMPDIR)/bar.tmp
27+
$(call FAIL,foo)
28+
mv $(TMPDIR)/bar.tmp $(call DYLIB,bar)
29+
1730
# prefer-dynamic does what it says
1831
$(RUSTC) foo.rs --extern bar -C prefer-dynamic
19-
$(call RUN,foo) | $(CGREP) 'dynamic'
32+
mv $(call DYLIB,bar) $(TMPDIR)/bar.tmp
33+
$(call FAIL,foo)
34+
mv $(TMPDIR)/bar.tmp $(call DYLIB,bar)

Diff for: tests/run-make/extern-flag-pathless/bar-dynamic.rs

-3
This file was deleted.

Diff for: tests/run-make/extern-flag-pathless/bar-static.rs

-3
This file was deleted.

Diff for: tests/run-make/extern-flag-pathless/bar.rs

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub fn f() {}

Diff for: tests/run-make/mixing-libs/Makefile

+3-5
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@
22
include ../tools.mk
33

44
all:
5-
$(RUSTC) rlib.rs
6-
$(RUSTC) dylib.rs
7-
$(RUSTC) rlib.rs --crate-type=dylib
8-
$(RUSTC) dylib.rs
9-
$(call REMOVE_DYLIBS,rlib)
5+
$(RUSTC) rlib.rs --crate-type=rlib --crate-type=dylib
6+
$(RUSTC) dylib.rs # no -Cprefer-dynamic so statically linking librlib.rlib
7+
$(call REMOVE_DYLIBS,rlib) # remove librlib.so to test that prog.rs doesn't get confused about the removed dylib version of librlib
108
$(RUSTC) prog.rs && exit 1 || exit 0

Diff for: tests/run-make/no-cdylib-as-rdylib/Makefile

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# ignore-cross-compile
2+
include ../tools.mk
3+
4+
# Test that rustc will not attempt to link against a cdylib as if
5+
# it is a rust dylib when an rlib for the same crate is available.
6+
# Previously rustc didn't actually check if any further formats of
7+
# a crate which has been loaded are of the same version and if
8+
# they are actually valid. This caused a cdylib to be interpreted
9+
# as rust dylib as soon as the corresponding rlib was loaded. As
10+
# cdylibs don't export any rust symbols, linking would fail if
11+
# rustc decides to link against the cdylib rather than the rlib.
12+
13+
all:
14+
$(RUSTC) bar.rs --crate-type=rlib --crate-type=cdylib
15+
$(RUSTC) foo.rs -C prefer-dynamic
16+
$(call RUN,foo)

Diff for: tests/run-make/no-cdylib-as-rdylib/bar.rs

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub fn bar() {}

Diff for: tests/run-make/no-cdylib-as-rdylib/foo.rs

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
extern crate bar;
2+
3+
fn main() {
4+
bar::bar();
5+
}

Diff for: tests/run-make/rmeta-preferred/Makefile

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# ignore-cross-compile
2+
include ../tools.mk
3+
4+
# Test that using rlibs and rmeta dep crates work together. Specifically, that
5+
# there can be both an rmeta and an rlib file and rustc will prefer the rmeta
6+
# file.
7+
#
8+
# This behavior is simply making sure this doesn't accidentally change; in this
9+
# case we want to make sure that the rlib isn't being used as that would cause
10+
# bugs in -Zbinary-dep-depinfo (see #68298).
11+
12+
all:
13+
$(RUSTC) rmeta_aux.rs --crate-type=rlib --emit link,metadata
14+
$(RUSTC) lib.rs --crate-type=rlib --emit dep-info -Zbinary-dep-depinfo
15+
$(CGREP) "librmeta_aux.rmeta" < $(TMPDIR)/lib.d
16+
$(CGREP) -v "librmeta_aux.rlib" < $(TMPDIR)/lib.d
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// run-pass
21
// Test that using rlibs and rmeta dep crates work together. Specifically, that
32
// there can be both an rmeta and an rlib file and rustc will prefer the rmeta
43
// file.
@@ -7,12 +6,9 @@
76
// case we want to make sure that the rlib isn't being used as that would cause
87
// bugs in -Zbinary-dep-depinfo (see #68298).
98

10-
// aux-build:rmeta-rmeta.rs
11-
// aux-build:rmeta-rlib-rpass.rs
12-
139
extern crate rmeta_aux;
1410
use rmeta_aux::Foo;
1511

16-
pub fn main() {
17-
let _ = Foo { field2: 42 };
12+
pub fn foo() {
13+
let _ = Foo { field: 42 };
1814
}

Diff for: tests/run-make/rmeta-preferred/rmeta_aux.rs

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
pub struct Foo {
2+
pub field: i32,
3+
}

Diff for: tests/ui/rmeta/auxiliary/rmeta-rlib-rpass.rs

-8
This file was deleted.

Diff for: tests/ui/rmeta/auxiliary/rmeta-rmeta.rs

-9
This file was deleted.

0 commit comments

Comments
 (0)