]> git.lizzy.rs Git - rust.git/blob - src/lib.rs
47eaee6dfac32a946d3cda844883bc3da3ac5c38
[rust.git] / src / lib.rs
1 #![feature(rustc_private)]
2
3 #![cfg_attr(feature = "cargo-clippy", allow(cast_lossless))]
4
5 #[macro_use]
6 extern crate log;
7
8 // From rustc.
9 #[macro_use]
10 extern crate rustc;
11 extern crate rustc_data_structures;
12 extern crate rustc_mir;
13 extern crate rustc_target;
14 extern crate syntax;
15
16 use std::collections::HashMap;
17 use std::borrow::Cow;
18
19 use rustc::ty::{self, TyCtxt, query::TyCtxtAt};
20 use rustc::ty::layout::{TyLayout, LayoutOf, Size};
21 use rustc::hir::def_id::DefId;
22 use rustc::mir;
23
24 use syntax::attr;
25
26
27 pub use rustc_mir::interpret::*;
28 pub use rustc_mir::interpret::{self, AllocMap}; // resolve ambiguity
29
30 mod fn_call;
31 mod operator;
32 mod intrinsic;
33 mod helpers;
34 mod tls;
35 mod range_map;
36 mod mono_hash_map;
37 mod stacked_borrows;
38
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;
47
48 pub fn create_ecx<'a, 'mir: 'a, 'tcx: 'mir>(
49     tcx: TyCtxt<'a, 'tcx, 'tcx>,
50     main_id: DefId,
51     start_wrapper: Option<DefId>,
52     validate: bool,
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),
58     );
59
60     let main_instance = ty::Instance::mono(ecx.tcx.tcx, main_id);
61     let main_mir = ecx.load_mir(main_instance.def)?;
62
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"
66                 .to_owned(),
67         ));
68     }
69
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(
74             ecx.tcx.tcx,
75             ty::ParamEnv::reveal_all(),
76             start_id,
77             ecx.tcx.mk_substs(
78                 ::std::iter::once(ty::subst::Kind::from(main_ret_ty)))
79             ).unwrap();
80         let start_mir = ecx.load_mir(start_instance.def)?;
81
82         if start_mir.arg_count != 3 {
83             return err!(AbiViolation(format!(
84                 "'start' lang item should have three arguments, but has {}",
85                 start_mir.arg_count
86             )));
87         }
88
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())?;
92
93         // Push our stack frame
94         ecx.push_stack_frame(
95             start_instance,
96             start_mir.span,
97             start_mir,
98             Some(ret_ptr.into()),
99             StackPopCleanup::None { cleanup: true },
100         )?;
101
102         let mut args = ecx.frame().mir.args_iter();
103
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)?;
108
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)?;
112
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)?;
123
124         assert!(args.next().is_none(), "start lang item has more arguments than expected");
125     } else {
126         let ret_place = MPlaceTy::dangling(ecx.layout_of(tcx.mk_unit())?, &ecx).into();
127         ecx.push_stack_frame(
128             main_instance,
129             main_mir.span,
130             main_mir,
131             Some(ret_place),
132             StackPopCleanup::None { cleanup: true },
133         )?;
134
135         // No arguments
136         let mut args = ecx.frame().mir.args_iter();
137         assert!(args.next().is_none(), "main function must not have arguments");
138     }
139
140     Ok(ecx)
141 }
142
143 pub fn eval_main<'a, 'tcx: 'a>(
144     tcx: TyCtxt<'a, 'tcx, 'tcx>,
145     main_id: DefId,
146     start_wrapper: Option<DefId>,
147     validate: bool,
148 ) {
149     let mut ecx = create_ecx(tcx, main_id, start_wrapper, validate).expect("Couldn't create ecx");
150
151     let res: EvalResult = (|| {
152         ecx.run()?;
153         ecx.run_tls_dtors()
154     })();
155
156     match res {
157         Ok(()) => {
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");
165             }
166         }
167         Err(e) => {
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
172                 } else {
173                     block.terminator().source_info.span
174                 };
175
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));
183                 }
184                 err.emit();
185             } else {
186                 ecx.tcx.sess.err(&e.to_string());
187             }
188
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);
197                     }
198                 }
199             }*/
200         }
201     }
202 }
203
204
205 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
206 pub enum MiriMemoryKind {
207     /// `__rust_alloc` memory
208     Rust,
209     /// `malloc` memory
210     C,
211     /// Part of env var emulation
212     Env,
213     /// mutable statics
214     MutStatic,
215 }
216
217 impl Into<MemoryKind<MiriMemoryKind>> for MiriMemoryKind {
218     #[inline(always)]
219     fn into(self) -> MemoryKind<MiriMemoryKind> {
220         MemoryKind::Machine(self)
221     }
222 }
223
224 impl MayLeak for MiriMemoryKind {
225     #[inline(always)]
226     fn may_leak(self) -> bool {
227         use MiriMemoryKind::*;
228         match self {
229             Rust | C => false,
230             Env | MutStatic => true,
231         }
232     }
233 }
234
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>>,
240
241     /// TLS state
242     pub(crate) tls: TlsData<'tcx>,
243
244     /// Whether to enforce the validity invariant
245     pub(crate) validate: bool,
246 }
247
248 impl<'tcx> Evaluator<'tcx> {
249     fn new(validate: bool) -> Self {
250         Evaluator {
251             env_vars: HashMap::default(),
252             tls: TlsData::default(),
253             validate,
254         }
255     }
256 }
257
258 impl<'a, 'mir, 'tcx> Machine<'a, 'mir, 'tcx> for Evaluator<'tcx> {
259     type MemoryKinds = MiriMemoryKind;
260     type AllocExtra = ();
261     type PointerTag = Borrow;
262
263     type MemoryMap = MonoHashMap<AllocId, (MemoryKind<MiriMemoryKind>, Allocation<Borrow, Self::AllocExtra>)>;
264
265     const STATIC_KIND: Option<MiriMemoryKind> = Some(MiriMemoryKind::MutStatic);
266
267     fn enforce_validity(ecx: &EvalContext<'a, 'mir, 'tcx, Self>) -> bool {
268         if !ecx.machine.validate {
269             return false;
270         }
271
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
276             "std::ptr::read",
277             "std::sys::windows::mutex::Mutex::",
278         ];
279         for frame in ecx.stack().iter()
280             .rev().take(3)
281         {
282             let name = frame.instance.to_string();
283             if WHITELIST.iter().any(|white| name.starts_with(white)) {
284                 return false;
285             }
286         }
287         true
288     }
289
290     /// Returns Ok() when the function was handled, fail otherwise
291     fn find_fn(
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)
299     }
300
301     fn call_intrinsic(
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)
308     }
309
310     fn ptr_op(
311         ecx: &rustc_mir::interpret::EvalContext<'a, 'mir, 'tcx, Self>,
312         bin_op: mir::BinOp,
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)
319     }
320
321     fn box_alloc(
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(
331             malloc,
332             malloc_mir.span,
333             malloc_mir,
334             Some(dest),
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 },
339         )?;
340
341         let mut args = ecx.frame().mir.args_iter();
342         let layout = ecx.layout_of(dest.layout.ty.builtin_deref(false).unwrap().ty)?;
343
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)?;
349
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)?;
354
355         // No more arguments
356         assert!(args.next().is_none(), "exchange_malloc lang item has more arguments than expected");
357         Ok(())
358     }
359
360     fn find_foreign_static(
361         tcx: TyCtxtAt<'a, 'tcx, 'tcx>,
362         def_id: DefId,
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(),
368         };
369
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)
375             }
376             _ => return err!(Unimplemented(
377                     format!("can't access foreign static: {}", link_name),
378                 )),
379         };
380         Ok(Cow::Owned(alloc))
381     }
382
383     fn before_terminator(_ecx: &mut EvalContext<'a, 'mir, 'tcx, Self>) -> EvalResult<'tcx>
384     {
385         // We are not interested in detecting loops
386         Ok(())
387     }
388
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)))
397                     .collect()
398             ),
399             undef_mask: alloc.undef_mask.clone(),
400             align: alloc.align,
401             mutability: alloc.mutability,
402             extra: Self::AllocExtra::default(),
403         };
404         Cow::Owned(alloc)
405     }
406 }