Move some byte and scalar accessors from Memory to Allocation

This commit is contained in:
Oliver Scherer 2018-11-12 09:00:41 +01:00
parent ad11856431
commit 9ecde5712e
2 changed files with 200 additions and 197 deletions

View File

@ -86,6 +86,206 @@ impl<'tcx, Tag, Extra> Allocation<Tag, Extra> {
}
}
/// Reading and writing
impl<'tcx, Tag: Copy, Extra: AllocationExtra<Tag>> Allocation<Tag, Extra> {
pub fn read_c_str(&self, ptr: Pointer<M::PointerTag>) -> EvalResult<'tcx, &[u8]> {
let alloc = self.get(ptr.alloc_id)?;
assert_eq!(ptr.offset.bytes() as usize as u64, ptr.offset.bytes());
let offset = ptr.offset.bytes() as usize;
match alloc.bytes[offset..].iter().position(|&c| c == 0) {
Some(size) => {
let p1 = Size::from_bytes((size + 1) as u64);
self.check_relocations(ptr, p1)?;
self.check_defined(ptr, p1)?;
Ok(&alloc.bytes[offset..offset + size])
}
None => err!(UnterminatedCString(ptr.erase_tag())),
}
}
pub fn check_bytes(
&self,
ptr: Scalar<M::PointerTag>,
size: Size,
allow_ptr_and_undef: bool,
) -> EvalResult<'tcx> {
// Empty accesses don't need to be valid pointers, but they should still be non-NULL
let align = Align::from_bytes(1).unwrap();
if size.bytes() == 0 {
self.check_align(ptr, align)?;
return Ok(());
}
let ptr = ptr.to_ptr()?;
// Check bounds, align and relocations on the edges
self.get_bytes_with_undef_and_ptr(ptr, size, align)?;
// Check undef and ptr
if !allow_ptr_and_undef {
self.check_defined(ptr, size)?;
self.check_relocations(ptr, size)?;
}
Ok(())
}
pub fn read_bytes(&self, ptr: Scalar<M::PointerTag>, size: Size) -> EvalResult<'tcx, &[u8]> {
// Empty accesses don't need to be valid pointers, but they should still be non-NULL
let align = Align::from_bytes(1).unwrap();
if size.bytes() == 0 {
self.check_align(ptr, align)?;
return Ok(&[]);
}
self.get_bytes(ptr.to_ptr()?, size, align)
}
pub fn write_bytes(&mut self, ptr: Scalar<M::PointerTag>, src: &[u8]) -> EvalResult<'tcx> {
// Empty accesses don't need to be valid pointers, but they should still be non-NULL
let align = Align::from_bytes(1).unwrap();
if src.is_empty() {
self.check_align(ptr, align)?;
return Ok(());
}
let bytes = self.get_bytes_mut(ptr.to_ptr()?, Size::from_bytes(src.len() as u64), align)?;
bytes.clone_from_slice(src);
Ok(())
}
pub fn write_repeat(
&mut self,
ptr: Scalar<M::PointerTag>,
val: u8,
count: Size
) -> EvalResult<'tcx> {
// Empty accesses don't need to be valid pointers, but they should still be non-NULL
let align = Align::from_bytes(1).unwrap();
if count.bytes() == 0 {
self.check_align(ptr, align)?;
return Ok(());
}
let bytes = self.get_bytes_mut(ptr.to_ptr()?, count, align)?;
for b in bytes {
*b = val;
}
Ok(())
}
/// Read a *non-ZST* scalar
pub fn read_scalar(
&self,
ptr: Pointer<M::PointerTag>,
ptr_align: Align,
size: Size
) -> EvalResult<'tcx, ScalarMaybeUndef<M::PointerTag>> {
// get_bytes_unchecked tests alignment and relocation edges
let bytes = self.get_bytes_with_undef_and_ptr(
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() {
// this inflates undefined bytes to the entire scalar, even if only a few
// bytes are undefined
return Ok(ScalarMaybeUndef::Undef);
}
// Now we do the actual reading
let bits = read_target_uint(self.tcx.data_layout.endian, bytes).unwrap();
// See if we got a pointer
if size != self.pointer_size() {
// *Now* better make sure that the inside also is free of relocations.
self.check_relocations(ptr, size)?;
} else {
let alloc = self.get(ptr.alloc_id)?;
match alloc.relocations.get(&ptr.offset) {
Some(&(tag, alloc_id)) => {
let ptr = Pointer::new_with_tag(alloc_id, Size::from_bytes(bits as u64), tag);
return Ok(ScalarMaybeUndef::Scalar(ptr.into()))
}
None => {},
}
}
// We don't. Just return the bits.
Ok(ScalarMaybeUndef::Scalar(Scalar::from_uint(bits, size)))
}
pub fn read_ptr_sized(
&self,
ptr: Pointer<M::PointerTag>,
ptr_align: Align
) -> EvalResult<'tcx, ScalarMaybeUndef<M::PointerTag>> {
self.read_scalar(ptr, ptr_align, self.pointer_size())
}
/// Write a *non-ZST* scalar
pub fn write_scalar(
&mut self,
ptr: Pointer<M::PointerTag>,
ptr_align: Align,
val: ScalarMaybeUndef<M::PointerTag>,
type_size: Size,
) -> EvalResult<'tcx> {
let val = match val {
ScalarMaybeUndef::Scalar(scalar) => scalar,
ScalarMaybeUndef::Undef => return self.mark_definedness(ptr, type_size, false),
};
let bytes = match val {
Scalar::Ptr(val) => {
assert_eq!(type_size, self.pointer_size());
val.offset.bytes() as u128
}
Scalar::Bits { bits, size } => {
assert_eq!(size as u64, type_size.bytes());
debug_assert_eq!(truncate(bits, Size::from_bytes(size.into())), bits,
"Unexpected value of size {} when writing to memory", size);
bits
},
};
{
// get_bytes_mut checks alignment
let endian = self.tcx.data_layout.endian;
let dst = self.get_bytes_mut(ptr, type_size, ptr_align)?;
write_target_uint(endian, dst, bytes).unwrap();
}
// See if we have to also write a relocation
match val {
Scalar::Ptr(val) => {
self.get_mut(ptr.alloc_id)?.relocations.insert(
ptr.offset,
(val.tag, val.alloc_id),
);
}
_ => {}
}
Ok(())
}
pub fn write_ptr_sized(
&mut self,
ptr: Pointer<M::PointerTag>,
ptr_align: Align,
val: ScalarMaybeUndef<M::PointerTag>
) -> EvalResult<'tcx> {
let ptr_size = self.pointer_size();
self.write_scalar(ptr.into(), ptr_align, val, ptr_size)
}
fn int_align(&self, size: Size) -> 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.
let ity = match size.bytes() {
1 => layout::I8,
2 => layout::I16,
4 => layout::I32,
8 => layout::I64,
16 => layout::I128,
_ => bug!("bad integer size: {}", size.bytes()),
};
ity.align(self).abi
}
}
/// Byte accessors
impl<'tcx, Tag: Copy, Extra: AllocationExtra<Tag>> Allocation<Tag, Extra> {
/// The last argument controls whether we error out when there are undefined

View File

@ -714,203 +714,6 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
Ok(())
}
pub fn read_c_str(&self, ptr: Pointer<M::PointerTag>) -> EvalResult<'tcx, &[u8]> {
let alloc = self.get(ptr.alloc_id)?;
assert_eq!(ptr.offset.bytes() as usize as u64, ptr.offset.bytes());
let offset = ptr.offset.bytes() as usize;
match alloc.bytes[offset..].iter().position(|&c| c == 0) {
Some(size) => {
let p1 = Size::from_bytes((size + 1) as u64);
self.check_relocations(ptr, p1)?;
self.check_defined(ptr, p1)?;
Ok(&alloc.bytes[offset..offset + size])
}
None => err!(UnterminatedCString(ptr.erase_tag())),
}
}
pub fn check_bytes(
&self,
ptr: Scalar<M::PointerTag>,
size: Size,
allow_ptr_and_undef: bool,
) -> EvalResult<'tcx> {
// Empty accesses don't need to be valid pointers, but they should still be non-NULL
let align = Align::from_bytes(1).unwrap();
if size.bytes() == 0 {
self.check_align(ptr, align)?;
return Ok(());
}
let ptr = ptr.to_ptr()?;
// Check bounds, align and relocations on the edges
self.get_bytes_with_undef_and_ptr(ptr, size, align)?;
// Check undef and ptr
if !allow_ptr_and_undef {
self.check_defined(ptr, size)?;
self.check_relocations(ptr, size)?;
}
Ok(())
}
pub fn read_bytes(&self, ptr: Scalar<M::PointerTag>, size: Size) -> EvalResult<'tcx, &[u8]> {
// Empty accesses don't need to be valid pointers, but they should still be non-NULL
let align = Align::from_bytes(1).unwrap();
if size.bytes() == 0 {
self.check_align(ptr, align)?;
return Ok(&[]);
}
self.get_bytes(ptr.to_ptr()?, size, align)
}
pub fn write_bytes(&mut self, ptr: Scalar<M::PointerTag>, src: &[u8]) -> EvalResult<'tcx> {
// Empty accesses don't need to be valid pointers, but they should still be non-NULL
let align = Align::from_bytes(1).unwrap();
if src.is_empty() {
self.check_align(ptr, align)?;
return Ok(());
}
let bytes = self.get_bytes_mut(ptr.to_ptr()?, Size::from_bytes(src.len() as u64), align)?;
bytes.clone_from_slice(src);
Ok(())
}
pub fn write_repeat(
&mut self,
ptr: Scalar<M::PointerTag>,
val: u8,
count: Size
) -> EvalResult<'tcx> {
// Empty accesses don't need to be valid pointers, but they should still be non-NULL
let align = Align::from_bytes(1).unwrap();
if count.bytes() == 0 {
self.check_align(ptr, align)?;
return Ok(());
}
let bytes = self.get_bytes_mut(ptr.to_ptr()?, count, align)?;
for b in bytes {
*b = val;
}
Ok(())
}
/// Read a *non-ZST* scalar
pub fn read_scalar(
&self,
ptr: Pointer<M::PointerTag>,
ptr_align: Align,
size: Size
) -> EvalResult<'tcx, ScalarMaybeUndef<M::PointerTag>> {
// get_bytes_unchecked tests alignment and relocation edges
let bytes = self.get_bytes_with_undef_and_ptr(
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() {
// this inflates undefined bytes to the entire scalar, even if only a few
// bytes are undefined
return Ok(ScalarMaybeUndef::Undef);
}
// Now we do the actual reading
let bits = read_target_uint(self.tcx.data_layout.endian, bytes).unwrap();
// See if we got a pointer
if size != self.pointer_size() {
// *Now* better make sure that the inside also is free of relocations.
self.check_relocations(ptr, size)?;
} else {
let alloc = self.get(ptr.alloc_id)?;
match alloc.relocations.get(&ptr.offset) {
Some(&(tag, alloc_id)) => {
let ptr = Pointer::new_with_tag(alloc_id, Size::from_bytes(bits as u64), tag);
return Ok(ScalarMaybeUndef::Scalar(ptr.into()))
}
None => {},
}
}
// We don't. Just return the bits.
Ok(ScalarMaybeUndef::Scalar(Scalar::from_uint(bits, size)))
}
pub fn read_ptr_sized(
&self,
ptr: Pointer<M::PointerTag>,
ptr_align: Align
) -> EvalResult<'tcx, ScalarMaybeUndef<M::PointerTag>> {
self.read_scalar(ptr, ptr_align, self.pointer_size())
}
/// Write a *non-ZST* scalar
pub fn write_scalar(
&mut self,
ptr: Pointer<M::PointerTag>,
ptr_align: Align,
val: ScalarMaybeUndef<M::PointerTag>,
type_size: Size,
) -> EvalResult<'tcx> {
let val = match val {
ScalarMaybeUndef::Scalar(scalar) => scalar,
ScalarMaybeUndef::Undef => return self.mark_definedness(ptr, type_size, false),
};
let bytes = match val {
Scalar::Ptr(val) => {
assert_eq!(type_size, self.pointer_size());
val.offset.bytes() as u128
}
Scalar::Bits { bits, size } => {
assert_eq!(size as u64, type_size.bytes());
debug_assert_eq!(truncate(bits, Size::from_bytes(size.into())), bits,
"Unexpected value of size {} when writing to memory", size);
bits
},
};
{
// get_bytes_mut checks alignment
let endian = self.tcx.data_layout.endian;
let dst = self.get_bytes_mut(ptr, type_size, ptr_align)?;
write_target_uint(endian, dst, bytes).unwrap();
}
// See if we have to also write a relocation
match val {
Scalar::Ptr(val) => {
self.get_mut(ptr.alloc_id)?.relocations.insert(
ptr.offset,
(val.tag, val.alloc_id),
);
}
_ => {}
}
Ok(())
}
pub fn write_ptr_sized(
&mut self,
ptr: Pointer<M::PointerTag>,
ptr_align: Align,
val: ScalarMaybeUndef<M::PointerTag>
) -> EvalResult<'tcx> {
let ptr_size = self.pointer_size();
self.write_scalar(ptr.into(), ptr_align, val, ptr_size)
}
fn int_align(&self, size: Size) -> 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.
let ity = match size.bytes() {
1 => layout::I8,
2 => layout::I16,
4 => layout::I32,
8 => layout::I64,
16 => layout::I128,
_ => bug!("bad integer size: {}", size.bytes()),
};
ity.align(self).abi
}
}
/// Undefined bytes