}
}
+/// 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
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