1 use std::convert::TryFrom;
2 
3 use rustc_middle::mir::interpret::{InterpResult, Pointer, PointerArithmetic};
4 use rustc_middle::ty::{
5     self, Ty, COMMON_VTABLE_ENTRIES, COMMON_VTABLE_ENTRIES_ALIGN,
6     COMMON_VTABLE_ENTRIES_DROPINPLACE, COMMON_VTABLE_ENTRIES_SIZE,
7 };
8 use rustc_target::abi::{Align, Size};
9 
10 use super::util::ensure_monomorphic_enough;
11 use super::{FnVal, InterpCx, Machine};
12 
13 impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
14     /// Creates a dynamic vtable for the given type and vtable origin. This is used only for
15     /// objects.
16     ///
17     /// The `trait_ref` encodes the erased self type. Hence, if we are
18     /// making an object `Foo<Trait>` from a value of type `Foo<T>`, then
19     /// `trait_ref` would map `T: Trait`.
get_vtable( &mut self, ty: Ty<'tcx>, poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>, ) -> InterpResult<'tcx, Pointer<Option<M::PointerTag>>>20     pub fn get_vtable(
21         &mut self,
22         ty: Ty<'tcx>,
23         poly_trait_ref: Option<ty::PolyExistentialTraitRef<'tcx>>,
24     ) -> InterpResult<'tcx, Pointer<Option<M::PointerTag>>> {
25         trace!("get_vtable(trait_ref={:?})", poly_trait_ref);
26 
27         let (ty, poly_trait_ref) = self.tcx.erase_regions((ty, poly_trait_ref));
28 
29         // All vtables must be monomorphic, bail out otherwise.
30         ensure_monomorphic_enough(*self.tcx, ty)?;
31         ensure_monomorphic_enough(*self.tcx, poly_trait_ref)?;
32 
33         let vtable_allocation = self.tcx.vtable_allocation((ty, poly_trait_ref));
34 
35         let vtable_ptr = self.memory.global_base_pointer(Pointer::from(vtable_allocation))?;
36 
37         Ok(vtable_ptr.into())
38     }
39 
40     /// Resolves the function at the specified slot in the provided
41     /// vtable. Currently an index of '3' (`COMMON_VTABLE_ENTRIES.len()`)
42     /// corresponds to the first method declared in the trait of the provided vtable.
get_vtable_slot( &self, vtable: Pointer<Option<M::PointerTag>>, idx: u64, ) -> InterpResult<'tcx, FnVal<'tcx, M::ExtraFnVal>>43     pub fn get_vtable_slot(
44         &self,
45         vtable: Pointer<Option<M::PointerTag>>,
46         idx: u64,
47     ) -> InterpResult<'tcx, FnVal<'tcx, M::ExtraFnVal>> {
48         let ptr_size = self.pointer_size();
49         let vtable_slot = vtable.offset(ptr_size * idx, self)?;
50         let vtable_slot = self
51             .memory
52             .get(vtable_slot, ptr_size, self.tcx.data_layout.pointer_align.abi)?
53             .expect("cannot be a ZST");
54         let fn_ptr = self.scalar_to_ptr(vtable_slot.read_ptr_sized(Size::ZERO)?.check_init()?);
55         self.memory.get_fn(fn_ptr)
56     }
57 
58     /// Returns the drop fn instance as well as the actual dynamic type.
read_drop_type_from_vtable( &self, vtable: Pointer<Option<M::PointerTag>>, ) -> InterpResult<'tcx, (ty::Instance<'tcx>, Ty<'tcx>)>59     pub fn read_drop_type_from_vtable(
60         &self,
61         vtable: Pointer<Option<M::PointerTag>>,
62     ) -> InterpResult<'tcx, (ty::Instance<'tcx>, Ty<'tcx>)> {
63         let pointer_size = self.pointer_size();
64         // We don't care about the pointee type; we just want a pointer.
65         let vtable = self
66             .memory
67             .get(
68                 vtable,
69                 pointer_size * u64::try_from(COMMON_VTABLE_ENTRIES.len()).unwrap(),
70                 self.tcx.data_layout.pointer_align.abi,
71             )?
72             .expect("cannot be a ZST");
73         let drop_fn = vtable
74             .read_ptr_sized(
75                 pointer_size * u64::try_from(COMMON_VTABLE_ENTRIES_DROPINPLACE).unwrap(),
76             )?
77             .check_init()?;
78         // We *need* an instance here, no other kind of function value, to be able
79         // to determine the type.
80         let drop_instance = self.memory.get_fn(self.scalar_to_ptr(drop_fn))?.as_instance()?;
81         trace!("Found drop fn: {:?}", drop_instance);
82         let fn_sig = drop_instance.ty(*self.tcx, self.param_env).fn_sig(*self.tcx);
83         let fn_sig = self.tcx.normalize_erasing_late_bound_regions(self.param_env, fn_sig);
84         // The drop function takes `*mut T` where `T` is the type being dropped, so get that.
85         let args = fn_sig.inputs();
86         if args.len() != 1 {
87             throw_ub!(InvalidVtableDropFn(fn_sig));
88         }
89         let ty =
90             args[0].builtin_deref(true).ok_or_else(|| err_ub!(InvalidVtableDropFn(fn_sig)))?.ty;
91         Ok((drop_instance, ty))
92     }
93 
read_size_and_align_from_vtable( &self, vtable: Pointer<Option<M::PointerTag>>, ) -> InterpResult<'tcx, (Size, Align)>94     pub fn read_size_and_align_from_vtable(
95         &self,
96         vtable: Pointer<Option<M::PointerTag>>,
97     ) -> InterpResult<'tcx, (Size, Align)> {
98         let pointer_size = self.pointer_size();
99         // We check for `size = 3 * ptr_size`, which covers the drop fn (unused here),
100         // the size, and the align (which we read below).
101         let vtable = self
102             .memory
103             .get(
104                 vtable,
105                 pointer_size * u64::try_from(COMMON_VTABLE_ENTRIES.len()).unwrap(),
106                 self.tcx.data_layout.pointer_align.abi,
107             )?
108             .expect("cannot be a ZST");
109         let size = vtable
110             .read_ptr_sized(pointer_size * u64::try_from(COMMON_VTABLE_ENTRIES_SIZE).unwrap())?
111             .check_init()?;
112         let size = size.to_machine_usize(self)?;
113         let align = vtable
114             .read_ptr_sized(pointer_size * u64::try_from(COMMON_VTABLE_ENTRIES_ALIGN).unwrap())?
115             .check_init()?;
116         let align = align.to_machine_usize(self)?;
117         let align = Align::from_bytes(align).map_err(|e| err_ub!(InvalidVtableAlignment(e)))?;
118 
119         if size >= self.tcx.data_layout.obj_size_bound() {
120             throw_ub!(InvalidVtableSize);
121         }
122         Ok((Size::from_bytes(size), align))
123     }
124 
read_new_vtable_after_trait_upcasting_from_vtable( &self, vtable: Pointer<Option<M::PointerTag>>, idx: u64, ) -> InterpResult<'tcx, Pointer<Option<M::PointerTag>>>125     pub fn read_new_vtable_after_trait_upcasting_from_vtable(
126         &self,
127         vtable: Pointer<Option<M::PointerTag>>,
128         idx: u64,
129     ) -> InterpResult<'tcx, Pointer<Option<M::PointerTag>>> {
130         let pointer_size = self.pointer_size();
131 
132         let vtable_slot = vtable.offset(pointer_size * idx, self)?;
133         let new_vtable = self
134             .memory
135             .get(vtable_slot, pointer_size, self.tcx.data_layout.pointer_align.abi)?
136             .expect("cannot be a ZST");
137 
138         let new_vtable = self.scalar_to_ptr(new_vtable.read_ptr_sized(Size::ZERO)?.check_init()?);
139 
140         Ok(new_vtable)
141     }
142 }
143