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