]> git.lizzy.rs Git - rust.git/blob - miri/lib.rs
Remove type_size and type_align calls
[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, LayoutOf};
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, ty::ParamEnv::empty(traits::Reveal::All), 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 #[derive(Default)]
159 pub struct Evaluator {
160     /// Environment variables set by `setenv`
161     /// Miri does not expose env vars from the host to the emulated program
162     pub(crate) env_vars: HashMap<Vec<u8>, MemoryPointer>,
163 }
164
165 pub type TlsKey = usize;
166
167 #[derive(Copy, Clone, Debug)]
168 pub struct TlsEntry<'tcx> {
169     data: Pointer, // Will eventually become a map from thread IDs to `Pointer`s, if we ever support more than one thread.
170     dtor: Option<ty::Instance<'tcx>>,
171 }
172
173 #[derive(Default)]
174 pub struct MemoryData<'tcx> {
175     /// The Key to use for the next thread-local allocation.
176     next_thread_local: TlsKey,
177
178     /// pthreads-style thread-local storage.
179     thread_local: BTreeMap<TlsKey, TlsEntry<'tcx>>,
180 }
181
182 impl<'tcx> Machine<'tcx> for Evaluator {
183     type MemoryData = MemoryData<'tcx>;
184     type MemoryKinds = memory::MemoryKind;
185
186     /// Returns Ok() when the function was handled, fail otherwise
187     fn eval_fn_call<'a>(
188         ecx: &mut EvalContext<'a, 'tcx, Self>,
189         instance: ty::Instance<'tcx>,
190         destination: Option<(Place, mir::BasicBlock)>,
191         args: &[ValTy<'tcx>],
192         span: Span,
193         sig: ty::FnSig<'tcx>,
194     ) -> EvalResult<'tcx, bool> {
195         ecx.eval_fn_call(instance, destination, args, span, sig)
196     }
197
198     fn call_intrinsic<'a>(
199         ecx: &mut rustc::mir::interpret::EvalContext<'a, 'tcx, Self>,
200         instance: ty::Instance<'tcx>,
201         args: &[ValTy<'tcx>],
202         dest: Place,
203         dest_layout: TyLayout<'tcx>,
204         target: mir::BasicBlock,
205     ) -> EvalResult<'tcx> {
206         ecx.call_intrinsic(instance, args, dest, dest_layout, target)
207     }
208
209     fn try_ptr_op<'a>(
210         ecx: &rustc::mir::interpret::EvalContext<'a, 'tcx, Self>,
211         bin_op: mir::BinOp,
212         left: PrimVal,
213         left_ty: ty::Ty<'tcx>,
214         right: PrimVal,
215         right_ty: ty::Ty<'tcx>,
216     ) -> EvalResult<'tcx, Option<(PrimVal, bool)>> {
217         ecx.ptr_op(bin_op, left, left_ty, right, right_ty)
218     }
219
220     fn mark_static_initialized(m: memory::MemoryKind) -> EvalResult<'tcx> {
221         use memory::MemoryKind::*;
222         match m {
223             // FIXME: This could be allowed, but not for env vars set during miri execution
224             Env => err!(Unimplemented("statics can't refer to env vars".to_owned())),
225             _ => Ok(()),
226         }
227     }
228
229     fn box_alloc<'a>(
230         ecx: &mut EvalContext<'a, 'tcx, Self>,
231         ty: ty::Ty<'tcx>,
232         dest: Place,
233     ) -> EvalResult<'tcx> {
234         let layout = ecx.layout_of(ty)?;
235
236         // Call the `exchange_malloc` lang item
237         let malloc = ecx.tcx.lang_items().exchange_malloc_fn().unwrap();
238         let malloc = ty::Instance::mono(ecx.tcx, malloc);
239         let malloc_mir = ecx.load_mir(malloc.def)?;
240         ecx.push_stack_frame(
241             malloc,
242             malloc_mir.span,
243             malloc_mir,
244             dest,
245             // Don't do anything when we are done.  The statement() function will increment
246             // the old stack frame's stmt counter to the next statement, which means that when
247             // exchange_malloc returns, we go on evaluating exactly where we want to be.
248             StackPopCleanup::None,
249         )?;
250
251         let mut args = ecx.frame().mir.args_iter();
252         let usize = ecx.tcx.types.usize;
253
254         // First argument: size
255         let dest = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?;
256         ecx.write_value(
257             ValTy {
258                 value: Value::ByVal(PrimVal::Bytes(layout.size.bytes().into())),
259                 ty: usize,
260             },
261             dest,
262         )?;
263
264         // Second argument: align
265         let dest = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?;
266         ecx.write_value(
267             ValTy {
268                 value: Value::ByVal(PrimVal::Bytes(layout.align.abi().into())),
269                 ty: usize,
270             },
271             dest,
272         )?;
273
274         // No more arguments
275         assert!(args.next().is_none(), "exchange_malloc lang item has more arguments than expected");
276         Ok(())
277     }
278
279     fn global_item_with_linkage<'a>(
280         ecx: &mut EvalContext<'a, 'tcx, Self>,
281         instance: ty::Instance<'tcx>,
282         mutability: Mutability,
283     ) -> EvalResult<'tcx> {
284         // FIXME: check that it's `#[linkage = "extern_weak"]`
285         trace!("Initializing an extern global with NULL");
286         let ptr_size = ecx.memory.pointer_size();
287         let ptr = ecx.memory.allocate(
288             ptr_size,
289             ptr_size,
290             None,
291         )?;
292         ecx.memory.write_ptr_sized_unsigned(ptr, PrimVal::Bytes(0))?;
293         ecx.memory.mark_static_initalized(ptr.alloc_id, mutability)?;
294         ecx.tcx.interpret_interner.borrow_mut().cache(
295             GlobalId {
296                 instance,
297                 promoted: None,
298             },
299             PtrAndAlign {
300                 ptr: ptr.into(),
301                 aligned: true,
302             },
303         );
304         Ok(())
305     }
306 }