Skip to content

Commit 9e42475

Browse files
committed
Auto merge of #802 - RalfJung:machine, r=RalfJung
some lib.rs refactoring Taken out of rust-lang/miri#799 so that we can land it now and resolve merge conflicts instead of dragging them along. Splits lib.rs into machine.rs for the machine state and trait impl, and eval.rs for the main evaluator loop and setting up the initial stack frame.
2 parents bc969f1 + 67d3779 commit 9e42475

File tree

6 files changed

+642
-623
lines changed

6 files changed

+642
-623
lines changed

src/eval.rs

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
use rand::rngs::StdRng;
2+
use rand::SeedableRng;
3+
4+
use syntax::source_map::DUMMY_SP;
5+
use rustc::ty::{self, TyCtxt};
6+
use rustc::ty::layout::{LayoutOf, Size, Align};
7+
use rustc::hir::def_id::DefId;
8+
use rustc::mir;
9+
10+
use crate::{
11+
InterpResult, InterpError, InterpretCx, StackPopCleanup, struct_error,
12+
Scalar, Tag, Pointer,
13+
MiriMemoryKind, Evaluator, TlsEvalContextExt,
14+
};
15+
16+
/// Configuration needed to spawn a Miri instance.
17+
#[derive(Clone)]
18+
pub struct MiriConfig {
19+
pub validate: bool,
20+
pub args: Vec<String>,
21+
22+
// The seed to use when non-determinism is required (e.g. getrandom())
23+
pub seed: Option<u64>
24+
}
25+
26+
// Used by priroda.
27+
pub fn create_ecx<'mir, 'tcx: 'mir>(
28+
tcx: TyCtxt<'tcx>,
29+
main_id: DefId,
30+
config: MiriConfig,
31+
) -> InterpResult<'tcx, InterpretCx<'mir, 'tcx, Evaluator<'tcx>>> {
32+
let mut ecx = InterpretCx::new(
33+
tcx.at(syntax::source_map::DUMMY_SP),
34+
ty::ParamEnv::reveal_all(),
35+
Evaluator::new(config.validate),
36+
);
37+
38+
// FIXME: InterpretCx::new should take an initial MemoryExtra
39+
ecx.memory_mut().extra.rng = config.seed.map(StdRng::seed_from_u64);
40+
41+
let main_instance = ty::Instance::mono(ecx.tcx.tcx, main_id);
42+
let main_mir = ecx.load_mir(main_instance.def)?;
43+
44+
if !main_mir.return_ty().is_unit() || main_mir.arg_count != 0 {
45+
return err!(Unimplemented(
46+
"miri does not support main functions without `fn()` type signatures"
47+
.to_owned(),
48+
));
49+
}
50+
51+
let start_id = tcx.lang_items().start_fn().unwrap();
52+
let main_ret_ty = tcx.fn_sig(main_id).output();
53+
let main_ret_ty = main_ret_ty.no_bound_vars().unwrap();
54+
let start_instance = ty::Instance::resolve(
55+
ecx.tcx.tcx,
56+
ty::ParamEnv::reveal_all(),
57+
start_id,
58+
ecx.tcx.mk_substs(
59+
::std::iter::once(ty::subst::Kind::from(main_ret_ty)))
60+
).unwrap();
61+
let start_mir = ecx.load_mir(start_instance.def)?;
62+
63+
if start_mir.arg_count != 3 {
64+
return err!(AbiViolation(format!(
65+
"'start' lang item should have three arguments, but has {}",
66+
start_mir.arg_count
67+
)));
68+
}
69+
70+
// Return value (in static memory so that it does not count as leak).
71+
let ret = ecx.layout_of(start_mir.return_ty())?;
72+
let ret_ptr = ecx.allocate(ret, MiriMemoryKind::Static.into());
73+
74+
// Push our stack frame.
75+
ecx.push_stack_frame(
76+
start_instance,
77+
// There is no call site.
78+
DUMMY_SP,
79+
start_mir,
80+
Some(ret_ptr.into()),
81+
StackPopCleanup::None { cleanup: true },
82+
)?;
83+
84+
let mut args = ecx.frame().body.args_iter();
85+
86+
// First argument: pointer to `main()`.
87+
let main_ptr = ecx.memory_mut().create_fn_alloc(main_instance);
88+
let dest = ecx.eval_place(&mir::Place::Base(mir::PlaceBase::Local(args.next().unwrap())))?;
89+
ecx.write_scalar(Scalar::Ptr(main_ptr), dest)?;
90+
91+
// Second argument (argc): `1`.
92+
let dest = ecx.eval_place(&mir::Place::Base(mir::PlaceBase::Local(args.next().unwrap())))?;
93+
let argc = Scalar::from_uint(config.args.len() as u128, dest.layout.size);
94+
ecx.write_scalar(argc, dest)?;
95+
// Store argc for macOS's `_NSGetArgc`.
96+
{
97+
let argc_place = ecx.allocate(dest.layout, MiriMemoryKind::Env.into());
98+
ecx.write_scalar(argc, argc_place.into())?;
99+
ecx.machine.argc = Some(argc_place.ptr.to_ptr()?);
100+
}
101+
102+
// FIXME: extract main source file path.
103+
// Third argument (`argv`): created from `config.args`.
104+
let dest = ecx.eval_place(&mir::Place::Base(mir::PlaceBase::Local(args.next().unwrap())))?;
105+
// For Windows, construct a command string with all the aguments.
106+
let mut cmd = String::new();
107+
for arg in config.args.iter() {
108+
if !cmd.is_empty() {
109+
cmd.push(' ');
110+
}
111+
cmd.push_str(&*shell_escape::windows::escape(arg.as_str().into()));
112+
}
113+
// Don't forget `0` terminator.
114+
cmd.push(std::char::from_u32(0).unwrap());
115+
// Collect the pointers to the individual strings.
116+
let mut argvs = Vec::<Pointer<Tag>>::new();
117+
for arg in config.args {
118+
// Add `0` terminator.
119+
let mut arg = arg.into_bytes();
120+
arg.push(0);
121+
argvs.push(ecx.memory_mut().allocate_static_bytes(arg.as_slice(), MiriMemoryKind::Static.into()));
122+
}
123+
// Make an array with all these pointers, in the Miri memory.
124+
let argvs_layout = ecx.layout_of(ecx.tcx.mk_array(ecx.tcx.mk_imm_ptr(ecx.tcx.types.u8), argvs.len() as u64))?;
125+
let argvs_place = ecx.allocate(argvs_layout, MiriMemoryKind::Env.into());
126+
for (idx, arg) in argvs.into_iter().enumerate() {
127+
let place = ecx.mplace_field(argvs_place, idx as u64)?;
128+
ecx.write_scalar(Scalar::Ptr(arg), place.into())?;
129+
}
130+
ecx.memory_mut().mark_immutable(argvs_place.to_ptr()?.alloc_id)?;
131+
// Write a pointer to that place as the argument.
132+
let argv = argvs_place.ptr;
133+
ecx.write_scalar(argv, dest)?;
134+
// Store `argv` for macOS `_NSGetArgv`.
135+
{
136+
let argv_place = ecx.allocate(dest.layout, MiriMemoryKind::Env.into());
137+
ecx.write_scalar(argv, argv_place.into())?;
138+
ecx.machine.argv = Some(argv_place.ptr.to_ptr()?);
139+
}
140+
// Store command line as UTF-16 for Windows `GetCommandLineW`.
141+
{
142+
let tcx = &{ecx.tcx.tcx};
143+
let cmd_utf16: Vec<u16> = cmd.encode_utf16().collect();
144+
let cmd_ptr = ecx.memory_mut().allocate(
145+
Size::from_bytes(cmd_utf16.len() as u64 * 2),
146+
Align::from_bytes(2).unwrap(),
147+
MiriMemoryKind::Env.into(),
148+
);
149+
ecx.machine.cmd_line = Some(cmd_ptr);
150+
// Store the UTF-16 string.
151+
let char_size = Size::from_bytes(2);
152+
let cmd_alloc = ecx.memory_mut().get_mut(cmd_ptr.alloc_id)?;
153+
let mut cur_ptr = cmd_ptr;
154+
for &c in cmd_utf16.iter() {
155+
cmd_alloc.write_scalar(
156+
tcx,
157+
cur_ptr,
158+
Scalar::from_uint(c, char_size).into(),
159+
char_size,
160+
)?;
161+
cur_ptr = cur_ptr.offset(char_size, tcx)?;
162+
}
163+
}
164+
165+
assert!(args.next().is_none(), "start lang item has more arguments than expected");
166+
167+
Ok(ecx)
168+
}
169+
170+
pub fn eval_main<'tcx>(
171+
tcx: TyCtxt<'tcx>,
172+
main_id: DefId,
173+
config: MiriConfig,
174+
) {
175+
let mut ecx = match create_ecx(tcx, main_id, config) {
176+
Ok(ecx) => ecx,
177+
Err(mut err) => {
178+
err.print_backtrace();
179+
panic!("Miri initialziation error: {}", err.kind)
180+
}
181+
};
182+
183+
// Perform the main execution.
184+
let res: InterpResult = (|| {
185+
ecx.run()?;
186+
ecx.run_tls_dtors()
187+
})();
188+
189+
// Process the result.
190+
match res {
191+
Ok(()) => {
192+
let leaks = ecx.memory().leak_report();
193+
// Disable the leak test on some platforms where we do not
194+
// correctly implement TLS destructors.
195+
let target_os = ecx.tcx.tcx.sess.target.target.target_os.to_lowercase();
196+
let ignore_leaks = target_os == "windows" || target_os == "macos";
197+
if !ignore_leaks && leaks != 0 {
198+
tcx.sess.err("the evaluated program leaked memory");
199+
}
200+
}
201+
Err(mut e) => {
202+
// Special treatment for some error kinds
203+
let msg = match e.kind {
204+
InterpError::Exit(code) => std::process::exit(code),
205+
InterpError::NoMirFor(..) =>
206+
format!("{}. Did you set `MIRI_SYSROOT` to a Miri-enabled sysroot? You can prepare one with `cargo miri setup`.", e),
207+
_ => e.to_string()
208+
};
209+
e.print_backtrace();
210+
if let Some(frame) = ecx.stack().last() {
211+
let block = &frame.body.basic_blocks()[frame.block];
212+
let span = if frame.stmt < block.statements.len() {
213+
block.statements[frame.stmt].source_info.span
214+
} else {
215+
block.terminator().source_info.span
216+
};
217+
218+
let msg = format!("Miri evaluation error: {}", msg);
219+
let mut err = struct_error(ecx.tcx.tcx.at(span), msg.as_str());
220+
let frames = ecx.generate_stacktrace(None);
221+
err.span_label(span, msg);
222+
// We iterate with indices because we need to look at the next frame (the caller).
223+
for idx in 0..frames.len() {
224+
let frame_info = &frames[idx];
225+
let call_site_is_local = frames.get(idx+1).map_or(false,
226+
|caller_info| caller_info.instance.def_id().is_local());
227+
if call_site_is_local {
228+
err.span_note(frame_info.call_site, &frame_info.to_string());
229+
} else {
230+
err.note(&frame_info.to_string());
231+
}
232+
}
233+
err.emit();
234+
} else {
235+
ecx.tcx.sess.err(&msg);
236+
}
237+
238+
for (i, frame) in ecx.stack().iter().enumerate() {
239+
trace!("-------------------");
240+
trace!("Frame {}", i);
241+
trace!(" return: {:?}", frame.return_place.map(|p| *p));
242+
for (i, local) in frame.locals.iter().enumerate() {
243+
trace!(" local {}: {:?}", i, local.value);
244+
}
245+
}
246+
}
247+
}
248+
}

src/helpers.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::mem;
22

3-
use rustc::ty::{self, layout};
3+
use rustc::ty::{self, layout::{self, Size}};
44
use rustc::hir::def_id::{DefId, CRATE_DEF_INDEX};
55

66
use crate::*;

0 commit comments

Comments
 (0)