2018-10-23 17:54:20 +02:00
|
|
|
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
|
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
2018-10-23 18:05:32 +02:00
|
|
|
//! The virtual memory representation of the MIR interpreter
|
2018-10-23 17:54:20 +02:00
|
|
|
|
2018-11-04 15:14:54 +01:00
|
|
|
use super::{Pointer, EvalResult, AllocId};
|
|
|
|
|
2018-10-23 18:05:32 +02:00
|
|
|
use ty::layout::{Size, Align};
|
2018-10-23 17:54:20 +02:00
|
|
|
use syntax::ast::Mutability;
|
2018-10-25 16:09:42 +02:00
|
|
|
use std::iter;
|
|
|
|
use mir;
|
|
|
|
use std::ops::{Deref, DerefMut};
|
|
|
|
use rustc_data_structures::sorted_map::SortedMap;
|
2018-10-23 17:54:20 +02:00
|
|
|
|
|
|
|
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)]
|
|
|
|
pub struct Allocation<Tag=(),Extra=()> {
|
|
|
|
/// The actual bytes of the allocation.
|
|
|
|
/// Note that the bytes of a pointer represent the offset of the pointer
|
|
|
|
pub bytes: Vec<u8>,
|
|
|
|
/// Maps from byte addresses to extra data for each pointer.
|
|
|
|
/// Only the first byte of a pointer is inserted into the map; i.e.,
|
|
|
|
/// every entry in this map applies to `pointer_size` consecutive bytes starting
|
|
|
|
/// at the given offset.
|
|
|
|
pub relocations: Relocations<Tag>,
|
|
|
|
/// 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: Align,
|
|
|
|
/// Whether the allocation is mutable.
|
|
|
|
/// Also used by codegen to determine if a static should be put into mutable memory,
|
|
|
|
/// which happens for `static mut` and `static` with interior mutability.
|
|
|
|
pub mutability: Mutability,
|
|
|
|
/// Extra state for the machine.
|
|
|
|
pub extra: Extra,
|
|
|
|
}
|
|
|
|
|
2018-11-04 15:14:54 +01:00
|
|
|
pub trait AllocationExtra<Tag>: ::std::fmt::Debug + Default + Clone {
|
2018-10-23 18:32:50 +02:00
|
|
|
/// Hook for performing extra checks on a memory read access.
|
|
|
|
///
|
|
|
|
/// Takes read-only access to the allocation so we can keep all the memory read
|
|
|
|
/// operations take `&self`. Use a `RefCell` in `AllocExtra` if you
|
|
|
|
/// need to mutate.
|
|
|
|
#[inline]
|
|
|
|
fn memory_read(
|
2018-11-04 16:01:50 +01:00
|
|
|
_alloc: &Allocation<Tag, Self>,
|
2018-11-04 15:14:54 +01:00
|
|
|
_ptr: Pointer<Tag>,
|
2018-10-23 18:32:50 +02:00
|
|
|
_size: Size,
|
|
|
|
) -> EvalResult<'tcx> {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Hook for performing extra checks on a memory write access.
|
|
|
|
#[inline]
|
|
|
|
fn memory_written(
|
2018-11-04 16:01:50 +01:00
|
|
|
_alloc: &mut Allocation<Tag, Self>,
|
2018-11-12 08:39:13 +01:00
|
|
|
_ptr: Pointer<Tag>,
|
|
|
|
_size: Size,
|
|
|
|
) -> EvalResult<'tcx> {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Hook for performing extra checks on a memory deallocation.
|
|
|
|
/// `size` will be the size of the allocation.
|
|
|
|
#[inline]
|
|
|
|
fn memory_deallocated(
|
|
|
|
_alloc: &mut Allocation<Tag, Self>,
|
2018-11-04 15:14:54 +01:00
|
|
|
_ptr: Pointer<Tag>,
|
2018-10-23 18:32:50 +02:00
|
|
|
_size: Size,
|
|
|
|
) -> EvalResult<'tcx> {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-04 15:14:54 +01:00
|
|
|
impl AllocationExtra<()> for () {}
|
|
|
|
|
2018-10-23 17:54:20 +02:00
|
|
|
impl<Tag, Extra: Default> Allocation<Tag, Extra> {
|
|
|
|
/// Creates a read-only allocation initialized by the given bytes
|
|
|
|
pub fn from_bytes(slice: &[u8], align: Align) -> Self {
|
|
|
|
let mut undef_mask = UndefMask::new(Size::ZERO);
|
|
|
|
undef_mask.grow(Size::from_bytes(slice.len() as u64), true);
|
|
|
|
Self {
|
|
|
|
bytes: slice.to_owned(),
|
|
|
|
relocations: Relocations::new(),
|
|
|
|
undef_mask,
|
|
|
|
align,
|
|
|
|
mutability: Mutability::Immutable,
|
|
|
|
extra: Extra::default(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn from_byte_aligned_bytes(slice: &[u8]) -> Self {
|
|
|
|
Allocation::from_bytes(slice, Align::from_bytes(1, 1).unwrap())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn undef(size: Size, align: Align) -> Self {
|
|
|
|
assert_eq!(size.bytes() as usize as u64, size.bytes());
|
|
|
|
Allocation {
|
|
|
|
bytes: vec![0; size.bytes() as usize],
|
|
|
|
relocations: Relocations::new(),
|
|
|
|
undef_mask: UndefMask::new(size),
|
|
|
|
align,
|
|
|
|
mutability: Mutability::Mutable,
|
|
|
|
extra: Extra::default(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'tcx> ::serialize::UseSpecializedDecodable for &'tcx Allocation {}
|
2018-10-25 16:09:42 +02:00
|
|
|
|
|
|
|
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, RustcEncodable, RustcDecodable)]
|
|
|
|
pub struct Relocations<Tag=(), Id=AllocId>(SortedMap<Size, (Tag, Id)>);
|
|
|
|
|
|
|
|
impl<Tag, Id> Relocations<Tag, Id> {
|
|
|
|
pub fn new() -> Self {
|
|
|
|
Relocations(SortedMap::new())
|
|
|
|
}
|
|
|
|
|
|
|
|
// The caller must guarantee that the given relocations are already sorted
|
|
|
|
// by address and contain no duplicates.
|
|
|
|
pub fn from_presorted(r: Vec<(Size, (Tag, Id))>) -> Self {
|
|
|
|
Relocations(SortedMap::from_presorted_elements(r))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<Tag> Deref for Relocations<Tag> {
|
|
|
|
type Target = SortedMap<Size, (Tag, AllocId)>;
|
|
|
|
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
&self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<Tag> DerefMut for Relocations<Tag> {
|
|
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
|
|
&mut self.0
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Undefined byte tracking
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
type Block = u64;
|
|
|
|
const BLOCK_SIZE: u64 = 64;
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash, RustcEncodable, RustcDecodable)]
|
|
|
|
pub struct UndefMask {
|
|
|
|
blocks: Vec<Block>,
|
|
|
|
len: Size,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl_stable_hash_for!(struct mir::interpret::UndefMask{blocks, len});
|
|
|
|
|
|
|
|
impl UndefMask {
|
|
|
|
pub fn new(size: Size) -> Self {
|
|
|
|
let mut m = UndefMask {
|
|
|
|
blocks: vec![],
|
|
|
|
len: Size::ZERO,
|
|
|
|
};
|
|
|
|
m.grow(size, false);
|
|
|
|
m
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Check whether the range `start..end` (end-exclusive) is entirely defined.
|
|
|
|
///
|
|
|
|
/// Returns `Ok(())` if it's defined. Otherwise returns the index of the byte
|
|
|
|
/// at which the first undefined access begins.
|
|
|
|
#[inline]
|
|
|
|
pub fn is_range_defined(&self, start: Size, end: Size) -> Result<(), Size> {
|
|
|
|
if end > self.len {
|
|
|
|
return Err(self.len);
|
|
|
|
}
|
|
|
|
|
|
|
|
let idx = (start.bytes()..end.bytes())
|
|
|
|
.map(|i| Size::from_bytes(i))
|
|
|
|
.find(|&i| !self.get(i));
|
|
|
|
|
|
|
|
match idx {
|
|
|
|
Some(idx) => Err(idx),
|
|
|
|
None => Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_range(&mut self, start: Size, end: Size, new_state: bool) {
|
|
|
|
let len = self.len;
|
|
|
|
if end > len {
|
|
|
|
self.grow(end - len, new_state);
|
|
|
|
}
|
|
|
|
self.set_range_inbounds(start, end, new_state);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_range_inbounds(&mut self, start: Size, end: Size, new_state: bool) {
|
|
|
|
for i in start.bytes()..end.bytes() {
|
|
|
|
self.set(Size::from_bytes(i), new_state);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn get(&self, i: Size) -> bool {
|
|
|
|
let (block, bit) = bit_index(i);
|
|
|
|
(self.blocks[block] & 1 << bit) != 0
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
pub fn set(&mut self, i: Size, new_state: bool) {
|
|
|
|
let (block, bit) = bit_index(i);
|
|
|
|
if new_state {
|
|
|
|
self.blocks[block] |= 1 << bit;
|
|
|
|
} else {
|
|
|
|
self.blocks[block] &= !(1 << bit);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn grow(&mut self, amount: Size, new_state: bool) {
|
|
|
|
let unused_trailing_bits = self.blocks.len() as u64 * BLOCK_SIZE - self.len.bytes();
|
|
|
|
if amount.bytes() > unused_trailing_bits {
|
|
|
|
let additional_blocks = amount.bytes() / BLOCK_SIZE + 1;
|
|
|
|
assert_eq!(additional_blocks as usize as u64, additional_blocks);
|
|
|
|
self.blocks.extend(
|
|
|
|
iter::repeat(0).take(additional_blocks as usize),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
let start = self.len;
|
|
|
|
self.len += amount;
|
|
|
|
self.set_range_inbounds(start, start + amount, new_state);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn bit_index(bits: Size) -> (usize, usize) {
|
|
|
|
let bits = bits.bytes();
|
|
|
|
let a = bits / BLOCK_SIZE;
|
|
|
|
let b = bits % BLOCK_SIZE;
|
|
|
|
assert_eq!(a as usize as u64, a);
|
|
|
|
assert_eq!(b as usize as u64, b);
|
|
|
|
(a as usize, b as usize)
|
|
|
|
}
|