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