Skip to content

Commit 008be2d

Browse files
committed
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.
1 parent 77e24f9 commit 008be2d

File tree

9 files changed

+52
-30
lines changed

9 files changed

+52
-30
lines changed

compiler/rustc_metadata/src/locator.rs

Lines changed: 5 additions & 13 deletions
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 {
@@ -539,7 +539,7 @@ impl<'a> CrateLocator<'a> {
539539
&mut self,
540540
m: FxHashMap<PathBuf, PathKind>,
541541
flavor: CrateFlavor,
542-
slot: &mut Option<(Svh, MetadataBlob)>,
542+
slot: &mut Option<(PathBuf, Svh, MetadataBlob)>,
543543
) -> Result<Option<(PathBuf, PathKind)>, CrateError> {
544544
// If we are producing an rlib, and we've already loaded metadata, then
545545
// we should not attempt to discover further crate sources (unless we're
@@ -550,16 +550,9 @@ impl<'a> CrateLocator<'a> {
550550
//
551551
// See also #68149 which provides more detail on why emitting the
552552
// 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.
558553
if slot.is_some() {
559554
if m.is_empty() || !self.needs_crate_flavor(flavor) {
560555
return Ok(None);
561-
} else if m.len() == 1 {
562-
return Ok(Some(m.into_iter().next().unwrap()));
563556
}
564557
}
565558

@@ -602,16 +595,15 @@ impl<'a> CrateLocator<'a> {
602595
}
603596
};
604597
// If we see multiple hashes, emit an error about duplicate candidates.
605-
if slot.as_ref().is_some_and(|s| s.0 != hash) {
598+
if slot.as_ref().is_some_and(|s| s.1 != hash) {
606599
if let Some(candidates) = err_data {
607600
return Err(CrateError::MultipleCandidates(
608601
self.crate_name,
609602
flavor,
610603
candidates,
611604
));
612605
}
613-
err_data = Some(vec![ret.as_ref().unwrap().0.clone()]);
614-
*slot = None;
606+
err_data = Some(vec![slot.take().unwrap().0]);
615607
}
616608
if let Some(candidates) = &mut err_data {
617609
candidates.push(lib);
@@ -644,7 +636,7 @@ impl<'a> CrateLocator<'a> {
644636
continue;
645637
}
646638
}
647-
*slot = Some((hash, metadata));
639+
*slot = Some((lib.clone(), hash, metadata));
648640
ret = Some((lib, kind));
649641
}
650642

tests/run-make/extern-flag-pathless/Makefile

Lines changed: 21 additions & 6 deletions
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)

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

Lines changed: 0 additions & 3 deletions
This file was deleted.

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

Lines changed: 0 additions & 3 deletions
This file was deleted.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub fn f() {}

tests/run-make/mixing-libs/Makefile

Lines changed: 3 additions & 5 deletions
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
Lines changed: 16 additions & 0 deletions
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)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub fn bar() {}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
extern crate bar;
2+
3+
fn main() {
4+
bar::bar();
5+
}

0 commit comments

Comments
 (0)