Skip to content

Commit 18d731e

Browse files
committed
CFI: Generate super vtables explicitly
CFI shimming means they're not gauranteed to be pre-generated. Traditionally, the base vtable has all the elements of the supertrait vtable, and so visiting the base vtable implies you don't need to visit the supertrait vtable. However, with CFI the base vtable entries will have invocation type `dyn Child`, and the parent vtable will have invocation type `dyn Parent`, so they aren't actually the same instance, and both must be visited.
1 parent 5ec1ec4 commit 18d731e

File tree

2 files changed

+81
-13
lines changed

2 files changed

+81
-13
lines changed

compiler/rustc_monomorphize/src/collector.rs

+42-13
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ use rustc_span::source_map::{dummy_spanned, respan, Spanned};
191191
use rustc_span::symbol::{sym, Ident};
192192
use rustc_span::{Span, DUMMY_SP};
193193
use rustc_target::abi::Size;
194+
use std::iter;
194195
use std::path::PathBuf;
195196

196197
use crate::errors::{
@@ -1201,23 +1202,51 @@ fn create_mono_items_for_vtable_methods<'tcx>(
12011202
assert!(!poly_trait_ref.has_escaping_bound_vars());
12021203

12031204
// Walk all methods of the trait, including those of its supertraits
1204-
let entries = tcx.vtable_entries(poly_trait_ref);
1205-
let methods = entries
1206-
.iter()
1207-
.filter_map(|entry| match entry {
1205+
for entry in tcx.vtable_entries(poly_trait_ref) {
1206+
match entry {
12081207
VtblEntry::MetadataDropInPlace
12091208
| VtblEntry::MetadataSize
12101209
| VtblEntry::MetadataAlign
1211-
| VtblEntry::Vacant => None,
1212-
VtblEntry::TraitVPtr(_) => {
1213-
// all super trait items already covered, so skip them.
1214-
None
1210+
| VtblEntry::Vacant => (),
1211+
VtblEntry::TraitVPtr(super_trait) => {
1212+
// If CFI shims are enabled, the super_trait will use a different invocation
1213+
// type than the instances selected for this trait. This means we must walk
1214+
// the super_trait pointer visit its instances as well.
1215+
if tcx.sess.cfi_shims() {
1216+
let pep: ty::PolyExistentialPredicate<'tcx> =
1217+
super_trait.map_bound(|t| {
1218+
ty::ExistentialPredicate::Trait(
1219+
ty::ExistentialTraitRef::erase_self_ty(tcx, t),
1220+
)
1221+
});
1222+
let existential_predicates = tcx
1223+
.mk_poly_existential_predicates_from_iter(
1224+
iter::once(pep).chain(trait_ty.iter().skip(1)),
1225+
);
1226+
let super_trait_ty = Ty::new_dynamic(
1227+
tcx,
1228+
existential_predicates,
1229+
tcx.lifetimes.re_erased,
1230+
ty::Dyn,
1231+
);
1232+
1233+
create_mono_items_for_vtable_methods(
1234+
tcx,
1235+
super_trait_ty,
1236+
impl_ty,
1237+
source,
1238+
output,
1239+
);
1240+
}
1241+
}
1242+
VtblEntry::Method(instance) => {
1243+
let instance = instance.cfi_shim(tcx, invoke_trait);
1244+
if should_codegen_locally(tcx, &instance) {
1245+
output.push(create_fn_mono_item(tcx, instance, source));
1246+
}
12151247
}
1216-
VtblEntry::Method(instance) => Some(instance.cfi_shim(tcx, invoke_trait))
1217-
.filter(|instance| should_codegen_locally(tcx, instance)),
1218-
})
1219-
.map(|item| create_fn_mono_item(tcx, item, source));
1220-
output.extend(methods);
1248+
}
1249+
}
12211250
};
12221251

12231252
// Also add the destructor.
+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Check that super-traits with vptrs have their shims generated
2+
3+
//@ needs-sanitizer-cfi
4+
//@ compile-flags: --crate-type=bin -Cprefer-dynamic=off -Clto -Zsanitizer=cfi
5+
//@ compile-flags: -C codegen-units=1 -C opt-level=0
6+
//@ build-pass
7+
8+
trait Parent1 {
9+
fn p1(&self);
10+
}
11+
12+
trait Parent2 {
13+
fn p2(&self);
14+
}
15+
16+
// We need two parent traits to force the vtable upcasting code to choose to add a pointer to
17+
// another vtable to the child. This vtable is generated even if trait upcasting is not in use.
18+
trait Child : Parent1 + Parent2 {
19+
fn c(&self);
20+
}
21+
22+
struct Foo;
23+
24+
impl Parent1 for Foo {
25+
fn p1(&self) {}
26+
}
27+
28+
impl Parent2 for Foo {
29+
fn p2(&self) {}
30+
}
31+
32+
impl Child for Foo {
33+
fn c(&self) {}
34+
}
35+
36+
fn main() {
37+
let x = &Foo as &dyn Child;
38+
x.c();
39+
}

0 commit comments

Comments
 (0)