]> git.lizzy.rs Git - rust.git/blobdiff - src/machine.rs
Fix merge conflicts
[rust.git] / src / machine.rs
index 9785d6a45560cc62d7c5a398553d5cc47e8eed72..7904e1cc123b359fbc08603f7325c5abbad80b5d 100644 (file)
@@ -1,20 +1,30 @@
 //! Global machine state as well as implementation of the interpreter engine
 //! `Machine` trait.
 
-use std::rc::Rc;
 use std::borrow::Cow;
-use std::collections::HashMap;
+use std::cell::RefCell;
+use std::rc::Rc;
 
 use rand::rngs::StdRng;
 
-use syntax::attr;
-use syntax::symbol::sym;
 use rustc::hir::def_id::DefId;
-use rustc::ty::{self, layout::{Size, LayoutOf}, query::TyCtxtAt};
 use rustc::mir;
+use rustc::ty::{
+    self,
+    layout::{LayoutOf, Size},
+    Ty, TyCtxt,
+};
+use syntax::attr;
+use syntax::symbol::sym;
 
 use crate::*;
 
+// Some global facts about the emulated machine.
+pub const PAGE_SIZE: u64 = 4 * 1024; // FIXME: adjust to target architecture
+pub const STACK_ADDR: u64 = 32 * PAGE_SIZE; // not really about the "stack", but where we start assigning integer addresses to allocations
+pub const STACK_SIZE: u64 = 16 * PAGE_SIZE; // whatever
+pub const NUM_CPUS: u64 = 1;
+
 /// Extra memory kinds
 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
 pub enum MiriMemoryKind {
@@ -22,6 +32,8 @@ pub enum MiriMemoryKind {
     Rust,
     /// `malloc` memory.
     C,
+    /// Windows `HeapAlloc` memory.
+    WinHeap,
     /// Part of env var emulation.
     Env,
     /// Statics.
@@ -38,8 +50,8 @@ fn into(self) -> MemoryKind<MiriMemoryKind> {
 /// Extra per-allocation data
 #[derive(Debug, Clone)]
 pub struct AllocExtra {
-    pub stacked_borrows: stacked_borrows::AllocExtra,
-    pub intptrcast: intptrcast::AllocExtra,
+    /// Stacked Borrows state is only added if validation is enabled.
+    pub stacked_borrows: Option<stacked_borrows::AllocExtra>,
 }
 
 /// Extra global memory data
@@ -47,17 +59,21 @@ pub struct AllocExtra {
 pub struct MemoryExtra {
     pub stacked_borrows: stacked_borrows::MemoryExtra,
     pub intptrcast: intptrcast::MemoryExtra,
-    /// The random number generator to use if Miri is running in non-deterministic mode and to
-    /// enable intptrcast
-    pub(crate) rng: Option<StdRng>
+
+    /// The random number generator used for resolving non-determinism.
+    pub(crate) rng: RefCell<StdRng>,
+
+    /// Whether to enforce the validity invariant.
+    pub(crate) validate: bool,
 }
 
 impl MemoryExtra {
-    pub fn with_rng(rng: Option<StdRng>) -> Self {
+    pub fn new(rng: StdRng, validate: bool) -> Self {
         MemoryExtra {
             stacked_borrows: Default::default(),
             intptrcast: Default::default(),
-            rng,
+            rng: RefCell::new(rng),
+            validate,
         }
     }
 }
@@ -66,7 +82,7 @@ pub fn with_rng(rng: Option<StdRng>) -> Self {
 pub struct Evaluator<'tcx> {
     /// Environment variables set by `setenv`.
     /// Miri does not expose env vars from the host to the emulated program.
-    pub(crate) env_vars: HashMap<Vec<u8>, Pointer<Tag>>,
+    pub(crate) env_vars: EnvVars,
 
     /// Program arguments (`Option` because we can only initialize them after creating the ecx).
     /// These are *pointers* to argc/argv because macOS.
@@ -75,32 +91,38 @@ pub struct Evaluator<'tcx> {
     pub(crate) argv: Option<Pointer<Tag>>,
     pub(crate) cmd_line: Option<Pointer<Tag>>,
 
-    /// Last OS error.
-    pub(crate) last_error: u32,
+    /// Last OS error location in memory. It is a 32-bit integer.
+    pub(crate) last_error: Option<MPlaceTy<'tcx, Tag>>,
 
     /// TLS state.
     pub(crate) tls: TlsData<'tcx>,
 
-    /// Whether to enforce the validity invariant.
-    pub(crate) validate: bool,
+    /// If enabled, the `env_vars` field is populated with the host env vars during initialization
+    /// and random number generation is delegated to the host.
+    pub(crate) communicate: bool,
+
+    pub(crate) file_handler: FileHandler,
 }
 
 impl<'tcx> Evaluator<'tcx> {
-    pub(crate) fn new(validate: bool) -> Self {
+    pub(crate) fn new(communicate: bool) -> Self {
         Evaluator {
-            env_vars: HashMap::default(),
+            // `env_vars` could be initialized properly here if `Memory` were available before
+            // calling this method.
+            env_vars: EnvVars::default(),
             argc: None,
             argv: None,
             cmd_line: None,
-            last_error: 0,
+            last_error: None,
             tls: TlsData::default(),
-            validate,
+            communicate,
+            file_handler: Default::default(),
         }
     }
 }
 
-/// A rustc InterpretCx for Miri.
-pub type MiriEvalContext<'mir, 'tcx> = InterpretCx<'mir, 'tcx, Evaluator<'tcx>>;
+/// A rustc InterpCx for Miri.
+pub type MiriEvalContext<'mir, 'tcx> = InterpCx<'mir, 'tcx, Evaluator<'tcx>>;
 
 /// A little trait that's useful to be inherited by extension traits.
 pub trait MiriEvalContextExt<'mir, 'tcx> {
@@ -126,20 +148,28 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'tcx> {
     type MemoryExtra = MemoryExtra;
     type AllocExtra = AllocExtra;
     type PointerTag = Tag;
+    type ExtraFnVal = Dlsym;
 
-    type MemoryMap = MonoHashMap<AllocId, (MemoryKind<MiriMemoryKind>, Allocation<Tag, Self::AllocExtra>)>;
+    type MemoryMap = MonoHashMap<
+        AllocId,
+        (
+            MemoryKind<MiriMemoryKind>,
+            Allocation<Tag, Self::AllocExtra>,
+        ),
+    >;
 
     const STATIC_KIND: Option<MiriMemoryKind> = Some(MiriMemoryKind::Static);
 
+    const CHECK_ALIGN: bool = true;
+
     #[inline(always)]
-    fn enforce_validity(ecx: &InterpretCx<'mir, 'tcx, Self>) -> bool {
-        ecx.machine.validate
+    fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
+        ecx.memory.extra.validate
     }
 
-    /// Returns `Ok()` when the function was handled; fail otherwise.
     #[inline(always)]
     fn find_fn(
-        ecx: &mut InterpretCx<'mir, 'tcx, Self>,
+        ecx: &mut InterpCx<'mir, 'tcx, Self>,
         instance: ty::Instance<'tcx>,
         args: &[OpTy<'tcx, Tag>],
         dest: Option<PlaceTy<'tcx, Tag>>,
@@ -148,9 +178,20 @@ fn find_fn(
         ecx.find_fn(instance, args, dest, ret)
     }
 
+    #[inline(always)]
+    fn call_extra_fn(
+        ecx: &mut InterpCx<'mir, 'tcx, Self>,
+        fn_val: Dlsym,
+        args: &[OpTy<'tcx, Tag>],
+        dest: Option<PlaceTy<'tcx, Tag>>,
+        ret: Option<mir::BasicBlock>,
+    ) -> InterpResult<'tcx> {
+        ecx.call_dlsym(fn_val, args, dest, ret)
+    }
+
     #[inline(always)]
     fn call_intrinsic(
-        ecx: &mut rustc_mir::interpret::InterpretCx<'mir, 'tcx, Self>,
+        ecx: &mut rustc_mir::interpret::InterpCx<'mir, 'tcx, Self>,
         instance: ty::Instance<'tcx>,
         args: &[OpTy<'tcx, Tag>],
         dest: PlaceTy<'tcx, Tag>,
@@ -159,24 +200,24 @@ fn call_intrinsic(
     }
 
     #[inline(always)]
-    fn ptr_op(
-        ecx: &rustc_mir::interpret::InterpretCx<'mir, 'tcx, Self>,
+    fn binary_ptr_op(
+        ecx: &rustc_mir::interpret::InterpCx<'mir, 'tcx, Self>,
         bin_op: mir::BinOp,
         left: ImmTy<'tcx, Tag>,
         right: ImmTy<'tcx, Tag>,
-    ) -> InterpResult<'tcx, (Scalar<Tag>, bool)> {
-        ecx.ptr_op(bin_op, left, right)
+    ) -> InterpResult<'tcx, (Scalar<Tag>, bool, Ty<'tcx>)> {
+        ecx.binary_ptr_op(bin_op, left, right)
     }
 
     fn box_alloc(
-        ecx: &mut InterpretCx<'mir, 'tcx, Self>,
+        ecx: &mut InterpCx<'mir, 'tcx, Self>,
         dest: PlaceTy<'tcx, Tag>,
     ) -> InterpResult<'tcx> {
         trace!("box_alloc for {:?}", dest.layout.ty);
         // Call the `exchange_malloc` lang item.
         let malloc = ecx.tcx.lang_items().exchange_malloc_fn().unwrap();
         let malloc = ty::Instance::mono(ecx.tcx.tcx, malloc);
-        let malloc_mir = ecx.load_mir(malloc.def)?;
+        let malloc_mir = ecx.load_mir(malloc.def, None)?;
         ecx.push_stack_frame(
             malloc,
             malloc_mir.span,
@@ -193,26 +234,23 @@ fn box_alloc(
 
         // First argument: `size`.
         // (`0` is allowed here -- this is expected to be handled by the lang item).
-        let arg = ecx.eval_place(&mir::Place::Base(mir::PlaceBase::Local(args.next().unwrap())))?;
+        let arg = ecx.local_place(args.next().unwrap())?;
         let size = layout.size.bytes();
         ecx.write_scalar(Scalar::from_uint(size, arg.layout.size), arg)?;
 
         // Second argument: `align`.
-        let arg = ecx.eval_place(&mir::Place::Base(mir::PlaceBase::Local(args.next().unwrap())))?;
+        let arg = ecx.local_place(args.next().unwrap())?;
         let align = layout.align.abi.bytes();
         ecx.write_scalar(Scalar::from_uint(align, arg.layout.size), arg)?;
 
         // No more arguments.
-        assert!(
-            args.next().is_none(),
-            "`exchange_malloc` lang item has more arguments than expected"
-        );
+        args.next().expect_none("`exchange_malloc` lang item has more arguments than expected");
         Ok(())
     }
 
     fn find_foreign_static(
+        tcx: TyCtxt<'tcx>,
         def_id: DefId,
-        tcx: TyCtxtAt<'tcx>,
     ) -> InterpResult<'tcx, Cow<'tcx, Allocation>> {
         let attrs = tcx.get_attrs(def_id);
         let link_name = match attr::first_attr_value_str_by_name(&attrs, sym::link_name) {
@@ -220,87 +258,85 @@ fn find_foreign_static(
             None => tcx.item_name(def_id).as_str(),
         };
 
-        let alloc = match link_name.get() {
+        let alloc = match &*link_name {
             "__cxa_thread_atexit_impl" => {
                 // This should be all-zero, pointer-sized.
                 let size = tcx.data_layout.pointer_size;
                 let data = vec![0; size.bytes() as usize];
                 Allocation::from_bytes(&data, tcx.data_layout.pointer_align.abi)
             }
-            _ => return err!(Unimplemented(
-                    format!("can't access foreign static: {}", link_name),
-                )),
+            _ => throw_unsup_format!("can't access foreign static: {}", link_name),
         };
         Ok(Cow::Owned(alloc))
     }
 
     #[inline(always)]
-    fn before_terminator(_ecx: &mut InterpretCx<'mir, 'tcx, Self>) -> InterpResult<'tcx>
-    {
+    fn before_terminator(_ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
         // We are not interested in detecting loops.
         Ok(())
     }
 
     fn tag_allocation<'b>(
+        memory_extra: &MemoryExtra,
         id: AllocId,
         alloc: Cow<'b, Allocation>,
         kind: Option<MemoryKind<Self::MemoryKinds>>,
-        memory: &Memory<'mir, 'tcx, Self>,
-    ) -> (Cow<'b, Allocation<Self::PointerTag, Self::AllocExtra>>, Self::PointerTag) {
+    ) -> (
+        Cow<'b, Allocation<Self::PointerTag, Self::AllocExtra>>,
+        Self::PointerTag,
+    ) {
         let kind = kind.expect("we set our STATIC_KIND so this cannot be None");
         let alloc = alloc.into_owned();
-        let (stacks, base_tag) = Stacks::new_allocation(
-            id,
-            Size::from_bytes(alloc.bytes.len() as u64),
-            Rc::clone(&memory.extra.stacked_borrows),
-            kind,
-        );
-        if kind != MiriMemoryKind::Static.into() {
-            assert!(alloc.relocations.is_empty(), "Only statics can come initialized with inner pointers");
-            // Now we can rely on the inner pointers being static, too.
-        }
-        let mut memory_extra = memory.extra.stacked_borrows.borrow_mut();
-        let alloc: Allocation<Tag, Self::AllocExtra> = Allocation {
-            bytes: alloc.bytes,
-            relocations: Relocations::from_presorted(
-                alloc.relocations.iter()
-                    // The allocations in the relocations (pointers stored *inside* this allocation)
-                    // all get the base pointer tag.
-                    .map(|&(offset, ((), alloc))| (offset, (memory_extra.static_base_ptr(alloc), alloc)))
-                    .collect()
-            ),
-            undef_mask: alloc.undef_mask,
-            align: alloc.align,
-            mutability: alloc.mutability,
-            extra: AllocExtra {
+        let (stacks, base_tag) = if !memory_extra.validate {
+            (None, Tag::Untagged)
+        } else {
+            let (stacks, base_tag) = Stacks::new_allocation(
+                id,
+                alloc.size,
+                Rc::clone(&memory_extra.stacked_borrows),
+                kind,
+            );
+            (Some(stacks), base_tag)
+        };
+        let mut stacked_borrows = memory_extra.stacked_borrows.borrow_mut();
+        let alloc: Allocation<Tag, Self::AllocExtra> = alloc.with_tags_and_extra(
+            |alloc| {
+                if !memory_extra.validate {
+                    Tag::Untagged
+                } else {
+                    // Only statics may already contain pointers at this point
+                    assert_eq!(kind, MiriMemoryKind::Static.into());
+                    stacked_borrows.static_base_ptr(alloc)
+                }
+            },
+            AllocExtra {
                 stacked_borrows: stacks,
-                intptrcast: Default::default(),
             },
-        };
+        );
         (Cow::Owned(alloc), base_tag)
     }
 
     #[inline(always)]
-    fn tag_static_base_pointer(
-        id: AllocId,
-        memory: &Memory<'mir, 'tcx, Self>,
-    ) -> Self::PointerTag {
-        memory.extra.stacked_borrows.borrow_mut().static_base_ptr(id)
+    fn tag_static_base_pointer(memory_extra: &MemoryExtra, id: AllocId) -> Self::PointerTag {
+        if !memory_extra.validate {
+            Tag::Untagged
+        } else {
+            memory_extra
+                .stacked_borrows
+                .borrow_mut()
+                .static_base_ptr(id)
+        }
     }
 
     #[inline(always)]
     fn retag(
-        ecx: &mut InterpretCx<'mir, 'tcx, Self>,
+        ecx: &mut InterpCx<'mir, 'tcx, Self>,
         kind: mir::RetagKind,
         place: PlaceTy<'tcx, Tag>,
     ) -> InterpResult<'tcx> {
-        if !ecx.tcx.sess.opts.debugging_opts.mir_emit_retag || !Self::enforce_validity(ecx) {
-            // No tracking, or no retagging. The latter is possible because a dependency of ours
-            // might be called with different flags than we are, so there are `Retag`
-            // statements but we do not want to execute them.
-            // Also, honor the whitelist in `enforce_validity` because otherwise we might retag
-            // uninitialized data.
-             Ok(())
+        if !Self::enforce_validity(ecx) {
+            // No tracking.
+            Ok(())
         } else {
             ecx.retag(kind, place)
         }
@@ -308,41 +344,38 @@ fn retag(
 
     #[inline(always)]
     fn stack_push(
-        ecx: &mut InterpretCx<'mir, 'tcx, Self>,
+        ecx: &mut InterpCx<'mir, 'tcx, Self>,
     ) -> InterpResult<'tcx, stacked_borrows::CallId> {
-        Ok(ecx.memory().extra.stacked_borrows.borrow_mut().new_call())
+        Ok(ecx.memory.extra.stacked_borrows.borrow_mut().new_call())
     }
 
     #[inline(always)]
     fn stack_pop(
-        ecx: &mut InterpretCx<'mir, 'tcx, Self>,
+        ecx: &mut InterpCx<'mir, 'tcx, Self>,
         extra: stacked_borrows::CallId,
     ) -> InterpResult<'tcx> {
-        Ok(ecx.memory().extra.stacked_borrows.borrow_mut().end_call(extra))
+        Ok(ecx
+            .memory
+            .extra
+            .stacked_borrows
+            .borrow_mut()
+            .end_call(extra))
     }
 
+    #[inline(always)]
     fn int_to_ptr(
-        int: u64,
         memory: &Memory<'mir, 'tcx, Self>,
+        int: u64,
     ) -> InterpResult<'tcx, Pointer<Self::PointerTag>> {
-        if int == 0 {
-            err!(InvalidNullPointerUsage)
-        } else if memory.extra.rng.is_none() {
-            err!(ReadBytesAsPointer)
-        } else {
-           intptrcast::GlobalState::int_to_ptr(int, memory)
-        }
+        intptrcast::GlobalState::int_to_ptr(int, memory)
     }
 
+    #[inline(always)]
     fn ptr_to_int(
-        ptr: Pointer<Self::PointerTag>,
         memory: &Memory<'mir, 'tcx, Self>,
+        ptr: Pointer<Self::PointerTag>,
     ) -> InterpResult<'tcx, u64> {
-        if memory.extra.rng.is_none() {
-            err!(ReadPointerAsBytes)
-        } else {
-            intptrcast::GlobalState::ptr_to_int(ptr, memory)
-        }
+        intptrcast::GlobalState::ptr_to_int(ptr, memory)
     }
 }
 
@@ -353,7 +386,11 @@ fn memory_read<'tcx>(
         ptr: Pointer<Tag>,
         size: Size,
     ) -> InterpResult<'tcx> {
-        alloc.extra.stacked_borrows.memory_read(ptr, size)
+        if let Some(ref stacked_borrows) = alloc.extra.stacked_borrows {
+            stacked_borrows.memory_read(ptr, size)
+        } else {
+            Ok(())
+        }
     }
 
     #[inline(always)]
@@ -362,7 +399,11 @@ fn memory_written<'tcx>(
         ptr: Pointer<Tag>,
         size: Size,
     ) -> InterpResult<'tcx> {
-        alloc.extra.stacked_borrows.memory_written(ptr, size)
+        if let Some(ref mut stacked_borrows) = alloc.extra.stacked_borrows {
+            stacked_borrows.memory_written(ptr, size)
+        } else {
+            Ok(())
+        }
     }
 
     #[inline(always)]
@@ -371,7 +412,11 @@ fn memory_deallocated<'tcx>(
         ptr: Pointer<Tag>,
         size: Size,
     ) -> InterpResult<'tcx> {
-        alloc.extra.stacked_borrows.memory_deallocated(ptr, size)
+        if let Some(ref mut stacked_borrows) = alloc.extra.stacked_borrows {
+            stacked_borrows.memory_deallocated(ptr, size)
+        } else {
+            Ok(())
+        }
     }
 }
 
@@ -380,7 +425,7 @@ impl MayLeak for MiriMemoryKind {
     fn may_leak(self) -> bool {
         use self::MiriMemoryKind::*;
         match self {
-            Rust | C => false,
+            Rust | C | WinHeap => false,
             Env | Static => true,
         }
     }