1 #![feature(rustc_private)]
3 #![cfg_attr(feature = "cargo-clippy", allow(cast_lossless))]
11 extern crate rustc_data_structures;
12 extern crate rustc_mir;
13 extern crate rustc_target;
16 use std::collections::HashMap;
19 use rustc::ty::{self, TyCtxt, query::TyCtxtAt};
20 use rustc::ty::layout::{TyLayout, LayoutOf, Size};
21 use rustc::hir::def_id::DefId;
27 pub use rustc_mir::interpret::*;
28 pub use rustc_mir::interpret::{self, AllocMap}; // resolve ambiguity
39 use fn_call::EvalContextExt as MissingFnsEvalContextExt;
40 use operator::EvalContextExt as OperatorEvalContextExt;
41 use intrinsic::EvalContextExt as IntrinsicEvalContextExt;
42 use tls::{EvalContextExt as TlsEvalContextExt, TlsData};
43 use range_map::RangeMap;
44 use helpers::FalibleScalarExt;
45 use mono_hash_map::MonoHashMap;
46 use stacked_borrows::Borrow;
48 pub fn create_ecx<'a, 'mir: 'a, 'tcx: 'mir>(
49 tcx: TyCtxt<'a, 'tcx, 'tcx>,
51 start_wrapper: Option<DefId>,
53 ) -> EvalResult<'tcx, EvalContext<'a, 'mir, 'tcx, Evaluator<'tcx>>> {
54 let mut ecx = EvalContext::new(
55 tcx.at(syntax::source_map::DUMMY_SP),
56 ty::ParamEnv::reveal_all(),
57 Evaluator::new(validate),
60 let main_instance = ty::Instance::mono(ecx.tcx.tcx, main_id);
61 let main_mir = ecx.load_mir(main_instance.def)?;
63 if !main_mir.return_ty().is_unit() || main_mir.arg_count != 0 {
64 return err!(Unimplemented(
65 "miri does not support main functions without `fn()` type signatures"
70 if let Some(start_id) = start_wrapper {
71 let main_ret_ty = ecx.tcx.fn_sig(main_id).output();
72 let main_ret_ty = main_ret_ty.no_late_bound_regions().unwrap();
73 let start_instance = ty::Instance::resolve(
75 ty::ParamEnv::reveal_all(),
78 ::std::iter::once(ty::subst::Kind::from(main_ret_ty)))
80 let start_mir = ecx.load_mir(start_instance.def)?;
82 if start_mir.arg_count != 3 {
83 return err!(AbiViolation(format!(
84 "'start' lang item should have three arguments, but has {}",
89 // Return value (in static memory so that it does not count as leak)
90 let ret = ecx.layout_of(start_mir.return_ty())?;
91 let ret_ptr = ecx.allocate(ret, MiriMemoryKind::MutStatic.into())?;
93 // Push our stack frame
99 StackPopCleanup::None { cleanup: true },
102 let mut args = ecx.frame().mir.args_iter();
104 // First argument: pointer to main()
105 let main_ptr = ecx.memory_mut().create_fn_alloc(main_instance);
106 let dest = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?;
107 ecx.write_scalar(Scalar::Ptr(main_ptr), dest)?;
109 // Second argument (argc): 1
110 let dest = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?;
111 ecx.write_scalar(Scalar::from_int(1, dest.layout.size), dest)?;
113 // FIXME: extract main source file path
114 // Third argument (argv): &[b"foo"]
115 let dest = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?;
116 let foo = ecx.memory.allocate_static_bytes(b"foo\0");
117 let foo_ty = ecx.tcx.mk_imm_ptr(ecx.tcx.types.u8);
118 let foo_layout = ecx.layout_of(foo_ty)?;
119 let foo_place = ecx.allocate(foo_layout, MiriMemoryKind::Env.into())?;
120 ecx.write_scalar(Scalar::Ptr(foo), foo_place.into())?;
121 ecx.memory.mark_immutable(foo_place.to_ptr()?.alloc_id)?;
122 ecx.write_scalar(foo_place.ptr, dest)?;
124 assert!(args.next().is_none(), "start lang item has more arguments than expected");
126 let ret_place = MPlaceTy::dangling(ecx.layout_of(tcx.mk_unit())?, &ecx).into();
127 ecx.push_stack_frame(
132 StackPopCleanup::None { cleanup: true },
136 let mut args = ecx.frame().mir.args_iter();
137 assert!(args.next().is_none(), "main function must not have arguments");
143 pub fn eval_main<'a, 'tcx: 'a>(
144 tcx: TyCtxt<'a, 'tcx, 'tcx>,
146 start_wrapper: Option<DefId>,
149 let mut ecx = create_ecx(tcx, main_id, start_wrapper, validate).expect("Couldn't create ecx");
151 let res: EvalResult = (|| {
158 let leaks = ecx.memory().leak_report();
159 // Disable the leak test on some platforms where we likely do not
160 // correctly implement TLS destructors.
161 let target_os = ecx.tcx.tcx.sess.target.target.target_os.to_lowercase();
162 let ignore_leaks = target_os == "windows" || target_os == "macos";
163 if !ignore_leaks && leaks != 0 {
164 tcx.sess.err("the evaluated program leaked memory");
168 if let Some(frame) = ecx.stack().last() {
169 let block = &frame.mir.basic_blocks()[frame.block];
170 let span = if frame.stmt < block.statements.len() {
171 block.statements[frame.stmt].source_info.span
173 block.terminator().source_info.span
176 let e = e.to_string();
177 let msg = format!("constant evaluation error: {}", e);
178 let mut err = struct_error(ecx.tcx.tcx.at(span), msg.as_str());
179 let (frames, span) = ecx.generate_stacktrace(None);
180 err.span_label(span, e);
181 for FrameInfo { span, location, .. } in frames {
182 err.span_note(span, &format!("inside call to `{}`", location));
186 ecx.tcx.sess.err(&e.to_string());
189 /* Nice try, but with MIRI_BACKTRACE this shows 100s of backtraces.
190 for (i, frame) in ecx.stack().iter().enumerate() {
191 trace!("-------------------");
192 trace!("Frame {}", i);
193 trace!(" return: {:#?}", frame.return_place);
194 for (i, local) in frame.locals.iter().enumerate() {
195 if let Ok(local) = local.access() {
196 trace!(" local {}: {:?}", i, local);
205 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
206 pub enum MiriMemoryKind {
207 /// `__rust_alloc` memory
211 /// Part of env var emulation
217 impl Into<MemoryKind<MiriMemoryKind>> for MiriMemoryKind {
219 fn into(self) -> MemoryKind<MiriMemoryKind> {
220 MemoryKind::Machine(self)
224 impl MayLeak for MiriMemoryKind {
226 fn may_leak(self) -> bool {
227 use MiriMemoryKind::*;
230 Env | MutStatic => true,
235 #[derive(Clone, PartialEq, Eq)]
236 pub struct Evaluator<'tcx> {
237 /// Environment variables set by `setenv`
238 /// Miri does not expose env vars from the host to the emulated program
239 pub(crate) env_vars: HashMap<Vec<u8>, Pointer<Borrow>>,
242 pub(crate) tls: TlsData<'tcx>,
244 /// Whether to enforce the validity invariant
245 pub(crate) validate: bool,
248 impl<'tcx> Evaluator<'tcx> {
249 fn new(validate: bool) -> Self {
251 env_vars: HashMap::default(),
252 tls: TlsData::default(),
258 impl<'a, 'mir, 'tcx> Machine<'a, 'mir, 'tcx> for Evaluator<'tcx> {
259 type MemoryKinds = MiriMemoryKind;
260 type AllocExtra = ();
261 type PointerTag = Borrow;
263 type MemoryMap = MonoHashMap<AllocId, (MemoryKind<MiriMemoryKind>, Allocation<Borrow, Self::AllocExtra>)>;
265 const STATIC_KIND: Option<MiriMemoryKind> = Some(MiriMemoryKind::MutStatic);
267 fn enforce_validity(ecx: &EvalContext<'a, 'mir, 'tcx, Self>) -> bool {
268 if !ecx.machine.validate {
272 // Some functions are whitelisted until we figure out how to fix them.
273 // We walk up the stack a few frames to also cover their callees.
274 const WHITELIST: &[&str] = &[
275 // Uses mem::uninitialized
277 "std::sys::windows::mutex::Mutex::",
279 for frame in ecx.stack().iter()
282 let name = frame.instance.to_string();
283 if WHITELIST.iter().any(|white| name.starts_with(white)) {
290 /// Returns Ok() when the function was handled, fail otherwise
292 ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
293 instance: ty::Instance<'tcx>,
294 args: &[OpTy<'tcx, Borrow>],
295 dest: Option<PlaceTy<'tcx, Borrow>>,
296 ret: Option<mir::BasicBlock>,
297 ) -> EvalResult<'tcx, Option<&'mir mir::Mir<'tcx>>> {
298 ecx.find_fn(instance, args, dest, ret)
302 ecx: &mut rustc_mir::interpret::EvalContext<'a, 'mir, 'tcx, Self>,
303 instance: ty::Instance<'tcx>,
304 args: &[OpTy<'tcx, Borrow>],
305 dest: PlaceTy<'tcx, Borrow>,
306 ) -> EvalResult<'tcx> {
307 ecx.call_intrinsic(instance, args, dest)
311 ecx: &rustc_mir::interpret::EvalContext<'a, 'mir, 'tcx, Self>,
313 left: Scalar<Borrow>,
314 left_layout: TyLayout<'tcx>,
315 right: Scalar<Borrow>,
316 right_layout: TyLayout<'tcx>,
317 ) -> EvalResult<'tcx, (Scalar<Borrow>, bool)> {
318 ecx.ptr_op(bin_op, left, left_layout, right, right_layout)
322 ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
323 dest: PlaceTy<'tcx, Borrow>,
324 ) -> EvalResult<'tcx> {
325 trace!("box_alloc for {:?}", dest.layout.ty);
326 // Call the `exchange_malloc` lang item
327 let malloc = ecx.tcx.lang_items().exchange_malloc_fn().unwrap();
328 let malloc = ty::Instance::mono(ecx.tcx.tcx, malloc);
329 let malloc_mir = ecx.load_mir(malloc.def)?;
330 ecx.push_stack_frame(
335 // Don't do anything when we are done. The statement() function will increment
336 // the old stack frame's stmt counter to the next statement, which means that when
337 // exchange_malloc returns, we go on evaluating exactly where we want to be.
338 StackPopCleanup::None { cleanup: true },
341 let mut args = ecx.frame().mir.args_iter();
342 let layout = ecx.layout_of(dest.layout.ty.builtin_deref(false).unwrap().ty)?;
344 // First argument: size
345 // (0 is allowed here, this is expected to be handled by the lang item)
346 let arg = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?;
347 let size = layout.size.bytes();
348 ecx.write_scalar(Scalar::from_uint(size, arg.layout.size), arg)?;
350 // Second argument: align
351 let arg = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?;
352 let align = layout.align.abi();
353 ecx.write_scalar(Scalar::from_uint(align, arg.layout.size), arg)?;
356 assert!(args.next().is_none(), "exchange_malloc lang item has more arguments than expected");
360 fn find_foreign_static(
361 tcx: TyCtxtAt<'a, 'tcx, 'tcx>,
363 ) -> EvalResult<'tcx, Cow<'tcx, Allocation<Borrow, Self::AllocExtra>>> {
364 let attrs = tcx.get_attrs(def_id);
365 let link_name = match attr::first_attr_value_str_by_name(&attrs, "link_name") {
366 Some(name) => name.as_str(),
367 None => tcx.item_name(def_id).as_str(),
370 let alloc = match &link_name[..] {
371 "__cxa_thread_atexit_impl" => {
372 // This should be all-zero, pointer-sized
373 let data = vec![0; tcx.data_layout.pointer_size.bytes() as usize];
374 Allocation::from_bytes(&data[..], tcx.data_layout.pointer_align)
376 _ => return err!(Unimplemented(
377 format!("can't access foreign static: {}", link_name),
380 Ok(Cow::Owned(alloc))
383 fn before_terminator(_ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>) -> EvalResult<'tcx>
385 // We are not interested in detecting loops
389 fn static_with_default_tag(
390 alloc: &'_ Allocation
391 ) -> Cow<'_, Allocation<Borrow, Self::AllocExtra>> {
392 let alloc: Allocation<Borrow, Self::AllocExtra> = Allocation {
393 bytes: alloc.bytes.clone(),
394 relocations: Relocations::from_presorted(
395 alloc.relocations.iter()
396 .map(|&(offset, ((), alloc))| (offset, (Borrow::default(), alloc)))
399 undef_mask: alloc.undef_mask.clone(),
401 mutability: alloc.mutability,
402 extra: Self::AllocExtra::default(),