Skip to content

Commit 5bbda3f

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 1e0cf00 commit 5bbda3f

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::{
@@ -1214,23 +1215,51 @@ fn create_mono_items_for_vtable_methods<'tcx>(
12141215
assert!(!poly_trait_ref.has_escaping_bound_vars());
12151216

12161217
// Walk all methods of the trait, including those of its supertraits
1217-
let entries = tcx.vtable_entries(poly_trait_ref);
1218-
let methods = entries
1219-
.iter()
1220-
.filter_map(|entry| match entry {
1218+
for entry in tcx.vtable_entries(poly_trait_ref) {
1219+
match entry {
12211220
VtblEntry::MetadataDropInPlace
12221221
| VtblEntry::MetadataSize
12231222
| VtblEntry::MetadataAlign
1224-
| VtblEntry::Vacant => None,
1225-
VtblEntry::TraitVPtr(_) => {
1226-
// all super trait items already covered, so skip them.
1227-
None
1223+
| VtblEntry::Vacant => (),
1224+
VtblEntry::TraitVPtr(super_trait) => {
1225+
// If CFI shims are enabled, the super_trait will use a different invocation
1226+
// type than the instances selected for this trait. This means we must walk
1227+
// the super_trait pointer visit its instances as well.
1228+
if tcx.sess.cfi_shims() {
1229+
let pep: ty::PolyExistentialPredicate<'tcx> =
1230+
super_trait.map_bound(|t| {
1231+
ty::ExistentialPredicate::Trait(
1232+
ty::ExistentialTraitRef::erase_self_ty(tcx, t),
1233+
)
1234+
});
1235+
let existential_predicates = tcx
1236+
.mk_poly_existential_predicates_from_iter(
1237+
iter::once(pep).chain(trait_ty.iter().skip(1)),
1238+
);
1239+
let super_trait_ty = Ty::new_dynamic(
1240+
tcx,
1241+
existential_predicates,
1242+
tcx.lifetimes.re_erased,
1243+
ty::Dyn,
1244+
);
1245+
1246+
create_mono_items_for_vtable_methods(
1247+
tcx,
1248+
super_trait_ty,
1249+
impl_ty,
1250+
source,
1251+
output,
1252+
);
1253+
}
1254+
}
1255+
VtblEntry::Method(instance) => {
1256+
let instance = instance.cfi_shim(tcx, invoke_trait);
1257+
if should_codegen_locally(tcx, &instance) {
1258+
output.push(create_fn_mono_item(tcx, instance, source));
1259+
}
12281260
}
1229-
VtblEntry::Method(instance) => Some(instance.cfi_shim(tcx, invoke_trait))
1230-
.filter(|instance| should_codegen_locally(tcx, instance)),
1231-
})
1232-
.map(|item| create_fn_mono_item(tcx, item, source));
1233-
output.extend(methods);
1261+
}
1262+
}
12341263
};
12351264

12361265
// 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)