Move some byte and scalar accessors from Memory
to Allocation
This commit is contained in:
parent
ad11856431
commit
9ecde5712e
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user