2017-07-21 10:25:30 -05:00
|
|
|
//! This module contains everything needed to instantiate an interpreter.
|
|
|
|
//! This separation exists to ensure that no fancy miri features like
|
|
|
|
//! interpreting common C functions leak into CTFE.
|
|
|
|
|
2018-10-05 08:13:59 -05:00
|
|
|
use std::borrow::{Borrow, Cow};
|
2018-09-21 16:32:59 -05:00
|
|
|
use std::hash::Hash;
|
|
|
|
|
2019-05-28 03:44:46 -05:00
|
|
|
use rustc::hir::def_id::DefId;
|
2017-12-12 10:14:49 -06:00
|
|
|
use rustc::mir;
|
2019-08-10 12:40:56 -05:00
|
|
|
use rustc::ty::{self, Ty, TyCtxt};
|
2019-10-24 10:03:57 -05:00
|
|
|
use syntax_pos::Span;
|
2018-08-23 14:22:27 -05:00
|
|
|
|
2018-10-16 07:50:07 -05:00
|
|
|
use super::{
|
2019-07-31 02:18:54 -05:00
|
|
|
Allocation, AllocId, InterpResult, Scalar, AllocationExtra,
|
|
|
|
InterpCx, PlaceTy, OpTy, ImmTy, MemoryKind, Pointer, Memory,
|
2019-09-24 20:12:59 -05:00
|
|
|
Frame, Operand,
|
2018-10-16 07:50:07 -05:00
|
|
|
};
|
|
|
|
|
2019-04-16 20:04:54 -05:00
|
|
|
/// Data returned by Machine::stack_pop,
|
|
|
|
/// to provide further control over the popping of the stack frame
|
2019-10-20 11:51:25 -05:00
|
|
|
#[derive(Eq, PartialEq, Debug, Copy, Clone)]
|
2019-04-16 20:04:54 -05:00
|
|
|
pub enum StackPopInfo {
|
2019-11-04 12:49:50 -06:00
|
|
|
/// Indicates that no special handling should be
|
|
|
|
/// done - we'll either return normally or unwind
|
|
|
|
/// based on the terminator for the function
|
|
|
|
/// we're leaving.
|
2019-04-16 20:04:54 -05:00
|
|
|
Normal,
|
|
|
|
|
|
|
|
/// Indicates that we should stop unwinding,
|
|
|
|
/// as we've reached a catch frame
|
|
|
|
StopUnwinding
|
|
|
|
}
|
|
|
|
|
2018-10-16 05:45:44 -05:00
|
|
|
/// Whether this kind of memory is allowed to leak
|
|
|
|
pub trait MayLeak: Copy {
|
|
|
|
fn may_leak(self) -> bool;
|
|
|
|
}
|
|
|
|
|
2018-10-05 08:13:59 -05:00
|
|
|
/// The functionality needed by memory to manage its allocations
|
|
|
|
pub trait AllocMap<K: Hash + Eq, V> {
|
2019-02-08 07:53:55 -06:00
|
|
|
/// Tests if the map contains the given key.
|
2018-10-05 08:13:59 -05:00
|
|
|
/// Deliberately takes `&mut` because that is sufficient, and some implementations
|
|
|
|
/// can be more efficient then (using `RefCell::get_mut`).
|
|
|
|
fn contains_key<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> bool
|
|
|
|
where K: Borrow<Q>;
|
|
|
|
|
2019-02-08 07:53:55 -06:00
|
|
|
/// Inserts a new entry into the map.
|
2018-10-05 08:13:59 -05:00
|
|
|
fn insert(&mut self, k: K, v: V) -> Option<V>;
|
|
|
|
|
2019-02-08 07:53:55 -06:00
|
|
|
/// Removes an entry from the map.
|
2018-10-05 08:13:59 -05:00
|
|
|
fn remove<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> Option<V>
|
|
|
|
where K: Borrow<Q>;
|
|
|
|
|
2019-02-08 07:53:55 -06:00
|
|
|
/// Returns data based the keys and values in the map.
|
2018-10-05 08:13:59 -05:00
|
|
|
fn filter_map_collect<T>(&self, f: impl FnMut(&K, &V) -> Option<T>) -> Vec<T>;
|
|
|
|
|
2019-02-08 07:53:55 -06:00
|
|
|
/// Returns a reference to entry `k`. If no such entry exists, call
|
2018-10-05 08:13:59 -05:00
|
|
|
/// `vacant` and either forward its error, or add its result to the map
|
|
|
|
/// and return a reference to *that*.
|
|
|
|
fn get_or<E>(
|
|
|
|
&self,
|
|
|
|
k: K,
|
|
|
|
vacant: impl FnOnce() -> Result<V, E>
|
|
|
|
) -> Result<&V, E>;
|
|
|
|
|
2019-02-08 07:53:55 -06:00
|
|
|
/// Returns a mutable reference to entry `k`. If no such entry exists, call
|
2018-10-05 08:13:59 -05:00
|
|
|
/// `vacant` and either forward its error, or add its result to the map
|
|
|
|
/// and return a reference to *that*.
|
|
|
|
fn get_mut_or<E>(
|
|
|
|
&mut self,
|
|
|
|
k: K,
|
|
|
|
vacant: impl FnOnce() -> Result<V, E>
|
|
|
|
) -> Result<&mut V, E>;
|
2019-07-28 05:58:39 -05:00
|
|
|
|
|
|
|
/// Read-only lookup.
|
|
|
|
fn get(&self, k: K) -> Option<&V> {
|
2019-07-28 15:30:19 -05:00
|
|
|
self.get_or(k, || Err(())).ok()
|
2019-07-28 05:58:39 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Mutable lookup.
|
|
|
|
fn get_mut(&mut self, k: K) -> Option<&mut V> {
|
2019-07-28 15:30:19 -05:00
|
|
|
self.get_mut_or(k, || Err(())).ok()
|
2019-07-28 05:58:39 -05:00
|
|
|
}
|
2018-10-05 08:13:59 -05:00
|
|
|
}
|
2018-08-23 12:04:33 -05:00
|
|
|
|
2017-07-21 10:25:30 -05:00
|
|
|
/// Methods of this trait signifies a point where CTFE evaluation would fail
|
2018-09-20 03:12:21 -05:00
|
|
|
/// and some use case dependent behaviour can instead be applied.
|
2019-06-11 14:03:44 -05:00
|
|
|
pub trait Machine<'mir, 'tcx>: Sized {
|
2017-07-28 09:48:43 -05:00
|
|
|
/// Additional memory kinds a machine wishes to distinguish from the builtin ones
|
2018-10-16 05:45:44 -05:00
|
|
|
type MemoryKinds: ::std::fmt::Debug + MayLeak + Eq + 'static;
|
2018-10-16 02:15:13 -05:00
|
|
|
|
2019-02-08 07:53:55 -06:00
|
|
|
/// Tag tracked alongside every pointer. This is used to implement "Stacked Borrows"
|
2018-10-16 02:15:13 -05:00
|
|
|
/// <https://www.ralfj.de/blog/2018/08/07/stacked-borrows.html>.
|
2018-10-22 10:15:42 -05:00
|
|
|
/// The `default()` is used for pointers to consts, statics, vtables and functions.
|
2019-05-28 03:44:46 -05:00
|
|
|
type PointerTag: ::std::fmt::Debug + Copy + Eq + Hash + 'static;
|
2018-10-16 02:15:13 -05:00
|
|
|
|
2019-06-30 06:51:18 -05:00
|
|
|
/// Machines can define extra (non-instance) things that represent values of function pointers.
|
2019-11-26 21:19:54 -06:00
|
|
|
/// For example, Miri uses this to return a function pointer from `dlsym`
|
2019-06-30 06:51:18 -05:00
|
|
|
/// that can later be called to execute the right thing.
|
|
|
|
type ExtraFnVal: ::std::fmt::Debug + Copy;
|
|
|
|
|
2018-11-15 10:14:53 -06:00
|
|
|
/// Extra data stored in every call frame.
|
|
|
|
type FrameExtra;
|
|
|
|
|
2019-02-08 07:53:55 -06:00
|
|
|
/// Extra data stored in memory. A reference to this is available when `AllocExtra`
|
2018-11-26 20:59:49 -06:00
|
|
|
/// gets initialized, so you can e.g., have an `Rc` here if there is global state you
|
2018-11-14 09:00:52 -06:00
|
|
|
/// need access to in the `AllocExtra` hooks.
|
2019-06-26 13:56:33 -05:00
|
|
|
type MemoryExtra;
|
2018-11-14 09:00:52 -06:00
|
|
|
|
2018-10-16 02:15:13 -05:00
|
|
|
/// Extra data stored in every allocation.
|
2019-04-15 03:05:13 -05:00
|
|
|
type AllocExtra: AllocationExtra<Self::PointerTag> + 'static;
|
2018-08-26 05:59:59 -05:00
|
|
|
|
2018-10-05 08:13:59 -05:00
|
|
|
/// Memory's allocation map
|
|
|
|
type MemoryMap:
|
2018-10-16 02:15:13 -05:00
|
|
|
AllocMap<
|
|
|
|
AllocId,
|
|
|
|
(MemoryKind<Self::MemoryKinds>, Allocation<Self::PointerTag, Self::AllocExtra>)
|
|
|
|
> +
|
2018-10-05 08:13:59 -05:00
|
|
|
Default +
|
|
|
|
Clone;
|
|
|
|
|
2018-11-20 04:11:09 -06:00
|
|
|
/// The memory kind to use for copied statics -- or None if statics should not be mutated
|
2018-11-20 05:29:05 -06:00
|
|
|
/// and thus any such attempt will cause a `ModifiedStatic` error to be raised.
|
2018-09-21 16:32:59 -05:00
|
|
|
/// Statics are copied under two circumstances: When they are mutated, and when
|
2019-05-28 03:44:46 -05:00
|
|
|
/// `tag_allocation` or `find_foreign_static` (see below) returns an owned allocation
|
2018-09-21 16:32:59 -05:00
|
|
|
/// that is added to the memory so that the work is not done twice.
|
|
|
|
const STATIC_KIND: Option<Self::MemoryKinds>;
|
2017-07-28 09:48:43 -05:00
|
|
|
|
2019-07-28 06:44:11 -05:00
|
|
|
/// Whether memory accesses should be alignment-checked.
|
|
|
|
const CHECK_ALIGN: bool;
|
|
|
|
|
2018-10-02 13:20:14 -05:00
|
|
|
/// Whether to enforce the validity invariant
|
2019-06-27 04:36:01 -05:00
|
|
|
fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool;
|
2018-10-02 13:20:14 -05:00
|
|
|
|
2018-09-20 03:12:21 -05:00
|
|
|
/// Called before a basic block terminator is executed.
|
|
|
|
/// You can use this to detect endlessly running programs.
|
2019-06-27 04:36:01 -05:00
|
|
|
fn before_terminator(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx>;
|
2018-09-09 03:06:39 -05:00
|
|
|
|
2017-07-28 02:52:19 -05:00
|
|
|
/// Entry point to all function calls.
|
|
|
|
///
|
2018-08-23 12:04:33 -05:00
|
|
|
/// Returns either the mir to use for the call, or `None` if execution should
|
|
|
|
/// just proceed (which usually means this hook did all the work that the
|
2019-02-08 07:53:55 -06:00
|
|
|
/// called function should usually have done). In the latter case, it is
|
2019-11-25 09:23:44 -06:00
|
|
|
/// this hook's responsibility to advance the instruction pointer!
|
2018-08-23 12:04:33 -05:00
|
|
|
/// (This is to support functions like `__rust_maybe_catch_panic` that neither find a MIR
|
|
|
|
/// nor just jump to `ret`, but instead push their own stack frame.)
|
|
|
|
/// Passing `dest`and `ret` in the same `Option` proved very annoying when only one of them
|
|
|
|
/// was used.
|
2018-09-20 03:12:21 -05:00
|
|
|
fn find_fn(
|
2019-06-27 04:36:01 -05:00
|
|
|
ecx: &mut InterpCx<'mir, 'tcx, Self>,
|
2017-07-21 10:25:30 -05:00
|
|
|
instance: ty::Instance<'tcx>,
|
2018-09-21 16:32:59 -05:00
|
|
|
args: &[OpTy<'tcx, Self::PointerTag>],
|
2019-11-25 15:00:58 -06:00
|
|
|
ret: Option<(PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>,
|
|
|
|
unwind: Option<mir::BasicBlock>,
|
2019-06-07 11:56:27 -05:00
|
|
|
) -> InterpResult<'tcx, Option<&'mir mir::Body<'tcx>>>;
|
2017-07-25 04:32:48 -05:00
|
|
|
|
2019-11-25 09:23:44 -06:00
|
|
|
/// Execute `fn_val`. It is the hook's responsibility to advance the instruction
|
2019-06-30 08:06:13 -05:00
|
|
|
/// pointer as appropriate.
|
|
|
|
fn call_extra_fn(
|
2019-07-01 04:26:28 -05:00
|
|
|
ecx: &mut InterpCx<'mir, 'tcx, Self>,
|
2019-06-30 08:06:13 -05:00
|
|
|
fn_val: Self::ExtraFnVal,
|
|
|
|
args: &[OpTy<'tcx, Self::PointerTag>],
|
2019-11-25 15:00:58 -06:00
|
|
|
ret: Option<(PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>,
|
|
|
|
unwind: Option<mir::BasicBlock>,
|
2019-06-30 08:06:13 -05:00
|
|
|
) -> InterpResult<'tcx>;
|
|
|
|
|
2019-11-25 09:23:44 -06:00
|
|
|
/// Directly process an intrinsic without pushing a stack frame. It is the hook's
|
|
|
|
/// responsibility to advance the instruction pointer as appropriate.
|
2018-09-20 03:12:21 -05:00
|
|
|
fn call_intrinsic(
|
2019-06-27 04:36:01 -05:00
|
|
|
ecx: &mut InterpCx<'mir, 'tcx, Self>,
|
2019-10-24 10:03:57 -05:00
|
|
|
span: Span,
|
2017-07-28 06:08:27 -05:00
|
|
|
instance: ty::Instance<'tcx>,
|
2018-09-21 16:32:59 -05:00
|
|
|
args: &[OpTy<'tcx, Self::PointerTag>],
|
2019-11-25 15:00:58 -06:00
|
|
|
ret: Option<(PlaceTy<'tcx, Self::PointerTag>, mir::BasicBlock)>,
|
2019-11-04 23:06:05 -06:00
|
|
|
unwind: Option<mir::BasicBlock>,
|
2019-06-07 11:56:27 -05:00
|
|
|
) -> InterpResult<'tcx>;
|
2017-07-28 06:08:27 -05:00
|
|
|
|
2018-08-23 14:22:27 -05:00
|
|
|
/// Called for read access to a foreign static item.
|
2018-09-21 16:32:59 -05:00
|
|
|
///
|
|
|
|
/// This will only be called once per static and machine; the result is cached in
|
2018-10-05 09:49:51 -05:00
|
|
|
/// the machine memory. (This relies on `AllocMap::get_or` being able to add the
|
|
|
|
/// owned allocation to the map even when the map is shared.)
|
2019-05-28 03:44:46 -05:00
|
|
|
///
|
|
|
|
/// This allocation will then be fed to `tag_allocation` to initialize the "extra" state.
|
2018-09-20 03:12:21 -05:00
|
|
|
fn find_foreign_static(
|
2019-07-02 05:19:58 -05:00
|
|
|
tcx: TyCtxt<'tcx>,
|
2018-08-23 14:22:27 -05:00
|
|
|
def_id: DefId,
|
2019-06-07 11:56:27 -05:00
|
|
|
) -> InterpResult<'tcx, Cow<'tcx, Allocation>>;
|
2018-09-21 16:32:59 -05:00
|
|
|
|
2019-07-24 09:08:50 -05:00
|
|
|
/// Called for all binary operations where the LHS has pointer type.
|
2017-07-25 04:32:48 -05:00
|
|
|
///
|
2017-08-01 04:11:57 -05:00
|
|
|
/// Returns a (value, overflowed) pair if the operation succeeded
|
2019-07-24 09:08:50 -05:00
|
|
|
fn binary_ptr_op(
|
2019-06-27 04:36:01 -05:00
|
|
|
ecx: &InterpCx<'mir, 'tcx, Self>,
|
2017-07-25 04:32:48 -05:00
|
|
|
bin_op: mir::BinOp,
|
2019-02-08 07:00:52 -06:00
|
|
|
left: ImmTy<'tcx, Self::PointerTag>,
|
|
|
|
right: ImmTy<'tcx, Self::PointerTag>,
|
2019-08-10 12:40:56 -05:00
|
|
|
) -> InterpResult<'tcx, (Scalar<Self::PointerTag>, bool, Ty<'tcx>)>;
|
2017-07-28 09:48:43 -05:00
|
|
|
|
2018-10-16 07:50:07 -05:00
|
|
|
/// Heap allocations via the `box` keyword.
|
2018-09-20 03:12:21 -05:00
|
|
|
fn box_alloc(
|
2019-06-27 04:36:01 -05:00
|
|
|
ecx: &mut InterpCx<'mir, 'tcx, Self>,
|
2018-09-21 16:32:59 -05:00
|
|
|
dest: PlaceTy<'tcx, Self::PointerTag>,
|
2019-06-07 11:56:27 -05:00
|
|
|
) -> InterpResult<'tcx>;
|
2017-09-15 06:02:33 -05:00
|
|
|
|
2019-09-24 20:12:59 -05:00
|
|
|
/// Called to read the specified `local` from the `frame`.
|
|
|
|
fn access_local(
|
|
|
|
_ecx: &InterpCx<'mir, 'tcx, Self>,
|
|
|
|
frame: &Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>,
|
|
|
|
local: mir::Local,
|
|
|
|
) -> InterpResult<'tcx, Operand<Self::PointerTag>> {
|
|
|
|
frame.locals[local].access()
|
|
|
|
}
|
|
|
|
|
2019-09-24 21:08:22 -05:00
|
|
|
/// Called before a `StaticKind::Static` value is accessed.
|
|
|
|
fn before_access_static(
|
|
|
|
_allocation: &Allocation,
|
2019-09-24 20:12:59 -05:00
|
|
|
) -> InterpResult<'tcx> {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-05-28 03:44:46 -05:00
|
|
|
/// Called to initialize the "extra" state of an allocation and make the pointers
|
|
|
|
/// it contains (in relocations) tagged. The way we construct allocations is
|
|
|
|
/// to always first construct it without extra and then add the extra.
|
|
|
|
/// This keeps uniform code paths for handling both allocations created by CTFE
|
|
|
|
/// for statics, and allocations ceated by Miri during evaluation.
|
|
|
|
///
|
|
|
|
/// `kind` is the kind of the allocation being tagged; it can be `None` when
|
|
|
|
/// it's a static and `STATIC_KIND` is `None`.
|
2019-04-15 03:05:13 -05:00
|
|
|
///
|
|
|
|
/// This should avoid copying if no work has to be done! If this returns an owned
|
|
|
|
/// allocation (because a copy had to be done to add tags or metadata), machine memory will
|
|
|
|
/// cache the result. (This relies on `AllocMap::get_or` being able to add the
|
|
|
|
/// owned allocation to the map even when the map is shared.)
|
2019-05-28 03:44:46 -05:00
|
|
|
///
|
2019-05-28 12:08:14 -05:00
|
|
|
/// For static allocations, the tag returned must be the same as the one returned by
|
|
|
|
/// `tag_static_base_pointer`.
|
2019-05-28 03:44:46 -05:00
|
|
|
fn tag_allocation<'b>(
|
2019-07-02 05:31:17 -05:00
|
|
|
memory_extra: &Self::MemoryExtra,
|
2019-05-28 03:44:46 -05:00
|
|
|
id: AllocId,
|
|
|
|
alloc: Cow<'b, Allocation>,
|
|
|
|
kind: Option<MemoryKind<Self::MemoryKinds>>,
|
|
|
|
) -> (Cow<'b, Allocation<Self::PointerTag, Self::AllocExtra>>, Self::PointerTag);
|
|
|
|
|
|
|
|
/// Return the "base" tag for the given static allocation: the one that is used for direct
|
|
|
|
/// accesses to this static/const/fn allocation.
|
|
|
|
///
|
|
|
|
/// Be aware that requesting the `Allocation` for that `id` will lead to cycles
|
|
|
|
/// for cyclic statics!
|
|
|
|
fn tag_static_base_pointer(
|
2019-07-02 05:31:17 -05:00
|
|
|
memory_extra: &Self::MemoryExtra,
|
2019-05-28 03:44:46 -05:00
|
|
|
id: AllocId,
|
|
|
|
) -> Self::PointerTag;
|
2018-10-16 10:00:39 -05:00
|
|
|
|
2019-02-08 07:53:55 -06:00
|
|
|
/// Executes a retagging operation
|
2018-10-16 07:50:07 -05:00
|
|
|
#[inline]
|
2018-10-24 04:47:17 -05:00
|
|
|
fn retag(
|
2019-06-27 04:36:01 -05:00
|
|
|
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
|
2018-12-11 12:54:38 -06:00
|
|
|
_kind: mir::RetagKind,
|
2018-10-24 04:47:17 -05:00
|
|
|
_place: PlaceTy<'tcx, Self::PointerTag>,
|
2019-06-07 11:56:27 -05:00
|
|
|
) -> InterpResult<'tcx> {
|
2017-12-14 04:36:28 -06:00
|
|
|
Ok(())
|
|
|
|
}
|
2018-11-06 04:04:10 -06:00
|
|
|
|
2018-11-15 10:14:53 -06:00
|
|
|
/// Called immediately before a new stack frame got pushed
|
2019-06-27 04:36:01 -05:00
|
|
|
fn stack_push(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx, Self::FrameExtra>;
|
2018-11-15 10:14:53 -06:00
|
|
|
|
|
|
|
/// Called immediately after a stack frame gets popped
|
|
|
|
fn stack_pop(
|
2019-10-28 18:09:54 -05:00
|
|
|
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
|
|
|
|
_extra: Self::FrameExtra,
|
2019-10-28 20:37:58 -05:00
|
|
|
_unwinding: bool
|
2019-10-28 18:09:54 -05:00
|
|
|
) -> InterpResult<'tcx, StackPopInfo> {
|
|
|
|
// By default, we do not support unwinding from panics
|
|
|
|
Ok(StackPopInfo::Normal)
|
|
|
|
}
|
2019-06-12 12:49:46 -05:00
|
|
|
|
|
|
|
fn int_to_ptr(
|
2019-06-20 14:07:34 -05:00
|
|
|
_mem: &Memory<'mir, 'tcx, Self>,
|
2019-07-02 05:19:58 -05:00
|
|
|
int: u64,
|
2019-06-12 12:49:46 -05:00
|
|
|
) -> InterpResult<'tcx, Pointer<Self::PointerTag>> {
|
2019-07-02 03:48:51 -05:00
|
|
|
Err((if int == 0 {
|
2019-07-31 02:18:54 -05:00
|
|
|
err_unsup!(InvalidNullPointerUsage)
|
2019-06-16 03:48:40 -05:00
|
|
|
} else {
|
2019-07-31 02:18:54 -05:00
|
|
|
err_unsup!(ReadBytesAsPointer)
|
2019-07-02 03:48:51 -05:00
|
|
|
}).into())
|
2019-06-12 12:49:46 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
fn ptr_to_int(
|
2019-06-20 14:07:34 -05:00
|
|
|
_mem: &Memory<'mir, 'tcx, Self>,
|
2019-07-02 05:19:58 -05:00
|
|
|
_ptr: Pointer<Self::PointerTag>,
|
2019-07-24 17:06:47 -05:00
|
|
|
) -> InterpResult<'tcx, u64>;
|
2017-07-21 10:25:30 -05:00
|
|
|
}
|