]> git.lizzy.rs Git - rust.git/blob - miri/lib.rs
399418f6734bcad1c9dfb04ef5fc74c5c284100a
[rust.git] / miri / lib.rs
1 #![feature(
2     i128_type,
3     rustc_private,
4 )]
5
6 // From rustc.
7 #[macro_use]
8 extern crate log;
9 #[macro_use]
10 extern crate rustc;
11 extern crate syntax;
12
13 use rustc::ty::{self, TyCtxt};
14 use rustc::ty::layout::TyLayout;
15 use rustc::hir::def_id::DefId;
16 use rustc::mir;
17 use rustc::traits;
18
19 use syntax::ast::Mutability;
20 use syntax::codemap::Span;
21
22 use std::collections::{HashMap, BTreeMap};
23
24 pub use rustc::mir::interpret::*;
25
26 mod fn_call;
27 mod operator;
28 mod intrinsic;
29 mod helpers;
30 mod memory;
31 mod tls;
32
33 use fn_call::EvalContextExt as MissingFnsEvalContextExt;
34 use operator::EvalContextExt as OperatorEvalContextExt;
35 use intrinsic::EvalContextExt as IntrinsicEvalContextExt;
36 use tls::EvalContextExt as TlsEvalContextExt;
37
38 pub fn eval_main<'a, 'tcx: 'a>(
39     tcx: TyCtxt<'a, 'tcx, 'tcx>,
40     main_id: DefId,
41     start_wrapper: Option<DefId>,
42     limits: ResourceLimits,
43 ) {
44     fn run_main<'a, 'tcx: 'a>(
45         ecx: &mut rustc::mir::interpret::EvalContext<'a, 'tcx, Evaluator>,
46         main_id: DefId,
47         start_wrapper: Option<DefId>,
48     ) -> EvalResult<'tcx> {
49         let main_instance = ty::Instance::mono(ecx.tcx, main_id);
50         let main_mir = ecx.load_mir(main_instance.def)?;
51         let mut cleanup_ptr = None; // Pointer to be deallocated when we are done
52
53         if !main_mir.return_ty().is_nil() || main_mir.arg_count != 0 {
54             return err!(Unimplemented(
55                 "miri does not support main functions without `fn()` type signatures"
56                     .to_owned(),
57             ));
58         }
59
60         if let Some(start_id) = start_wrapper {
61             let start_instance = ty::Instance::mono(ecx.tcx, start_id);
62             let start_mir = ecx.load_mir(start_instance.def)?;
63
64             if start_mir.arg_count != 3 {
65                 return err!(AbiViolation(format!(
66                     "'start' lang item should have three arguments, but has {}",
67                     start_mir.arg_count
68                 )));
69             }
70
71             // Return value
72             let size = ecx.tcx.data_layout.pointer_size.bytes();
73             let align = ecx.tcx.data_layout.pointer_align.abi();
74             let ret_ptr = ecx.memory_mut().allocate(size, align, Some(MemoryKind::Stack))?;
75             cleanup_ptr = Some(ret_ptr);
76
77             // Push our stack frame
78             ecx.push_stack_frame(
79                 start_instance,
80                 start_mir.span,
81                 start_mir,
82                 Place::from_ptr(ret_ptr),
83                 StackPopCleanup::None,
84             )?;
85
86             let mut args = ecx.frame().mir.args_iter();
87
88             // First argument: pointer to main()
89             let main_ptr = ecx.memory_mut().create_fn_alloc(main_instance);
90             let dest = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?;
91             let main_ty = main_instance.def.def_ty(ecx.tcx);
92             let main_ptr_ty = ecx.tcx.mk_fn_ptr(main_ty.fn_sig(ecx.tcx));
93             ecx.write_value(
94                 ValTy {
95                     value: Value::ByVal(PrimVal::Ptr(main_ptr)),
96                     ty: main_ptr_ty,
97                 },
98                 dest,
99             )?;
100
101             // Second argument (argc): 1
102             let dest = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?;
103             let ty = ecx.tcx.types.isize;
104             ecx.write_primval(dest, PrimVal::Bytes(1), ty)?;
105
106             // FIXME: extract main source file path
107             // Third argument (argv): &[b"foo"]
108             let dest = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?;
109             let ty = ecx.tcx.mk_imm_ptr(ecx.tcx.mk_imm_ptr(ecx.tcx.types.u8));
110             let foo = ecx.memory.allocate_cached(b"foo\0");
111             let ptr_size = ecx.memory.pointer_size();
112             let foo_ptr = ecx.memory.allocate(ptr_size * 1, ptr_size, None)?;
113             ecx.memory.write_primval(foo_ptr.into(), PrimVal::Ptr(foo.into()), ptr_size, false)?;
114             ecx.memory.mark_static_initalized(foo_ptr.alloc_id, Mutability::Immutable)?;
115             ecx.write_ptr(dest, foo_ptr.into(), ty)?;
116
117             assert!(args.next().is_none(), "start lang item has more arguments than expected");
118         } else {
119             ecx.push_stack_frame(
120                 main_instance,
121                 main_mir.span,
122                 main_mir,
123                 Place::undef(),
124                 StackPopCleanup::None,
125             )?;
126
127             // No arguments
128             let mut args = ecx.frame().mir.args_iter();
129             assert!(args.next().is_none(), "main function must not have arguments");
130         }
131
132         while ecx.step()? {}
133         ecx.run_tls_dtors()?;
134         if let Some(cleanup_ptr) = cleanup_ptr {
135             ecx.memory_mut().deallocate(
136                 cleanup_ptr,
137                 None,
138                 MemoryKind::Stack,
139             )?;
140         }
141         Ok(())
142     }
143
144     let mut ecx = EvalContext::new(tcx, limits, Default::default(), Default::default());
145     match run_main(&mut ecx, main_id, start_wrapper) {
146         Ok(()) => {
147             let leaks = ecx.memory().leak_report();
148             if leaks != 0 {
149                 tcx.sess.err("the evaluated program leaked memory");
150             }
151         }
152         Err(mut e) => {
153             ecx.report(&mut e);
154         }
155     }
156 }
157
158 pub struct Evaluator;
159 #[derive(Default)]
160 pub struct EvaluatorData {
161     /// Environment variables set by `setenv`
162     /// Miri does not expose env vars from the host to the emulated program
163     pub(crate) env_vars: HashMap<Vec<u8>, MemoryPointer>,
164 }
165
166 pub type TlsKey = usize;
167
168 #[derive(Copy, Clone, Debug)]
169 pub struct TlsEntry<'tcx> {
170     data: Pointer, // Will eventually become a map from thread IDs to `Pointer`s, if we ever support more than one thread.
171     dtor: Option<ty::Instance<'tcx>>,
172 }
173
174 #[derive(Default)]
175 pub struct MemoryData<'tcx> {
176     /// The Key to use for the next thread-local allocation.
177     next_thread_local: TlsKey,
178
179     /// pthreads-style thread-local storage.
180     thread_local: BTreeMap<TlsKey, TlsEntry<'tcx>>,
181 }
182
183 impl<'tcx> Machine<'tcx> for Evaluator {
184     type Data = EvaluatorData;
185     type MemoryData = MemoryData<'tcx>;
186     type MemoryKinds = memory::MemoryKind;
187
188     fn param_env<'a>(
189         _: &EvalContext<'a, 'tcx, Self>,
190     ) -> ty::ParamEnv<'tcx> {
191         ty::ParamEnv::empty(traits::Reveal::All)
192     }
193
194     /// Returns Ok() when the function was handled, fail otherwise
195     fn eval_fn_call<'a>(
196         ecx: &mut EvalContext<'a, 'tcx, Self>,
197         instance: ty::Instance<'tcx>,
198         destination: Option<(Place, mir::BasicBlock)>,
199         args: &[ValTy<'tcx>],
200         span: Span,
201         sig: ty::FnSig<'tcx>,
202     ) -> EvalResult<'tcx, bool> {
203         ecx.eval_fn_call(instance, destination, args, span, sig)
204     }
205
206     fn call_intrinsic<'a>(
207         ecx: &mut rustc::mir::interpret::EvalContext<'a, 'tcx, Self>,
208         instance: ty::Instance<'tcx>,
209         args: &[ValTy<'tcx>],
210         dest: Place,
211         dest_layout: TyLayout<'tcx>,
212         target: mir::BasicBlock,
213     ) -> EvalResult<'tcx> {
214         ecx.call_intrinsic(instance, args, dest, dest_layout, target)
215     }
216
217     fn try_ptr_op<'a>(
218         ecx: &rustc::mir::interpret::EvalContext<'a, 'tcx, Self>,
219         bin_op: mir::BinOp,
220         left: PrimVal,
221         left_ty: ty::Ty<'tcx>,
222         right: PrimVal,
223         right_ty: ty::Ty<'tcx>,
224     ) -> EvalResult<'tcx, Option<(PrimVal, bool)>> {
225         ecx.ptr_op(bin_op, left, left_ty, right, right_ty)
226     }
227
228     fn mark_static_initialized(m: memory::MemoryKind) -> EvalResult<'tcx> {
229         use memory::MemoryKind::*;
230         match m {
231             // FIXME: This could be allowed, but not for env vars set during miri execution
232             Env => err!(Unimplemented("statics can't refer to env vars".to_owned())),
233             _ => Ok(()),
234         }
235     }
236
237     fn box_alloc<'a>(
238         ecx: &mut EvalContext<'a, 'tcx, Self>,
239         ty: ty::Ty<'tcx>,
240         dest: Place,
241     ) -> EvalResult<'tcx> {
242         let size = ecx.type_size(ty)?.expect("box only works with sized types");
243         let align = ecx.type_align(ty)?;
244
245         // Call the `exchange_malloc` lang item
246         let malloc = ecx.tcx.lang_items().exchange_malloc_fn().unwrap();
247         let malloc = ty::Instance::mono(ecx.tcx, malloc);
248         let malloc_mir = ecx.load_mir(malloc.def)?;
249         ecx.push_stack_frame(
250             malloc,
251             malloc_mir.span,
252             malloc_mir,
253             dest,
254             // Don't do anything when we are done.  The statement() function will increment
255             // the old stack frame's stmt counter to the next statement, which means that when
256             // exchange_malloc returns, we go on evaluating exactly where we want to be.
257             StackPopCleanup::None,
258         )?;
259
260         let mut args = ecx.frame().mir.args_iter();
261         let usize = ecx.tcx.types.usize;
262
263         // First argument: size
264         let dest = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?;
265         ecx.write_value(
266             ValTy {
267                 value: Value::ByVal(PrimVal::Bytes(size as u128)),
268                 ty: usize,
269             },
270             dest,
271         )?;
272
273         // Second argument: align
274         let dest = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?;
275         ecx.write_value(
276             ValTy {
277                 value: Value::ByVal(PrimVal::Bytes(align as u128)),
278                 ty: usize,
279             },
280             dest,
281         )?;
282
283         // No more arguments
284         assert!(args.next().is_none(), "exchange_malloc lang item has more arguments than expected");
285         Ok(())
286     }
287
288     fn global_item_with_linkage<'a>(
289         ecx: &mut EvalContext<'a, 'tcx, Self>,
290         instance: ty::Instance<'tcx>,
291         mutability: Mutability,
292     ) -> EvalResult<'tcx> {
293         // FIXME: check that it's `#[linkage = "extern_weak"]`
294         trace!("Initializing an extern global with NULL");
295         let ptr_size = ecx.memory.pointer_size();
296         let ptr = ecx.memory.allocate(
297             ptr_size,
298             ptr_size,
299             None,
300         )?;
301         ecx.memory.write_ptr_sized_unsigned(ptr, PrimVal::Bytes(0))?;
302         ecx.memory.mark_static_initalized(ptr.alloc_id, mutability)?;
303         ecx.tcx.interpret_interner.borrow_mut().cache(
304             GlobalId {
305                 instance,
306                 promoted: None,
307             },
308             PtrAndAlign {
309                 ptr: ptr.into(),
310                 aligned: true,
311             },
312         );
313         Ok(())
314     }
315 }