miri: pass pointer alignments directly instead of contextually.
This commit is contained in:
parent
08646c6c2c
commit
7dc79cc49b
@ -13,11 +13,10 @@ pub use self::error::{EvalError, EvalResult, EvalErrorKind};
|
||||
pub use self::value::{PrimVal, PrimValKind, Value, Pointer, bytes_to_f32, bytes_to_f64};
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use ty::layout::HasDataLayout;
|
||||
use std::fmt;
|
||||
use ty::layout;
|
||||
use mir;
|
||||
use ty;
|
||||
use ty::layout::{self, Align, HasDataLayout};
|
||||
use middle::region;
|
||||
use std::iter;
|
||||
|
||||
@ -166,7 +165,7 @@ pub struct Allocation {
|
||||
/// Denotes undefined memory. Reading from undefined memory is forbidden in miri
|
||||
pub undef_mask: UndefMask,
|
||||
/// The alignment of the allocation to detect unaligned reads.
|
||||
pub align: u64,
|
||||
pub align: Align,
|
||||
}
|
||||
|
||||
impl Allocation {
|
||||
@ -177,7 +176,7 @@ impl Allocation {
|
||||
bytes: slice.to_owned(),
|
||||
relocations: BTreeMap::new(),
|
||||
undef_mask,
|
||||
align: 1,
|
||||
align: Align::from_bytes(1, 1).unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ pub fn eval_body<'a, 'tcx>(
|
||||
assert!(!layout.is_unsized());
|
||||
let ptr = ecx.memory.allocate(
|
||||
layout.size.bytes(),
|
||||
layout.align.abi(),
|
||||
layout.align,
|
||||
None,
|
||||
)?;
|
||||
tcx.interpret_interner.borrow_mut().cache(cid, ptr.into());
|
||||
@ -95,7 +95,7 @@ pub fn eval_body_as_integer<'a, 'tcx>(
|
||||
let ptr_ty = eval_body(tcx, instance, param_env);
|
||||
let (ptr, ty) = ptr_ty?;
|
||||
let ecx = mk_eval_cx(tcx, instance, param_env)?;
|
||||
let prim = match ecx.try_read_value(ptr, ty)? {
|
||||
let prim = match ecx.try_read_value(ptr, ecx.layout_of(ty)?.align, ty)? {
|
||||
Some(Value::ByVal(prim)) => prim.to_bytes()?,
|
||||
_ => return err!(TypeNotPrimitive(ty)),
|
||||
};
|
||||
|
@ -211,8 +211,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
assert!(!layout.is_unsized(), "cannot alloc memory for unsized type");
|
||||
|
||||
let size = layout.size.bytes();
|
||||
let align = layout.align.abi();
|
||||
self.memory.allocate(size, align, Some(MemoryKind::Stack))
|
||||
self.memory.allocate(size, layout.align, Some(MemoryKind::Stack))
|
||||
}
|
||||
|
||||
pub fn memory(&self) -> &Memory<'a, 'tcx, M> {
|
||||
@ -612,12 +611,12 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
let elem_size = self.layout_of(elem_ty)?.size.bytes();
|
||||
let value = self.eval_operand(operand)?.value;
|
||||
|
||||
let dest = Pointer::from(self.force_allocation(dest)?.to_ptr()?);
|
||||
let (dest, dest_align) = self.force_allocation(dest)?.to_ptr_align();
|
||||
|
||||
// FIXME: speed up repeat filling
|
||||
for i in 0..length {
|
||||
let elem_dest = dest.offset(i * elem_size, &self)?;
|
||||
self.write_value_to_ptr(value, elem_dest, elem_ty)?;
|
||||
self.write_value_to_ptr(value, elem_dest, dest_align, elem_ty)?;
|
||||
}
|
||||
}
|
||||
|
||||
@ -955,15 +954,6 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
layout.align)
|
||||
}
|
||||
|
||||
fn copy(&mut self, src: Pointer, dest: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx> {
|
||||
let layout = self.layout_of(ty)?;
|
||||
assert!(!layout.is_unsized(), "cannot copy from an unsized type");
|
||||
let size = layout.size.bytes();
|
||||
let align = layout.align.abi();
|
||||
self.memory.copy(src, dest, size, align, false)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn force_allocation(&mut self, place: Place) -> EvalResult<'tcx, Place> {
|
||||
let new_place = match place {
|
||||
Place::Local { frame, local } => {
|
||||
@ -984,8 +974,9 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
let ptr = self.alloc_ptr(ty)?;
|
||||
self.stack[frame].locals[local.index() - 1] =
|
||||
Some(Value::ByRef(ptr.into(), layout.align)); // it stays live
|
||||
self.write_value_to_ptr(val, ptr.into(), ty)?;
|
||||
Place::from_ptr(ptr, layout.align)
|
||||
let place = Place::from_ptr(ptr, layout.align);
|
||||
self.write_value(ValTy { value: val, ty }, place)?;
|
||||
place
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1002,7 +993,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
) -> EvalResult<'tcx, Value> {
|
||||
match value {
|
||||
Value::ByRef(ptr, align) => {
|
||||
self.read_with_align(align, |ectx| ectx.read_value(ptr, ty))
|
||||
self.read_value(ptr, align, ty)
|
||||
}
|
||||
other => Ok(other),
|
||||
}
|
||||
@ -1059,8 +1050,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
match dest {
|
||||
Place::Ptr { ptr, align, extra } => {
|
||||
assert_eq!(extra, PlaceExtra::None);
|
||||
self.write_with_align_mut(align,
|
||||
|ectx| ectx.write_value_to_ptr(src_val, ptr, dest_ty))
|
||||
self.write_value_to_ptr(src_val, ptr, align, dest_ty)
|
||||
}
|
||||
|
||||
Place::Local { frame, local } => {
|
||||
@ -1091,10 +1081,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
//
|
||||
// Thus, it would be an error to replace the `ByRef` with a `ByVal`, unless we
|
||||
// knew for certain that there were no outstanding pointers to this allocation.
|
||||
self.write_with_align_mut(align, |ectx| {
|
||||
ectx.write_value_to_ptr(src_val, dest_ptr, dest_ty)
|
||||
})?;
|
||||
|
||||
self.write_value_to_ptr(src_val, dest_ptr, align, dest_ty)?;
|
||||
} else if let Value::ByRef(src_ptr, align) = src_val {
|
||||
// If the value is not `ByRef`, then we know there are no pointers to it
|
||||
// and we can simply overwrite the `Value` in the locals array directly.
|
||||
@ -1107,18 +1094,14 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
// It is a valid optimization to attempt reading a primitive value out of the
|
||||
// source and write that into the destination without making an allocation, so
|
||||
// we do so here.
|
||||
self.read_with_align_mut(align, |ectx| {
|
||||
if let Ok(Some(src_val)) = ectx.try_read_value(src_ptr, dest_ty) {
|
||||
write_dest(ectx, src_val)?;
|
||||
} else {
|
||||
let dest_ptr = ectx.alloc_ptr(dest_ty)?.into();
|
||||
ectx.copy(src_ptr, dest_ptr, dest_ty)?;
|
||||
let layout = ectx.layout_of(dest_ty)?;
|
||||
write_dest(ectx, Value::ByRef(dest_ptr, layout.align))?;
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
if let Ok(Some(src_val)) = self.try_read_value(src_ptr, align, dest_ty) {
|
||||
write_dest(self, src_val)?;
|
||||
} else {
|
||||
let dest_ptr = self.alloc_ptr(dest_ty)?.into();
|
||||
let layout = self.layout_of(dest_ty)?;
|
||||
self.memory.copy(src_ptr, align.min(layout.align), dest_ptr, layout.align, layout.size.bytes(), false)?;
|
||||
write_dest(self, Value::ByRef(dest_ptr, layout.align))?;
|
||||
}
|
||||
} else {
|
||||
// Finally, we have the simple case where neither source nor destination are
|
||||
// `ByRef`. We may simply copy the source value over the the destintion.
|
||||
@ -1131,26 +1114,26 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
&mut self,
|
||||
value: Value,
|
||||
dest: Pointer,
|
||||
dest_align: Align,
|
||||
dest_ty: Ty<'tcx>,
|
||||
) -> EvalResult<'tcx> {
|
||||
trace!("write_value_to_ptr: {:#?}", value);
|
||||
let layout = self.layout_of(dest_ty)?;
|
||||
match value {
|
||||
Value::ByRef(ptr, align) => {
|
||||
self.read_with_align_mut(align, |ectx| ectx.copy(ptr, dest, dest_ty))
|
||||
self.memory.copy(ptr, align.min(layout.align), dest, dest_align.min(layout.align), layout.size.bytes(), false)
|
||||
}
|
||||
Value::ByVal(primval) => {
|
||||
let layout = self.layout_of(dest_ty)?;
|
||||
match layout.abi {
|
||||
layout::Abi::Scalar(_) => {}
|
||||
_ if primval.is_undef() => {}
|
||||
_ => bug!("write_value_to_ptr: invalid ByVal layout: {:#?}", layout)
|
||||
}
|
||||
// TODO: Do we need signedness?
|
||||
self.memory.write_primval(dest.to_ptr()?, primval, layout.size.bytes(), false)
|
||||
self.memory.write_primval(dest.to_ptr()?, dest_align, primval, layout.size.bytes(), false)
|
||||
}
|
||||
Value::ByValPair(a_val, b_val) => {
|
||||
let ptr = dest.to_ptr()?;
|
||||
let mut layout = self.layout_of(dest_ty)?;
|
||||
trace!("write_value_to_ptr valpair: {:#?}", layout);
|
||||
let (a, b) = match layout.abi {
|
||||
layout::Abi::ScalarPair(ref a, ref b) => (&a.value, &b.value),
|
||||
@ -1161,9 +1144,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
let b_offset = a_size.abi_align(b.align(&self));
|
||||
let b_ptr = ptr.offset(b_offset.bytes(), &self)?.into();
|
||||
// TODO: What about signedess?
|
||||
self.memory.write_primval(a_ptr, a_val, a_size.bytes(), false)?;
|
||||
self.memory.write_primval(b_ptr, b_val, b_size.bytes(), false)?;
|
||||
Ok(())
|
||||
self.memory.write_primval(a_ptr, dest_align, a_val, a_size.bytes(), false)?;
|
||||
self.memory.write_primval(b_ptr, dest_align, b_val, b_size.bytes(), false)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1246,8 +1228,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_value(&self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> {
|
||||
if let Some(val) = self.try_read_value(ptr, ty)? {
|
||||
pub fn read_value(&self, ptr: Pointer, align: Align, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> {
|
||||
if let Some(val) = self.try_read_value(ptr, align, ty)? {
|
||||
Ok(val)
|
||||
} else {
|
||||
bug!("primitive read failed for type: {:?}", ty);
|
||||
@ -1257,10 +1239,11 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
pub(crate) fn read_ptr(
|
||||
&self,
|
||||
ptr: MemoryPointer,
|
||||
ptr_align: Align,
|
||||
pointee_ty: Ty<'tcx>,
|
||||
) -> EvalResult<'tcx, Value> {
|
||||
let ptr_size = self.memory.pointer_size();
|
||||
let p : Pointer = self.memory.read_ptr_sized_unsigned(ptr)?.into();
|
||||
let p: Pointer = self.memory.read_ptr_sized_unsigned(ptr, ptr_align)?.into();
|
||||
if self.type_is_sized(pointee_ty) {
|
||||
Ok(p.to_value())
|
||||
} else {
|
||||
@ -1268,23 +1251,23 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
let extra = ptr.offset(ptr_size, self)?;
|
||||
match self.tcx.struct_tail(pointee_ty).sty {
|
||||
ty::TyDynamic(..) => Ok(p.to_value_with_vtable(
|
||||
self.memory.read_ptr_sized_unsigned(extra)?.to_ptr()?,
|
||||
self.memory.read_ptr_sized_unsigned(extra, ptr_align)?.to_ptr()?,
|
||||
)),
|
||||
ty::TySlice(..) | ty::TyStr => Ok(
|
||||
p.to_value_with_len(self.memory.read_ptr_sized_unsigned(extra)?.to_bytes()? as u64),
|
||||
p.to_value_with_len(self.memory.read_ptr_sized_unsigned(extra, ptr_align)?.to_bytes()? as u64),
|
||||
),
|
||||
_ => bug!("unsized primval ptr read from {:?}", pointee_ty),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_read_value(&self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, Option<Value>> {
|
||||
pub fn try_read_value(&self, ptr: Pointer, ptr_align: Align, ty: Ty<'tcx>) -> EvalResult<'tcx, Option<Value>> {
|
||||
use syntax::ast::FloatTy;
|
||||
|
||||
let ptr = ptr.to_ptr()?;
|
||||
let val = match ty.sty {
|
||||
ty::TyBool => {
|
||||
let val = self.memory.read_primval(ptr, 1, false)?;
|
||||
let val = self.memory.read_primval(ptr, ptr_align, 1, false)?;
|
||||
let val = match val {
|
||||
PrimVal::Bytes(0) => false,
|
||||
PrimVal::Bytes(1) => true,
|
||||
@ -1294,7 +1277,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
PrimVal::from_bool(val)
|
||||
}
|
||||
ty::TyChar => {
|
||||
let c = self.memory.read_primval(ptr, 4, false)?.to_bytes()? as u32;
|
||||
let c = self.memory.read_primval(ptr, ptr_align, 4, false)?.to_bytes()? as u32;
|
||||
match ::std::char::from_u32(c) {
|
||||
Some(ch) => PrimVal::from_char(ch),
|
||||
None => return err!(InvalidChar(c as u128)),
|
||||
@ -1311,7 +1294,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
I128 => 16,
|
||||
Is => self.memory.pointer_size(),
|
||||
};
|
||||
self.memory.read_primval(ptr, size, true)?
|
||||
self.memory.read_primval(ptr, ptr_align, size, true)?
|
||||
}
|
||||
|
||||
ty::TyUint(uint_ty) => {
|
||||
@ -1324,19 +1307,23 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
U128 => 16,
|
||||
Us => self.memory.pointer_size(),
|
||||
};
|
||||
self.memory.read_primval(ptr, size, false)?
|
||||
self.memory.read_primval(ptr, ptr_align, size, false)?
|
||||
}
|
||||
|
||||
ty::TyFloat(FloatTy::F32) => PrimVal::Bytes(self.memory.read_primval(ptr, 4, false)?.to_bytes()?),
|
||||
ty::TyFloat(FloatTy::F64) => PrimVal::Bytes(self.memory.read_primval(ptr, 8, false)?.to_bytes()?),
|
||||
ty::TyFloat(FloatTy::F32) => {
|
||||
PrimVal::Bytes(self.memory.read_primval(ptr, ptr_align, 4, false)?.to_bytes()?)
|
||||
}
|
||||
ty::TyFloat(FloatTy::F64) => {
|
||||
PrimVal::Bytes(self.memory.read_primval(ptr, ptr_align, 8, false)?.to_bytes()?)
|
||||
}
|
||||
|
||||
ty::TyFnPtr(_) => self.memory.read_ptr_sized_unsigned(ptr)?,
|
||||
ty::TyFnPtr(_) => self.memory.read_ptr_sized_unsigned(ptr, ptr_align)?,
|
||||
ty::TyRef(_, ref tam) |
|
||||
ty::TyRawPtr(ref tam) => return self.read_ptr(ptr, tam.ty).map(Some),
|
||||
ty::TyRawPtr(ref tam) => return self.read_ptr(ptr, ptr_align, tam.ty).map(Some),
|
||||
|
||||
ty::TyAdt(def, _) => {
|
||||
if def.is_box() {
|
||||
return self.read_ptr(ptr, ty.boxed_ty()).map(Some);
|
||||
return self.read_ptr(ptr, ptr_align, ty.boxed_ty()).map(Some);
|
||||
}
|
||||
|
||||
if let layout::Abi::Scalar(ref scalar) = self.layout_of(ty)?.abi {
|
||||
@ -1345,7 +1332,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
signed = s;
|
||||
}
|
||||
let size = scalar.value.size(self).bytes();
|
||||
self.memory.read_primval(ptr, size, signed)?
|
||||
self.memory.read_primval(ptr, ptr_align, size, signed)?
|
||||
} else {
|
||||
return Ok(None);
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian, BigEndian};
|
||||
use std::collections::{btree_map, BTreeMap, HashMap, HashSet, VecDeque};
|
||||
use std::{ptr, mem, io};
|
||||
use std::cell::Cell;
|
||||
|
||||
use rustc::ty::{Instance, TyCtxt};
|
||||
use rustc::ty::layout::{self, Align, TargetDataLayout};
|
||||
@ -51,11 +50,6 @@ pub struct Memory<'a, 'tcx: 'a, M: Machine<'tcx>> {
|
||||
/// Maximum number of virtual bytes that may be allocated.
|
||||
memory_size: u64,
|
||||
|
||||
/// To avoid having to pass flags to every single memory access, we have some global state saying how
|
||||
/// alignment checking is currently enforced for read and/or write accesses.
|
||||
read_align_override: Cell<Option<Align>>,
|
||||
write_align_override: Cell<Option<Align>>,
|
||||
|
||||
/// The current stack frame. Used to check accesses against locks.
|
||||
pub cur_frame: usize,
|
||||
|
||||
@ -72,8 +66,6 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
||||
tcx,
|
||||
memory_size: max_memory,
|
||||
memory_usage: 0,
|
||||
read_align_override: Cell::new(None),
|
||||
write_align_override: Cell::new(None),
|
||||
cur_frame: usize::max_value(),
|
||||
}
|
||||
}
|
||||
@ -98,12 +90,9 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
||||
pub fn allocate(
|
||||
&mut self,
|
||||
size: u64,
|
||||
align: u64,
|
||||
align: Align,
|
||||
kind: Option<MemoryKind<M::MemoryKinds>>,
|
||||
) -> EvalResult<'tcx, MemoryPointer> {
|
||||
assert_ne!(align, 0);
|
||||
assert!(align.is_power_of_two());
|
||||
|
||||
if self.memory_size - self.memory_usage < size {
|
||||
return err!(OutOfMemory {
|
||||
allocation_size: size,
|
||||
@ -139,13 +128,11 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
||||
&mut self,
|
||||
ptr: MemoryPointer,
|
||||
old_size: u64,
|
||||
old_align: u64,
|
||||
old_align: Align,
|
||||
new_size: u64,
|
||||
new_align: u64,
|
||||
new_align: Align,
|
||||
kind: MemoryKind<M::MemoryKinds>,
|
||||
) -> EvalResult<'tcx, MemoryPointer> {
|
||||
use std::cmp::min;
|
||||
|
||||
if ptr.offset != 0 {
|
||||
return err!(ReallocateNonBasePtr);
|
||||
}
|
||||
@ -163,9 +150,10 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
||||
let new_ptr = self.allocate(new_size, new_align, Some(kind))?;
|
||||
self.copy(
|
||||
ptr.into(),
|
||||
old_align,
|
||||
new_ptr.into(),
|
||||
min(old_size, new_size),
|
||||
min(old_align, new_align),
|
||||
new_align,
|
||||
old_size.min(new_size),
|
||||
/*nonoverlapping*/
|
||||
true,
|
||||
)?;
|
||||
@ -190,7 +178,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
||||
pub fn deallocate(
|
||||
&mut self,
|
||||
ptr: MemoryPointer,
|
||||
size_and_align: Option<(u64, u64)>,
|
||||
size_and_align: Option<(u64, Align)>,
|
||||
kind: MemoryKind<M::MemoryKinds>,
|
||||
) -> EvalResult<'tcx> {
|
||||
if ptr.offset != 0 {
|
||||
@ -236,7 +224,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
||||
}
|
||||
if let Some((size, align)) = size_and_align {
|
||||
if size != alloc.bytes.len() as u64 || align != alloc.align {
|
||||
return err!(IncorrectAllocationInformation(size, alloc.bytes.len(), align, alloc.align));
|
||||
return err!(IncorrectAllocationInformation(size, alloc.bytes.len(), align.abi(), alloc.align.abi()));
|
||||
}
|
||||
}
|
||||
|
||||
@ -255,7 +243,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
||||
}
|
||||
|
||||
/// Check that the pointer is aligned AND non-NULL.
|
||||
pub fn check_align(&self, ptr: Pointer, align: u64, access: Option<AccessKind>) -> EvalResult<'tcx> {
|
||||
pub fn check_align(&self, ptr: Pointer, required_align: Align) -> EvalResult<'tcx> {
|
||||
// Check non-NULL/Undef, extract offset
|
||||
let (offset, alloc_align) = match ptr.into_inner_primval() {
|
||||
PrimVal::Ptr(ptr) => {
|
||||
@ -267,30 +255,24 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
||||
if v == 0 {
|
||||
return err!(InvalidNullPointerUsage);
|
||||
}
|
||||
(v, align) // the base address if the "integer allocation" is 0 and hence always aligned
|
||||
// the base address if the "integer allocation" is 0 and hence always aligned
|
||||
(v, required_align)
|
||||
}
|
||||
PrimVal::Undef => return err!(ReadUndefBytes),
|
||||
};
|
||||
// See if alignment checking is disabled
|
||||
let align_override = match access {
|
||||
Some(AccessKind::Read) => self.read_align_override.get(),
|
||||
Some(AccessKind::Write) => self.write_align_override.get(),
|
||||
None => None,
|
||||
};
|
||||
let align = align_override.map_or(align, |o| o.abi().min(align));
|
||||
// Check alignment
|
||||
if alloc_align < align {
|
||||
if alloc_align.abi() < required_align.abi() {
|
||||
return err!(AlignmentCheckFailed {
|
||||
has: alloc_align,
|
||||
required: align,
|
||||
has: alloc_align.abi(),
|
||||
required: required_align.abi(),
|
||||
});
|
||||
}
|
||||
if offset % align == 0 {
|
||||
if offset % required_align.abi() == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
err!(AlignmentCheckFailed {
|
||||
has: offset % align,
|
||||
required: align,
|
||||
has: offset % required_align.abi(),
|
||||
required: required_align.abi(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -435,7 +417,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
||||
"{}({} bytes, alignment {}){}",
|
||||
msg,
|
||||
alloc.bytes.len(),
|
||||
alloc.align,
|
||||
alloc.align.abi(),
|
||||
immutable
|
||||
);
|
||||
|
||||
@ -480,10 +462,10 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
||||
&self,
|
||||
ptr: MemoryPointer,
|
||||
size: u64,
|
||||
align: u64,
|
||||
align: Align,
|
||||
) -> EvalResult<'tcx, &[u8]> {
|
||||
// Zero-sized accesses can use dangling pointers, but they still have to be aligned and non-NULL
|
||||
self.check_align(ptr.into(), align, Some(AccessKind::Read))?;
|
||||
self.check_align(ptr.into(), align)?;
|
||||
if size == 0 {
|
||||
return Ok(&[]);
|
||||
}
|
||||
@ -500,10 +482,10 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
||||
&mut self,
|
||||
ptr: MemoryPointer,
|
||||
size: u64,
|
||||
align: u64,
|
||||
align: Align,
|
||||
) -> EvalResult<'tcx, &mut [u8]> {
|
||||
// Zero-sized accesses can use dangling pointers, but they still have to be aligned and non-NULL
|
||||
self.check_align(ptr.into(), align, Some(AccessKind::Write))?;
|
||||
self.check_align(ptr.into(), align)?;
|
||||
if size == 0 {
|
||||
return Ok(&mut []);
|
||||
}
|
||||
@ -516,7 +498,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
||||
Ok(&mut alloc.bytes[offset..offset + size as usize])
|
||||
}
|
||||
|
||||
fn get_bytes(&self, ptr: MemoryPointer, size: u64, align: u64) -> EvalResult<'tcx, &[u8]> {
|
||||
fn get_bytes(&self, ptr: MemoryPointer, size: u64, align: Align) -> EvalResult<'tcx, &[u8]> {
|
||||
assert_ne!(size, 0);
|
||||
if self.relocations(ptr, size)?.count() != 0 {
|
||||
return err!(ReadPointerAsBytes);
|
||||
@ -529,7 +511,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
||||
&mut self,
|
||||
ptr: MemoryPointer,
|
||||
size: u64,
|
||||
align: u64,
|
||||
align: Align,
|
||||
) -> EvalResult<'tcx, &mut [u8]> {
|
||||
assert_ne!(size, 0);
|
||||
self.clear_relocations(ptr, size)?;
|
||||
@ -627,14 +609,15 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
||||
pub fn copy(
|
||||
&mut self,
|
||||
src: Pointer,
|
||||
src_align: Align,
|
||||
dest: Pointer,
|
||||
dest_align: Align,
|
||||
size: u64,
|
||||
align: u64,
|
||||
nonoverlapping: bool,
|
||||
) -> EvalResult<'tcx> {
|
||||
// Empty accesses don't need to be valid pointers, but they should still be aligned
|
||||
self.check_align(src, align, Some(AccessKind::Read))?;
|
||||
self.check_align(dest, align, Some(AccessKind::Write))?;
|
||||
self.check_align(src, src_align)?;
|
||||
self.check_align(dest, dest_align)?;
|
||||
if size == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
@ -653,8 +636,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
||||
})
|
||||
.collect();
|
||||
|
||||
let src_bytes = self.get_bytes_unchecked(src, size, align)?.as_ptr();
|
||||
let dest_bytes = self.get_bytes_mut(dest, size, align)?.as_mut_ptr();
|
||||
let src_bytes = self.get_bytes_unchecked(src, size, src_align)?.as_ptr();
|
||||
let dest_bytes = self.get_bytes_mut(dest, size, dest_align)?.as_mut_ptr();
|
||||
|
||||
// SAFE: The above indexing would have panicked if there weren't at least `size` bytes
|
||||
// behind `src` and `dest`. Also, we use the overlapping-safe `ptr::copy` if `src` and
|
||||
@ -703,41 +686,44 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
||||
|
||||
pub fn read_bytes(&self, ptr: Pointer, size: u64) -> EvalResult<'tcx, &[u8]> {
|
||||
// Empty accesses don't need to be valid pointers, but they should still be non-NULL
|
||||
self.check_align(ptr, 1, Some(AccessKind::Read))?;
|
||||
let align = Align::from_bytes(1, 1).unwrap();
|
||||
self.check_align(ptr, align)?;
|
||||
if size == 0 {
|
||||
return Ok(&[]);
|
||||
}
|
||||
self.get_bytes(ptr.to_ptr()?, size, 1)
|
||||
self.get_bytes(ptr.to_ptr()?, size, align)
|
||||
}
|
||||
|
||||
pub fn write_bytes(&mut self, ptr: Pointer, src: &[u8]) -> EvalResult<'tcx> {
|
||||
// Empty accesses don't need to be valid pointers, but they should still be non-NULL
|
||||
self.check_align(ptr, 1, Some(AccessKind::Write))?;
|
||||
let align = Align::from_bytes(1, 1).unwrap();
|
||||
self.check_align(ptr, align)?;
|
||||
if src.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
let bytes = self.get_bytes_mut(ptr.to_ptr()?, src.len() as u64, 1)?;
|
||||
let bytes = self.get_bytes_mut(ptr.to_ptr()?, src.len() as u64, align)?;
|
||||
bytes.clone_from_slice(src);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write_repeat(&mut self, ptr: Pointer, val: u8, count: u64) -> EvalResult<'tcx> {
|
||||
// Empty accesses don't need to be valid pointers, but they should still be non-NULL
|
||||
self.check_align(ptr, 1, Some(AccessKind::Write))?;
|
||||
let align = Align::from_bytes(1, 1).unwrap();
|
||||
self.check_align(ptr, align)?;
|
||||
if count == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
let bytes = self.get_bytes_mut(ptr.to_ptr()?, count, 1)?;
|
||||
let bytes = self.get_bytes_mut(ptr.to_ptr()?, count, align)?;
|
||||
for b in bytes {
|
||||
*b = val;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn read_primval(&self, ptr: MemoryPointer, size: u64, signed: bool) -> EvalResult<'tcx, PrimVal> {
|
||||
pub fn read_primval(&self, ptr: MemoryPointer, ptr_align: Align, size: u64, signed: bool) -> EvalResult<'tcx, PrimVal> {
|
||||
self.check_relocation_edges(ptr, size)?; // Make sure we don't read part of a pointer as a pointer
|
||||
let endianess = self.endianess();
|
||||
let bytes = self.get_bytes_unchecked(ptr, size, self.int_align(size))?;
|
||||
let bytes = self.get_bytes_unchecked(ptr, size, ptr_align.min(self.int_align(size)))?;
|
||||
// Undef check happens *after* we established that the alignment is correct.
|
||||
// We must not return Ok() for unaligned pointers!
|
||||
if self.check_defined(ptr, size).is_err() {
|
||||
@ -765,11 +751,11 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
||||
Ok(PrimVal::Bytes(bytes))
|
||||
}
|
||||
|
||||
pub fn read_ptr_sized_unsigned(&self, ptr: MemoryPointer) -> EvalResult<'tcx, PrimVal> {
|
||||
self.read_primval(ptr, self.pointer_size(), false)
|
||||
pub fn read_ptr_sized_unsigned(&self, ptr: MemoryPointer, ptr_align: Align) -> EvalResult<'tcx, PrimVal> {
|
||||
self.read_primval(ptr, ptr_align, self.pointer_size(), false)
|
||||
}
|
||||
|
||||
pub fn write_primval(&mut self, ptr: MemoryPointer, val: PrimVal, size: u64, signed: bool) -> EvalResult<'tcx> {
|
||||
pub fn write_primval(&mut self, ptr: MemoryPointer, ptr_align: Align, val: PrimVal, size: u64, signed: bool) -> EvalResult<'tcx> {
|
||||
let endianess = self.endianess();
|
||||
|
||||
let bytes = match val {
|
||||
@ -800,7 +786,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
||||
|
||||
{
|
||||
let align = self.int_align(size);
|
||||
let dst = self.get_bytes_mut(ptr, size, align)?;
|
||||
let dst = self.get_bytes_mut(ptr, size, ptr_align.min(align))?;
|
||||
if signed {
|
||||
write_target_int(endianess, dst, bytes as i128).unwrap();
|
||||
} else {
|
||||
@ -822,22 +808,23 @@ impl<'a, 'tcx, M: Machine<'tcx>> Memory<'a, 'tcx, M> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write_ptr_sized_unsigned(&mut self, ptr: MemoryPointer, val: PrimVal) -> EvalResult<'tcx> {
|
||||
pub fn write_ptr_sized_unsigned(&mut self, ptr: MemoryPointer, ptr_align: Align, val: PrimVal) -> EvalResult<'tcx> {
|
||||
let ptr_size = self.pointer_size();
|
||||
self.write_primval(ptr, val, ptr_size, false)
|
||||
self.write_primval(ptr, ptr_align, val, ptr_size, false)
|
||||
}
|
||||
|
||||
fn int_align(&self, size: u64) -> u64 {
|
||||
fn int_align(&self, size: u64) -> Align {
|
||||
// We assume pointer-sized integers have the same alignment as pointers.
|
||||
// We also assume signed and unsigned integers of the same size have the same alignment.
|
||||
match size {
|
||||
1 => self.tcx.data_layout.i8_align.abi(),
|
||||
2 => self.tcx.data_layout.i16_align.abi(),
|
||||
4 => self.tcx.data_layout.i32_align.abi(),
|
||||
8 => self.tcx.data_layout.i64_align.abi(),
|
||||
16 => self.tcx.data_layout.i128_align.abi(),
|
||||
let ity = match size {
|
||||
1 => layout::I8,
|
||||
2 => layout::I16,
|
||||
4 => layout::I32,
|
||||
8 => layout::I64,
|
||||
16 => layout::I128,
|
||||
_ => bug!("bad integer size: {}", size),
|
||||
}
|
||||
};
|
||||
ity.align(self)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1002,43 +989,6 @@ pub trait HasMemory<'a, 'tcx: 'a, M: Machine<'tcx>> {
|
||||
fn memory_mut(&mut self) -> &mut Memory<'a, 'tcx, M>;
|
||||
fn memory(&self) -> &Memory<'a, 'tcx, M>;
|
||||
|
||||
// These are not supposed to be overriden.
|
||||
fn read_with_align<F, T>(&self, align: Align, f: F) -> EvalResult<'tcx, T>
|
||||
where
|
||||
F: FnOnce(&Self) -> EvalResult<'tcx, T>,
|
||||
{
|
||||
let old = self.memory().read_align_override.get();
|
||||
// Do alignment checking for the minimum align out of *all* nested calls.
|
||||
self.memory().read_align_override.set(Some(old.map_or(align, |old| old.min(align))));
|
||||
let t = f(self);
|
||||
self.memory().read_align_override.set(old);
|
||||
t
|
||||
}
|
||||
|
||||
fn read_with_align_mut<F, T>(&mut self, align: Align, f: F) -> EvalResult<'tcx, T>
|
||||
where
|
||||
F: FnOnce(&mut Self) -> EvalResult<'tcx, T>,
|
||||
{
|
||||
let old = self.memory().read_align_override.get();
|
||||
// Do alignment checking for the minimum align out of *all* nested calls.
|
||||
self.memory().read_align_override.set(Some(old.map_or(align, |old| old.min(align))));
|
||||
let t = f(self);
|
||||
self.memory().read_align_override.set(old);
|
||||
t
|
||||
}
|
||||
|
||||
fn write_with_align_mut<F, T>(&mut self, align: Align, f: F) -> EvalResult<'tcx, T>
|
||||
where
|
||||
F: FnOnce(&mut Self) -> EvalResult<'tcx, T>,
|
||||
{
|
||||
let old = self.memory().write_align_override.get();
|
||||
// Do alignment checking for the minimum align out of *all* nested calls.
|
||||
self.memory().write_align_override.set(Some(old.map_or(align, |old| old.min(align))));
|
||||
let t = f(self);
|
||||
self.memory().write_align_override.set(old);
|
||||
t
|
||||
}
|
||||
|
||||
/// Convert the value into a pointer (or a pointer-sized integer). If the value is a ByRef,
|
||||
/// this may have to perform a load.
|
||||
fn into_ptr(
|
||||
@ -1047,7 +997,7 @@ pub trait HasMemory<'a, 'tcx: 'a, M: Machine<'tcx>> {
|
||||
) -> EvalResult<'tcx, Pointer> {
|
||||
Ok(match value {
|
||||
Value::ByRef(ptr, align) => {
|
||||
self.memory().read_with_align(align, |mem| mem.read_ptr_sized_unsigned(ptr.to_ptr()?))?
|
||||
self.memory().read_ptr_sized_unsigned(ptr.to_ptr()?, align)?
|
||||
}
|
||||
Value::ByVal(ptr) |
|
||||
Value::ByValPair(ptr, _) => ptr,
|
||||
@ -1060,13 +1010,13 @@ pub trait HasMemory<'a, 'tcx: 'a, M: Machine<'tcx>> {
|
||||
) -> EvalResult<'tcx, (Pointer, MemoryPointer)> {
|
||||
match value {
|
||||
Value::ByRef(ref_ptr, align) => {
|
||||
self.memory().read_with_align(align, |mem| {
|
||||
let ptr = mem.read_ptr_sized_unsigned(ref_ptr.to_ptr()?)?.into();
|
||||
let vtable = mem.read_ptr_sized_unsigned(
|
||||
ref_ptr.offset(mem.pointer_size(), &mem.tcx.data_layout)?.to_ptr()?,
|
||||
)?.to_ptr()?;
|
||||
Ok((ptr, vtable))
|
||||
})
|
||||
let mem = self.memory();
|
||||
let ptr = mem.read_ptr_sized_unsigned(ref_ptr.to_ptr()?, align)?.into();
|
||||
let vtable = mem.read_ptr_sized_unsigned(
|
||||
ref_ptr.offset(mem.pointer_size(), &mem.tcx.data_layout)?.to_ptr()?,
|
||||
align
|
||||
)?.to_ptr()?;
|
||||
Ok((ptr, vtable))
|
||||
}
|
||||
|
||||
Value::ByValPair(ptr, vtable) => Ok((ptr.into(), vtable.to_ptr()?)),
|
||||
@ -1082,13 +1032,13 @@ pub trait HasMemory<'a, 'tcx: 'a, M: Machine<'tcx>> {
|
||||
) -> EvalResult<'tcx, (Pointer, u64)> {
|
||||
match value {
|
||||
Value::ByRef(ref_ptr, align) => {
|
||||
self.memory().read_with_align(align, |mem| {
|
||||
let ptr = mem.read_ptr_sized_unsigned(ref_ptr.to_ptr()?)?.into();
|
||||
let len = mem.read_ptr_sized_unsigned(
|
||||
ref_ptr.offset(mem.pointer_size(), &mem.tcx.data_layout)?.to_ptr()?,
|
||||
)?.to_bytes()? as u64;
|
||||
Ok((ptr, len))
|
||||
})
|
||||
let mem = self.memory();
|
||||
let ptr = mem.read_ptr_sized_unsigned(ref_ptr.to_ptr()?, align)?.into();
|
||||
let len = mem.read_ptr_sized_unsigned(
|
||||
ref_ptr.offset(mem.pointer_size(), &mem.tcx.data_layout)?.to_ptr()?,
|
||||
align
|
||||
)?.to_bytes()? as u64;
|
||||
Ok((ptr, len))
|
||||
}
|
||||
Value::ByValPair(ptr, val) => {
|
||||
let len = val.to_u128()?;
|
||||
|
@ -179,7 +179,7 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
assert!(!layout.is_unsized());
|
||||
let ptr = self.memory.allocate(
|
||||
layout.size.bytes(),
|
||||
layout.align.abi(),
|
||||
layout.align,
|
||||
None,
|
||||
)?;
|
||||
self.tcx.interpret_interner.borrow_mut().cache(cid, ptr.into());
|
||||
@ -264,7 +264,7 @@ impl<'a, 'b, 'tcx, M: Machine<'tcx>> Visitor<'tcx> for ConstantExtractor<'a, 'b,
|
||||
assert!(!layout.is_unsized());
|
||||
let ptr = this.ecx.memory.allocate(
|
||||
layout.size.bytes(),
|
||||
layout.align.abi(),
|
||||
layout.align,
|
||||
None,
|
||||
)?;
|
||||
this.ecx.tcx.interpret_interner.borrow_mut().cache(cid, ptr.into());
|
||||
|
@ -400,9 +400,11 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
// cannot use the shim here, because that will only result in infinite recursion
|
||||
ty::InstanceDef::Virtual(_, idx) => {
|
||||
let ptr_size = self.memory.pointer_size();
|
||||
let ptr_align = self.tcx.data_layout.pointer_align;
|
||||
let (ptr, vtable) = self.into_ptr_vtable_pair(args[0].value)?;
|
||||
let fn_ptr = self.memory.read_ptr_sized_unsigned(
|
||||
vtable.offset(ptr_size * (idx as u64 + 3), &self)?
|
||||
vtable.offset(ptr_size * (idx as u64 + 3), &self)?,
|
||||
ptr_align
|
||||
)?.to_ptr()?;
|
||||
let instance = self.memory.get_fn(fn_ptr)?;
|
||||
let mut args = args.to_vec();
|
||||
|
@ -26,28 +26,29 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
let align = layout.align.abi();
|
||||
|
||||
let ptr_size = self.memory.pointer_size();
|
||||
let ptr_align = self.tcx.data_layout.pointer_align;
|
||||
let methods = self.tcx.vtable_methods(trait_ref);
|
||||
let vtable = self.memory.allocate(
|
||||
ptr_size * (3 + methods.len() as u64),
|
||||
ptr_size,
|
||||
ptr_align,
|
||||
None,
|
||||
)?;
|
||||
|
||||
let drop = eval_context::resolve_drop_in_place(self.tcx, ty);
|
||||
let drop = self.memory.create_fn_alloc(drop);
|
||||
self.memory.write_ptr_sized_unsigned(vtable, PrimVal::Ptr(drop))?;
|
||||
self.memory.write_ptr_sized_unsigned(vtable, ptr_align, PrimVal::Ptr(drop))?;
|
||||
|
||||
let size_ptr = vtable.offset(ptr_size, &self)?;
|
||||
self.memory.write_ptr_sized_unsigned(size_ptr, PrimVal::Bytes(size as u128))?;
|
||||
self.memory.write_ptr_sized_unsigned(size_ptr, ptr_align, PrimVal::Bytes(size as u128))?;
|
||||
let align_ptr = vtable.offset(ptr_size * 2, &self)?;
|
||||
self.memory.write_ptr_sized_unsigned(align_ptr, PrimVal::Bytes(align as u128))?;
|
||||
self.memory.write_ptr_sized_unsigned(align_ptr, ptr_align, PrimVal::Bytes(align as u128))?;
|
||||
|
||||
for (i, method) in methods.iter().enumerate() {
|
||||
if let Some((def_id, substs)) = *method {
|
||||
let instance = self.resolve(def_id, substs)?;
|
||||
let fn_ptr = self.memory.create_fn_alloc(instance);
|
||||
let method_ptr = vtable.offset(ptr_size * (3 + i as u64), &self)?;
|
||||
self.memory.write_ptr_sized_unsigned(method_ptr, PrimVal::Ptr(fn_ptr))?;
|
||||
self.memory.write_ptr_sized_unsigned(method_ptr, ptr_align, PrimVal::Ptr(fn_ptr))?;
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,7 +65,8 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
vtable: MemoryPointer,
|
||||
) -> EvalResult<'tcx, Option<ty::Instance<'tcx>>> {
|
||||
// we don't care about the pointee type, we just want a pointer
|
||||
match self.read_ptr(vtable, self.tcx.mk_nil_ptr())? {
|
||||
let pointer_align = self.tcx.data_layout.pointer_align;
|
||||
match self.read_ptr(vtable, pointer_align, self.tcx.mk_nil_ptr())? {
|
||||
// some values don't need to call a drop impl, so the value is null
|
||||
Value::ByVal(PrimVal::Bytes(0)) => Ok(None),
|
||||
Value::ByVal(PrimVal::Ptr(drop_fn)) => self.memory.get_fn(drop_fn).map(Some),
|
||||
@ -77,9 +79,11 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
|
||||
vtable: MemoryPointer,
|
||||
) -> EvalResult<'tcx, (Size, Align)> {
|
||||
let pointer_size = self.memory.pointer_size();
|
||||
let size = self.memory.read_ptr_sized_unsigned(vtable.offset(pointer_size, self)?)?.to_bytes()? as u64;
|
||||
let pointer_align = self.tcx.data_layout.pointer_align;
|
||||
let size = self.memory.read_ptr_sized_unsigned(vtable.offset(pointer_size, self)?, pointer_align)?.to_bytes()? as u64;
|
||||
let align = self.memory.read_ptr_sized_unsigned(
|
||||
vtable.offset(pointer_size * 2, self)?
|
||||
vtable.offset(pointer_size * 2, self)?,
|
||||
pointer_align
|
||||
)?.to_bytes()? as u64;
|
||||
Ok((Size::from_bytes(size), Align::from_bytes(align, align).unwrap()))
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user