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 rustc::ty::{self, TyCtxt, query::TyCtxtAt};
17 use rustc::ty::layout::{TyLayout, LayoutOf, Size};
18 use rustc::hir::def_id::DefId;
21 use syntax::ast::Mutability;
24 use std::collections::HashMap;
26 pub use rustc::mir::interpret::*;
27 pub use rustc_mir::interpret::*;
28 pub use rustc_mir::interpret;
38 use fn_call::EvalContextExt as MissingFnsEvalContextExt;
39 use operator::EvalContextExt as OperatorEvalContextExt;
40 use intrinsic::EvalContextExt as IntrinsicEvalContextExt;
41 use tls::{EvalContextExt as TlsEvalContextExt, TlsData};
42 use range_map::RangeMap;
43 use helpers::FalibleScalarExt;
45 pub fn create_ecx<'a, 'mir: 'a, 'tcx: 'mir>(
46 tcx: TyCtxt<'a, 'tcx, 'tcx>,
48 start_wrapper: Option<DefId>,
49 ) -> EvalResult<'tcx, EvalContext<'a, 'mir, 'tcx, Evaluator<'tcx>>> {
50 let mut ecx = EvalContext::new(
51 tcx.at(syntax::source_map::DUMMY_SP),
52 ty::ParamEnv::reveal_all(),
57 let main_instance = ty::Instance::mono(ecx.tcx.tcx, main_id);
58 let main_mir = ecx.load_mir(main_instance.def)?;
60 if !main_mir.return_ty().is_unit() || main_mir.arg_count != 0 {
61 return err!(Unimplemented(
62 "miri does not support main functions without `fn()` type signatures"
66 let ptr_size = ecx.memory.pointer_size();
68 if let Some(start_id) = start_wrapper {
69 let main_ret_ty = ecx.tcx.fn_sig(main_id).output();
70 let main_ret_ty = main_ret_ty.no_late_bound_regions().unwrap();
71 let start_instance = ty::Instance::resolve(
73 ty::ParamEnv::reveal_all(),
76 ::std::iter::once(ty::subst::Kind::from(main_ret_ty)))
78 let start_mir = ecx.load_mir(start_instance.def)?;
80 if start_mir.arg_count != 3 {
81 return err!(AbiViolation(format!(
82 "'start' lang item should have three arguments, but has {}",
87 // Return value (in static memory so that it does not count as leak)
88 let size = ecx.tcx.data_layout.pointer_size;
89 let align = ecx.tcx.data_layout.pointer_align;
90 let ret_ptr = ecx.memory_mut().allocate(size, align, MiriMemoryKind::MutStatic.into())?;
92 // Push our stack frame
97 Place::from_ptr(ret_ptr, align),
98 StackPopCleanup::None { cleanup: true },
101 let mut args = ecx.frame().mir.args_iter();
103 // First argument: pointer to main()
104 let main_ptr = ecx.memory_mut().create_fn_alloc(main_instance);
105 let dest = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?;
106 ecx.write_scalar(Scalar::Ptr(main_ptr), dest)?;
108 // Second argument (argc): 1
109 let dest = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?;
110 ecx.write_scalar(Scalar::from_int(1, dest.layout.size), dest)?;
112 // FIXME: extract main source file path
113 // Third argument (argv): &[b"foo"]
114 let dest = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?;
115 let foo = ecx.memory.allocate_static_bytes(b"foo\0");
116 let foo_ty = ecx.tcx.mk_imm_ptr(ecx.tcx.types.u8);
117 let foo_layout = ecx.layout_of(foo_ty)?;
118 let foo_place = ecx.allocate(foo_layout, MemoryKind::Stack)?; // will be interned in just a second
119 ecx.write_scalar(Scalar::Ptr(foo), foo_place.into())?;
120 ecx.memory.intern_static(foo_place.to_ptr()?.alloc_id, Mutability::Immutable)?;
121 ecx.write_scalar(foo_place.ptr, dest)?;
123 assert!(args.next().is_none(), "start lang item has more arguments than expected");
125 ecx.push_stack_frame(
129 Place::from_scalar_ptr(Scalar::from_int(1, ptr_size).into(), ty::layout::Align::from_bytes(1, 1).unwrap()),
130 StackPopCleanup::None { cleanup: true },
134 let mut args = ecx.frame().mir.args_iter();
135 assert!(args.next().is_none(), "main function must not have arguments");
141 pub fn eval_main<'a, 'tcx: 'a>(
142 tcx: TyCtxt<'a, 'tcx, 'tcx>,
144 start_wrapper: Option<DefId>,
146 let mut ecx = create_ecx(tcx, main_id, start_wrapper).expect("Couldn't create ecx");
148 let res: EvalResult = (|| {
155 let leaks = ecx.memory().leak_report();
156 // Disable the leak test on some platforms where we likely do not
157 // correctly implement TLS destructors.
158 let target_os = ecx.tcx.tcx.sess.target.target.target_os.to_lowercase();
159 let ignore_leaks = target_os == "windows" || target_os == "macos";
160 if !ignore_leaks && leaks != 0 {
161 tcx.sess.err("the evaluated program leaked memory");
165 if let Some(frame) = ecx.stack().last() {
166 let block = &frame.mir.basic_blocks()[frame.block];
167 let span = if frame.stmt < block.statements.len() {
168 block.statements[frame.stmt].source_info.span
170 block.terminator().source_info.span
173 let e = e.to_string();
174 let msg = format!("constant evaluation error: {}", e);
175 let mut err = struct_error(ecx.tcx.tcx.at(span), msg.as_str());
176 let (frames, span) = ecx.generate_stacktrace(None);
177 err.span_label(span, e);
178 for FrameInfo { span, location, .. } in frames {
179 err.span_note(span, &format!("inside call to `{}`", location));
183 ecx.tcx.sess.err(&e.to_string());
186 /* Nice try, but with MIRI_BACKTRACE this shows 100s of backtraces.
187 for (i, frame) in ecx.stack().iter().enumerate() {
188 trace!("-------------------");
189 trace!("Frame {}", i);
190 trace!(" return: {:#?}", frame.return_place);
191 for (i, local) in frame.locals.iter().enumerate() {
192 if let Ok(local) = local.access() {
193 trace!(" local {}: {:?}", i, local);
202 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
203 pub enum MiriMemoryKind {
204 /// `__rust_alloc` memory
208 /// Part of env var emulation
214 impl Into<MemoryKind<MiriMemoryKind>> for MiriMemoryKind {
215 fn into(self) -> MemoryKind<MiriMemoryKind> {
216 MemoryKind::Machine(self)
221 #[derive(Clone, Default, PartialEq, Eq)]
222 pub struct Evaluator<'tcx> {
223 /// Environment variables set by `setenv`
224 /// Miri does not expose env vars from the host to the emulated program
225 pub(crate) env_vars: HashMap<Vec<u8>, Pointer>,
228 pub(crate) tls: TlsData<'tcx>,
231 impl<'a, 'mir, 'tcx> Machine<'a, 'mir, 'tcx> for Evaluator<'tcx> {
232 type MemoryData = ();
233 type MemoryKinds = MiriMemoryKind;
235 const MUT_STATIC_KIND: Option<MiriMemoryKind> = Some(MiriMemoryKind::MutStatic);
236 const ENFORCE_VALIDITY: bool = false; // this is still WIP
238 /// Returns Ok() when the function was handled, fail otherwise
240 ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
241 instance: ty::Instance<'tcx>,
243 dest: Option<PlaceTy<'tcx>>,
244 ret: Option<mir::BasicBlock>,
245 ) -> EvalResult<'tcx, Option<&'mir mir::Mir<'tcx>>> {
246 ecx.find_fn(instance, args, dest, ret)
250 ecx: &mut rustc_mir::interpret::EvalContext<'a, 'mir, 'tcx, Self>,
251 instance: ty::Instance<'tcx>,
254 ) -> EvalResult<'tcx> {
255 ecx.call_intrinsic(instance, args, dest)
259 ecx: &rustc_mir::interpret::EvalContext<'a, 'mir, 'tcx, Self>,
262 left_layout: TyLayout<'tcx>,
264 right_layout: TyLayout<'tcx>,
265 ) -> EvalResult<'tcx, (Scalar, bool)> {
266 ecx.ptr_op(bin_op, left, left_layout, right, right_layout)
270 ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
272 ) -> EvalResult<'tcx> {
273 trace!("box_alloc for {:?}", dest.layout.ty);
274 // Call the `exchange_malloc` lang item
275 let malloc = ecx.tcx.lang_items().exchange_malloc_fn().unwrap();
276 let malloc = ty::Instance::mono(ecx.tcx.tcx, malloc);
277 let malloc_mir = ecx.load_mir(malloc.def)?;
278 ecx.push_stack_frame(
283 // Don't do anything when we are done. The statement() function will increment
284 // the old stack frame's stmt counter to the next statement, which means that when
285 // exchange_malloc returns, we go on evaluating exactly where we want to be.
286 StackPopCleanup::None { cleanup: true },
289 let mut args = ecx.frame().mir.args_iter();
290 let layout = ecx.layout_of(dest.layout.ty.builtin_deref(false).unwrap().ty)?;
292 // First argument: size
293 // (0 is allowed here, this is expected to be handled by the lang item)
294 let arg = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?;
295 let size = layout.size.bytes();
296 ecx.write_scalar(Scalar::from_uint(size, arg.layout.size), arg)?;
298 // Second argument: align
299 let arg = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?;
300 let align = layout.align.abi();
301 ecx.write_scalar(Scalar::from_uint(align, arg.layout.size), arg)?;
304 assert!(args.next().is_none(), "exchange_malloc lang item has more arguments than expected");
308 fn find_foreign_static(
309 tcx: TyCtxtAt<'a, 'tcx, 'tcx>,
311 ) -> EvalResult<'tcx, &'tcx Allocation> {
312 let attrs = tcx.get_attrs(def_id);
313 let link_name = match attr::first_attr_value_str_by_name(&attrs, "link_name") {
314 Some(name) => name.as_str(),
315 None => tcx.item_name(def_id).as_str(),
318 let alloc = match &link_name[..] {
319 "__cxa_thread_atexit_impl" => {
320 // This should be all-zero, pointer-sized
321 let data = vec![0; tcx.data_layout.pointer_size.bytes() as usize];
322 let alloc = Allocation::from_bytes(&data[..], tcx.data_layout.pointer_align);
323 tcx.intern_const_alloc(alloc)
325 _ => return err!(Unimplemented(
326 format!("can't access foreign static: {}", link_name),
333 _ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>,
334 _op: ::rustc::mir::ValidationOp,
335 _operand: &::rustc::mir::ValidationOperand<'tcx, ::rustc::mir::Place<'tcx>>,
336 ) -> EvalResult<'tcx> {
337 // FIXME: prevent this from ICEing
338 //ecx.validation_op(op, operand)
342 fn before_terminator(_ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>) -> EvalResult<'tcx>
344 // We are not interested in detecting loops