Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initialize MemoryExtra with StdRng #792

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
246 changes: 246 additions & 0 deletions src/eval.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
use rand::rngs::StdRng;
use rand::SeedableRng;

use syntax::source_map::DUMMY_SP;
use rustc::ty::{self, TyCtxt};
use rustc::ty::layout::{LayoutOf, Size, Align};
use rustc::hir::def_id::DefId;
use rustc::mir;

use crate::{
InterpResult, InterpError, InterpretCx, StackPopCleanup, struct_error,
Scalar, Tag, Pointer,
MiriMemoryKind, Evaluator, MemoryExtra, TlsEvalContextExt,
};

/// Configuration needed to spawn a Miri instance.
#[derive(Clone)]
pub struct MiriConfig {
pub validate: bool,
pub args: Vec<String>,

// The seed to use when non-determinism is required (e.g. getrandom())
pub seed: Option<u64>
}

// Used by priroda.
pub fn create_ecx<'mir, 'tcx: 'mir>(
tcx: TyCtxt<'tcx>,
main_id: DefId,
config: MiriConfig,
) -> InterpResult<'tcx, InterpretCx<'mir, 'tcx, Evaluator<'tcx>>> {
let mut ecx = InterpretCx::new(
tcx.at(syntax::source_map::DUMMY_SP),
ty::ParamEnv::reveal_all(),
Evaluator::new(config.validate),
MemoryExtra::with_rng(config.seed.map(StdRng::seed_from_u64)),
);

let main_instance = ty::Instance::mono(ecx.tcx.tcx, main_id);
let main_mir = ecx.load_mir(main_instance.def)?;

if !main_mir.return_ty().is_unit() || main_mir.arg_count != 0 {
return err!(Unimplemented(
"miri does not support main functions without `fn()` type signatures"
.to_owned(),
));
}

let start_id = tcx.lang_items().start_fn().unwrap();
let main_ret_ty = tcx.fn_sig(main_id).output();
let main_ret_ty = main_ret_ty.no_bound_vars().unwrap();
let start_instance = ty::Instance::resolve(
ecx.tcx.tcx,
ty::ParamEnv::reveal_all(),
start_id,
ecx.tcx.mk_substs(
::std::iter::once(ty::subst::Kind::from(main_ret_ty)))
).unwrap();
let start_mir = ecx.load_mir(start_instance.def)?;

if start_mir.arg_count != 3 {
return err!(AbiViolation(format!(
"'start' lang item should have three arguments, but has {}",
start_mir.arg_count
)));
}

// Return value (in static memory so that it does not count as leak).
let ret = ecx.layout_of(start_mir.return_ty())?;
let ret_ptr = ecx.allocate(ret, MiriMemoryKind::Static.into());

// Push our stack frame.
ecx.push_stack_frame(
start_instance,
// There is no call site.
DUMMY_SP,
start_mir,
Some(ret_ptr.into()),
StackPopCleanup::None { cleanup: true },
)?;

let mut args = ecx.frame().body.args_iter();

// First argument: pointer to `main()`.
let main_ptr = ecx.memory_mut().create_fn_alloc(main_instance);
let dest = ecx.eval_place(&mir::Place::Base(mir::PlaceBase::Local(args.next().unwrap())))?;
ecx.write_scalar(Scalar::Ptr(main_ptr), dest)?;

// Second argument (argc): `1`.
let dest = ecx.eval_place(&mir::Place::Base(mir::PlaceBase::Local(args.next().unwrap())))?;
let argc = Scalar::from_uint(config.args.len() as u128, dest.layout.size);
ecx.write_scalar(argc, dest)?;
// Store argc for macOS's `_NSGetArgc`.
{
let argc_place = ecx.allocate(dest.layout, MiriMemoryKind::Env.into());
ecx.write_scalar(argc, argc_place.into())?;
ecx.machine.argc = Some(argc_place.ptr.to_ptr()?);
}

// FIXME: extract main source file path.
// Third argument (`argv`): created from `config.args`.
let dest = ecx.eval_place(&mir::Place::Base(mir::PlaceBase::Local(args.next().unwrap())))?;
// For Windows, construct a command string with all the aguments.
let mut cmd = String::new();
for arg in config.args.iter() {
if !cmd.is_empty() {
cmd.push(' ');
}
cmd.push_str(&*shell_escape::windows::escape(arg.as_str().into()));
}
// Don't forget `0` terminator.
cmd.push(std::char::from_u32(0).unwrap());
// Collect the pointers to the individual strings.
let mut argvs = Vec::<Pointer<Tag>>::new();
for arg in config.args {
// Add `0` terminator.
let mut arg = arg.into_bytes();
arg.push(0);
argvs.push(ecx.memory_mut().allocate_static_bytes(arg.as_slice(), MiriMemoryKind::Static.into()));
}
// Make an array with all these pointers, in the Miri memory.
let argvs_layout = ecx.layout_of(ecx.tcx.mk_array(ecx.tcx.mk_imm_ptr(ecx.tcx.types.u8), argvs.len() as u64))?;
let argvs_place = ecx.allocate(argvs_layout, MiriMemoryKind::Env.into());
for (idx, arg) in argvs.into_iter().enumerate() {
let place = ecx.mplace_field(argvs_place, idx as u64)?;
ecx.write_scalar(Scalar::Ptr(arg), place.into())?;
}
ecx.memory_mut().mark_immutable(argvs_place.to_ptr()?.alloc_id)?;
// Write a pointer to that place as the argument.
let argv = argvs_place.ptr;
ecx.write_scalar(argv, dest)?;
// Store `argv` for macOS `_NSGetArgv`.
{
let argv_place = ecx.allocate(dest.layout, MiriMemoryKind::Env.into());
ecx.write_scalar(argv, argv_place.into())?;
ecx.machine.argv = Some(argv_place.ptr.to_ptr()?);
}
// Store command line as UTF-16 for Windows `GetCommandLineW`.
{
let tcx = &{ecx.tcx.tcx};
let cmd_utf16: Vec<u16> = cmd.encode_utf16().collect();
let cmd_ptr = ecx.memory_mut().allocate(
Size::from_bytes(cmd_utf16.len() as u64 * 2),
Align::from_bytes(2).unwrap(),
MiriMemoryKind::Env.into(),
);
ecx.machine.cmd_line = Some(cmd_ptr);
// Store the UTF-16 string.
let char_size = Size::from_bytes(2);
let cmd_alloc = ecx.memory_mut().get_mut(cmd_ptr.alloc_id)?;
let mut cur_ptr = cmd_ptr;
for &c in cmd_utf16.iter() {
cmd_alloc.write_scalar(
tcx,
cur_ptr,
Scalar::from_uint(c, char_size).into(),
char_size,
)?;
cur_ptr = cur_ptr.offset(char_size, tcx)?;
}
}

assert!(args.next().is_none(), "start lang item has more arguments than expected");

Ok(ecx)
}

pub fn eval_main<'tcx>(
tcx: TyCtxt<'tcx>,
main_id: DefId,
config: MiriConfig,
) {
let mut ecx = match create_ecx(tcx, main_id, config) {
Ok(ecx) => ecx,
Err(mut err) => {
err.print_backtrace();
panic!("Miri initialziation error: {}", err.kind)
}
};

// Perform the main execution.
let res: InterpResult = (|| {
ecx.run()?;
ecx.run_tls_dtors()
})();

// Process the result.
match res {
Ok(()) => {
let leaks = ecx.memory().leak_report();
// Disable the leak test on some platforms where we do not
// correctly implement TLS destructors.
let target_os = ecx.tcx.tcx.sess.target.target.target_os.to_lowercase();
let ignore_leaks = target_os == "windows" || target_os == "macos";
if !ignore_leaks && leaks != 0 {
tcx.sess.err("the evaluated program leaked memory");
}
}
Err(mut e) => {
// Special treatment for some error kinds
let msg = match e.kind {
InterpError::Exit(code) => std::process::exit(code),
InterpError::NoMirFor(..) =>
format!("{}. Did you set `MIRI_SYSROOT` to a Miri-enabled sysroot? You can prepare one with `cargo miri setup`.", e),
_ => e.to_string()
};
e.print_backtrace();
if let Some(frame) = ecx.stack().last() {
let block = &frame.body.basic_blocks()[frame.block];
let span = if frame.stmt < block.statements.len() {
block.statements[frame.stmt].source_info.span
} else {
block.terminator().source_info.span
};

let msg = format!("Miri evaluation error: {}", msg);
let mut err = struct_error(ecx.tcx.tcx.at(span), msg.as_str());
let frames = ecx.generate_stacktrace(None);
err.span_label(span, msg);
// We iterate with indices because we need to look at the next frame (the caller).
for idx in 0..frames.len() {
let frame_info = &frames[idx];
let call_site_is_local = frames.get(idx+1).map_or(false,
|caller_info| caller_info.instance.def_id().is_local());
if call_site_is_local {
err.span_note(frame_info.call_site, &frame_info.to_string());
} else {
err.note(&frame_info.to_string());
}
}
err.emit();
} else {
ecx.tcx.sess.err(&msg);
}

for (i, frame) in ecx.stack().iter().enumerate() {
trace!("-------------------");
trace!("Frame {}", i);
trace!(" return: {:?}", frame.return_place.map(|p| *p));
for (i, local) in frame.locals.iter().enumerate() {
trace!(" local {}: {:?}", i, local.value);
}
}
}
}
}
2 changes: 1 addition & 1 deletion src/helpers.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::mem;

use rustc::ty::{self, layout};
use rustc::ty::{self, layout::{self, Size}};
use rustc::hir::def_id::{DefId, CRATE_DEF_INDEX};

use crate::*;
Expand Down
Loading