use std::ffi::OsStr;
use std::{iter, mem};
+use std::convert::TryFrom;
-use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX};
use rustc::mir;
use rustc::ty::{
self,
layout::{self, LayoutOf, Size, TyLayout},
List, TyCtxt,
};
+use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX};
use rustc_span::source_map::DUMMY_SP;
use rand::RngCore;
impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
/// Gets an instance for a path.
-fn resolve_did<'mir, 'tcx>(tcx: TyCtxt<'tcx>, path: &[&str]) -> InterpResult<'tcx, DefId> {
+fn try_resolve_did<'mir, 'tcx>(tcx: TyCtxt<'tcx>, path: &[&str]) -> Option<DefId> {
tcx.crates()
.iter()
.find(|&&krate| tcx.original_crate_name(krate).as_str() == path[0])
}
None
})
- .ok_or_else(|| {
- let path = path.iter().map(|&s| s.to_owned()).collect();
- err_unsup!(PathNotFound(path)).into()
- })
}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
/// Gets an instance for a path.
- fn resolve_path(&self, path: &[&str]) -> InterpResult<'tcx, ty::Instance<'tcx>> {
- Ok(ty::Instance::mono(
- self.eval_context_ref().tcx.tcx,
- resolve_did(self.eval_context_ref().tcx.tcx, path)?,
- ))
+ fn resolve_path(&self, path: &[&str]) -> ty::Instance<'tcx> {
+ let did = try_resolve_did(self.eval_context_ref().tcx.tcx, path)
+ .unwrap_or_else(|| panic!("failed to find required Rust item: {:?}", path));
+ ty::Instance::mono(self.eval_context_ref().tcx.tcx, did)
+ }
+
+ /// Evaluates the scalar at the specified path. Returns Some(val)
+ /// if the path could be resolved, and None otherwise
+ fn eval_path_scalar(
+ &mut self,
+ path: &[&str],
+ ) -> InterpResult<'tcx, ScalarMaybeUndef<Tag>> {
+ let this = self.eval_context_mut();
+ let instance = this.resolve_path(path);
+ let cid = GlobalId { instance, promoted: None };
+ let const_val = this.const_eval_raw(cid)?;
+ let const_val = this.read_scalar(const_val.into())?;
+ return Ok(const_val);
+ }
+
+ /// Helper function to get a `libc` constant as a `Scalar`.
+ fn eval_libc(&mut self, name: &str) -> InterpResult<'tcx, Scalar<Tag>> {
+ self.eval_context_mut()
+ .eval_path_scalar(&["libc", name])?
+ .not_undef()
+ }
+
+ /// Helper function to get a `libc` constant as an `i32`.
+ fn eval_libc_i32(&mut self, name: &str) -> InterpResult<'tcx, i32> {
+ self.eval_libc(name)?.to_i32()
+ }
+
+ /// Helper function to get the `TyLayout` of a `libc` type
+ fn libc_ty_layout(&mut self, name: &str) -> InterpResult<'tcx, TyLayout<'tcx>> {
+ let this = self.eval_context_mut();
+ let ty = this.resolve_path(&["libc", name]).monomorphic_ty(*this.tcx);
+ this.layout_of(ty)
}
/// Write a 0 of the appropriate size to `dest`.
}
/// Generate some random bytes, and write them to `dest`.
- fn gen_random(&mut self, ptr: Scalar<Tag>, len: usize) -> InterpResult<'tcx> {
+ fn gen_random(&mut self, ptr: Scalar<Tag>, len: u64) -> InterpResult<'tcx> {
// Some programs pass in a null pointer and a length of 0
// to their platform's random-generation function (e.g. getrandom())
// on Linux. For compatibility with these programs, we don't perform
}
let this = self.eval_context_mut();
- let mut data = vec![0; len];
+ let mut data = vec![0; usize::try_from(len).unwrap()];
if this.machine.communicate {
// Fill the buffer using the host's rng.
getrandom::getrandom(&mut data)
- .map_err(|err| err_unsup_format!("getrandom failed: {}", err))?;
+ .map_err(|err| err_unsup_format!("host getrandom failed: {}", err))?;
} else {
let rng = this.memory.extra.rng.get_mut();
rng.fill_bytes(&mut data);
}
// We have to do *something* for unions.
- fn visit_union(&mut self, v: MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx> {
+ fn visit_union(&mut self, v: MPlaceTy<'tcx, Tag>, fields: usize) -> InterpResult<'tcx> {
+ assert!(fields > 0); // we should never reach "pseudo-unions" with 0 fields, like primitives
+
// With unions, we fall back to whatever the type says, to hopefully be consistent
// with LLVM IR.
// FIXME: are we consistent, and is this really the behavior we want?
let frozen = self.ecx.type_is_freeze(v.layout.ty);
if frozen { Ok(()) } else { (self.unsafe_cell_action)(v) }
}
-
- // We should never get to a primitive, but always short-circuit somewhere above.
- fn visit_primitive(&mut self, _v: MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx> {
- bug!("we should always short-circuit before coming to a primitive")
- }
}
}
- /// Helper function to get a `libc` constant as a `Scalar`.
- fn eval_libc(&mut self, name: &str) -> InterpResult<'tcx, Scalar<Tag>> {
- self.eval_context_mut()
- .eval_path_scalar(&["libc", name])?
- .ok_or_else(|| err_unsup_format!("Path libc::{} cannot be resolved.", name))?
- .not_undef()
- }
-
- /// Helper function to get a `libc` constant as an `i32`.
- fn eval_libc_i32(&mut self, name: &str) -> InterpResult<'tcx, i32> {
- self.eval_libc(name)?.to_i32()
- }
-
- /// Helper function to get the `TyLayout` of a `libc` type
- fn libc_ty_layout(&mut self, name: &str) -> InterpResult<'tcx, TyLayout<'tcx>> {
- let this = self.eval_context_mut();
- let ty = this.resolve_path(&["libc", name])?.monomorphic_ty(*this.tcx);
- this.layout_of(ty)
- }
-
// Writes several `ImmTy`s contiguosly into memory. This is useful when you have to pack
// different values into a struct.
fn write_packed_immediates(
fn check_no_isolation(&self, name: &str) -> InterpResult<'tcx> {
if !self.eval_context_ref().machine.communicate {
throw_unsup_format!(
- "`{}` not available when isolation is enabled. Pass the flag `-Zmiri-disable-isolation` to disable it.",
+ "`{}` not available when isolation is enabled (pass the flag `-Zmiri-disable-isolation` to disable isolation)",
name,
)
}
Ok(())
}
- /// Helper function used inside the shims of foreign functions to assert that the target
- /// platform is `platform`. It panics showing a message with the `name` of the foreign function
+ /// Helper function used inside the shims of foreign functions to assert that the target OS
+ /// is `target_os`. It panics showing a message with the `name` of the foreign function
/// if this is not the case.
- fn assert_platform(&self, platform: &str, name: &str) {
+ fn assert_target_os(&self, target_os: &str, name: &str) {
assert_eq!(
self.eval_context_ref().tcx.sess.target.target.target_os,
- platform,
- "`{}` is only available on the `{}` platform",
+ target_os,
+ "`{}` is only available on the `{}` target OS",
name,
- platform,
+ target_os,
)
}
AlreadyExists => "EEXIST",
WouldBlock => "EWOULDBLOCK",
_ => {
- throw_unsup_format!("The {} error cannot be transformed into a raw os error", e)
+ throw_unsup_format!("io error {} cannot be transformed into a raw os error", e)
}
})?
} else {
// FIXME: we have to implement the Windows equivalent of this.
throw_unsup_format!(
- "Setting the last OS error from an io::Error is unsupported for {}.",
+ "setting the last OS error from an io::Error is unsupported for {}.",
target.target_os
)
};
}
/// Helper function to write an OsStr as a null-terminated sequence of bytes, which is what
- /// the Unix APIs usually handle. This function returns `Ok(false)` without trying to write if
- /// `size` is not large enough to fit the contents of `os_string` plus a null terminator. It
- /// returns `Ok(true)` if the writing process was successful.
+ /// the Unix APIs usually handle. This function returns `Ok((false, length))` without trying
+ /// to write if `size` is not large enough to fit the contents of `os_string` plus a null
+ /// terminator. It returns `Ok((true, length))` if the writing process was successful. The
+ /// string length returned does not include the null terminator.
fn write_os_str_to_c_str(
&mut self,
os_str: &OsStr,
scalar: Scalar<Tag>,
size: u64,
- ) -> InterpResult<'tcx, bool> {
+ ) -> InterpResult<'tcx, (bool, u64)> {
#[cfg(target_os = "unix")]
fn os_str_to_bytes<'tcx, 'a>(os_str: &'a OsStr) -> InterpResult<'tcx, &'a [u8]> {
std::os::unix::ffi::OsStringExt::into_bytes(os_str)
let bytes = os_str_to_bytes(os_str)?;
// If `size` is smaller or equal than `bytes.len()`, writing `bytes` plus the required null
// terminator to memory using the `ptr` pointer would cause an out-of-bounds access.
- if size <= bytes.len() as u64 {
- return Ok(false);
+ let string_length = u64::try_from(bytes.len()).unwrap();
+ if size <= string_length {
+ return Ok((false, string_length));
}
self.eval_context_mut()
.memory
.write_bytes(scalar, bytes.iter().copied().chain(iter::once(0u8)))?;
- Ok(true)
+ Ok((true, string_length))
}
fn alloc_os_str_as_c_str(
&mut self,
os_str: &OsStr,
- memkind: MemoryKind<MiriMemoryKind>
+ memkind: MemoryKind<MiriMemoryKind>,
) -> Pointer<Tag> {
- let size = os_str.len() as u64 + 1; // Make space for `0` terminator.
+ let size = u64::try_from(os_str.len()).unwrap().checked_add(1).unwrap(); // Make space for `0` terminator.
let this = self.eval_context_mut();
let arg_type = this.tcx.mk_array(this.tcx.types.u8, size);
layout: TyLayout<'tcx>,
) -> InterpResult<'tcx, ImmTy<'tcx, Tag>> {
let int = int.into();
- Ok(ImmTy::try_from_int(int, layout).ok_or_else(||
- err_unsup_format!("Signed value {:#x} does not fit in {} bits", int, layout.size.bits())
- )?)
+ Ok(ImmTy::try_from_int(int, layout).ok_or_else(|| {
+ err_unsup_format!("signed value {:#x} does not fit in {} bits", int, layout.size.bits())
+ })?)
}
pub fn immty_from_uint_checked<'tcx>(
layout: TyLayout<'tcx>,
) -> InterpResult<'tcx, ImmTy<'tcx, Tag>> {
let int = int.into();
- Ok(ImmTy::try_from_uint(int, layout).ok_or_else(||
- err_unsup_format!("Signed value {:#x} does not fit in {} bits", int, layout.size.bits())
- )?)
+ Ok(ImmTy::try_from_uint(int, layout).ok_or_else(|| {
+ err_unsup_format!("unsigned value {:#x} does not fit in {} bits", int, layout.size.bits())
+ })?)
}