1 use rustc::ty::{self, Ty};
2 use rustc::ty::layout::LayoutOf;
3 use rustc::hir::def_id::{DefId, CRATE_DEF_INDEX};
7 use syntax::codemap::Span;
17 use super::memory::MemoryKind;
19 pub trait EvalContextExt<'tcx> {
26 dest_block: mir::BasicBlock,
27 ) -> EvalResult<'tcx>;
29 fn resolve_path(&self, path: &[&str]) -> EvalResult<'tcx, ty::Instance<'tcx>>;
33 instance: ty::Instance<'tcx>,
34 destination: Option<(Place, mir::BasicBlock)>,
38 ) -> EvalResult<'tcx>;
42 instance: ty::Instance<'tcx>,
43 destination: Option<(Place, mir::BasicBlock)>,
47 ) -> EvalResult<'tcx, bool>;
49 fn write_null(&mut self, dest: Place, dest_ty: Ty<'tcx>) -> EvalResult<'tcx>;
52 impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator<'tcx>> {
55 instance: ty::Instance<'tcx>,
56 destination: Option<(Place, mir::BasicBlock)>,
60 ) -> EvalResult<'tcx, bool> {
61 trace!("eval_fn_call: {:#?}, {:#?}", instance, destination);
63 let mir = match self.load_mir(instance.def) {
65 Err(EvalError { kind: EvalErrorKind::NoMirFor(path), .. }) => {
75 Err(other) => return Err(other),
78 let (return_place, return_to_block) = match destination {
79 Some((place, block)) => (place, StackPopCleanup::Goto(block)),
80 None => (Place::undef(), StackPopCleanup::None),
83 self.push_stack_frame(
100 dest_block: mir::BasicBlock,
101 ) -> EvalResult<'tcx> {
102 let attrs = self.tcx.get_attrs(def_id);
103 let link_name = match attr::first_attr_value_str_by_name(&attrs, "link_name") {
104 Some(name) => name.as_str(),
105 None => self.tcx.item_name(def_id),
108 match &link_name[..] {
110 let size = self.value_to_primval(args[0])?.to_u64()?;
112 self.write_null(dest, dest_ty)?;
114 let align = self.memory.pointer_size();
115 let ptr = self.memory.allocate(size, align, Some(MemoryKind::C.into()))?;
116 self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?;
121 let ptr = self.into_ptr(args[0].value)?;
123 self.memory.deallocate(
126 MemoryKind::C.into(),
132 // TODO: read `syscall` ids like `sysconf` ids and
133 // figure out some way to actually process some of them
135 // libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)
136 // is called if a `HashMap` is created the regular way.
137 match self.value_to_primval(args[0])?.to_u64()? {
139 return err!(Unimplemented(
140 "miri does not support random number generators".to_owned(),
144 return err!(Unimplemented(
145 format!("miri does not support syscall id {}", id),
152 let _handle = self.into_ptr(args[0].value)?;
153 let symbol = self.into_ptr(args[1].value)?.to_ptr()?;
154 let symbol_name = self.memory.read_c_str(symbol)?;
155 let err = format!("bad c unicode symbol: {:?}", symbol_name);
156 let symbol_name = ::std::str::from_utf8(symbol_name).unwrap_or(&err);
157 return err!(Unimplemented(format!(
158 "miri does not support dynamically loading libraries (requested symbol: {})",
163 "__rust_maybe_catch_panic" => {
164 // fn __rust_maybe_catch_panic(f: fn(*mut u8), data: *mut u8, data_ptr: *mut usize, vtable_ptr: *mut usize) -> u32
165 // We abort on panic, so not much is going on here, but we still have to call the closure
166 let u8_ptr_ty = self.tcx.mk_mut_ptr(self.tcx.types.u8);
167 let f = self.into_ptr(args[0].value)?.to_ptr()?;
168 let data = self.into_ptr(args[1].value)?;
169 let f_instance = self.memory.get_fn(f)?;
170 self.write_null(dest, dest_ty)?;
172 // Now we make a function call. TODO: Consider making this re-usable? EvalContext::step does sth. similar for the TLS dtors,
173 // and of course eval_main.
174 let mir = self.load_mir(f_instance.def)?;
175 self.push_stack_frame(
180 StackPopCleanup::Goto(dest_block),
182 let mut args = self.frame().mir.args_iter();
184 let arg_local = args.next().ok_or(
185 EvalErrorKind::AbiViolation(
186 "Argument to __rust_maybe_catch_panic does not take enough arguments."
190 let arg_dest = self.eval_place(&mir::Place::Local(arg_local))?;
191 self.write_ptr(arg_dest, data, u8_ptr_ty)?;
193 assert!(args.next().is_none(), "__rust_maybe_catch_panic argument has more arguments than expected");
195 // We ourselves return 0
196 self.write_null(dest, dest_ty)?;
198 // Don't fall through
202 "__rust_start_panic" => {
207 let left = self.into_ptr(args[0].value)?;
208 let right = self.into_ptr(args[1].value)?;
209 let n = self.value_to_primval(args[2])?.to_u64()?;
212 let left_bytes = self.memory.read_bytes(left, n)?;
213 let right_bytes = self.memory.read_bytes(right, n)?;
215 use std::cmp::Ordering::*;
216 match left_bytes.cmp(right_bytes) {
225 PrimVal::Bytes(result as u128),
231 let ptr = self.into_ptr(args[0].value)?;
232 let val = self.value_to_primval(args[1])?.to_u64()? as u8;
233 let num = self.value_to_primval(args[2])?.to_u64()?;
234 if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().rev().position(
238 let new_ptr = ptr.offset(num - idx as u64 - 1, &self)?;
239 self.write_ptr(dest, new_ptr, dest_ty)?;
241 self.write_null(dest, dest_ty)?;
246 let ptr = self.into_ptr(args[0].value)?;
247 let val = self.value_to_primval(args[1])?.to_u64()? as u8;
248 let num = self.value_to_primval(args[2])?.to_u64()?;
249 if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().position(
253 let new_ptr = ptr.offset(idx as u64, &self)?;
254 self.write_ptr(dest, new_ptr, dest_ty)?;
256 self.write_null(dest, dest_ty)?;
262 let name_ptr = self.into_ptr(args[0].value)?.to_ptr()?;
263 let name = self.memory.read_c_str(name_ptr)?;
264 match self.machine.env_vars.get(name) {
265 Some(&var) => PrimVal::Ptr(var),
266 None => PrimVal::Bytes(0),
269 self.write_primval(dest, result, dest_ty)?;
273 let mut success = None;
275 let name_ptr = self.into_ptr(args[0].value)?;
276 if !name_ptr.is_null()? {
277 let name = self.memory.read_c_str(name_ptr.to_ptr()?)?;
278 if !name.is_empty() && !name.contains(&b'=') {
279 success = Some(self.machine.env_vars.remove(name));
283 if let Some(old) = success {
284 if let Some(var) = old {
285 self.memory.deallocate(var, None, MemoryKind::Env.into())?;
287 self.write_null(dest, dest_ty)?;
289 self.write_primval(dest, PrimVal::from_i128(-1), dest_ty)?;
296 let name_ptr = self.into_ptr(args[0].value)?;
297 let value_ptr = self.into_ptr(args[1].value)?.to_ptr()?;
298 let value = self.memory.read_c_str(value_ptr)?;
299 if !name_ptr.is_null()? {
300 let name = self.memory.read_c_str(name_ptr.to_ptr()?)?;
301 if !name.is_empty() && !name.contains(&b'=') {
302 new = Some((name.to_owned(), value.to_owned()));
306 if let Some((name, value)) = new {
307 // +1 for the null terminator
308 let value_copy = self.memory.allocate(
309 (value.len() + 1) as u64,
311 Some(MemoryKind::Env.into()),
313 self.memory.write_bytes(value_copy.into(), &value)?;
314 let trailing_zero_ptr = value_copy.offset(value.len() as u64, &self)?.into();
315 self.memory.write_bytes(trailing_zero_ptr, &[0])?;
316 if let Some(var) = self.machine.env_vars.insert(
321 self.memory.deallocate(var, None, MemoryKind::Env.into())?;
323 self.write_null(dest, dest_ty)?;
325 self.write_primval(dest, PrimVal::from_i128(-1), dest_ty)?;
330 let fd = self.value_to_primval(args[0])?.to_u64()?;
331 let buf = self.into_ptr(args[1].value)?;
332 let n = self.value_to_primval(args[2])?.to_u64()?;
333 trace!("Called write({:?}, {:?}, {:?})", fd, buf, n);
334 let result = if fd == 1 || fd == 2 {
336 use std::io::{self, Write};
338 let buf_cont = self.memory.read_bytes(buf, n)?;
339 let res = if fd == 1 {
340 io::stdout().write(buf_cont)
342 io::stderr().write(buf_cont)
349 warn!("Ignored output to FD {}", fd);
350 n as isize // pretend it all went well
351 }; // now result is the value we return back to the program
354 PrimVal::Bytes(result as u128),
360 let ptr = self.into_ptr(args[0].value)?.to_ptr()?;
361 let n = self.memory.read_c_str(ptr)?.len();
362 self.write_primval(dest, PrimVal::Bytes(n as u128), dest_ty)?;
365 // Some things needed for sys::thread initialization to go through
366 "signal" | "sigaction" | "sigaltstack" => {
367 self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?;
371 let name = self.value_to_primval(args[0])?.to_u64()?;
372 trace!("sysconf() called with name {}", name);
373 // cache the sysconf integers via miri's global cache
375 (&["libc", "_SC_PAGESIZE"], PrimVal::Bytes(4096)),
376 (&["libc", "_SC_GETPW_R_SIZE_MAX"], PrimVal::from_i128(-1)),
378 let mut result = None;
379 for &(path, path_value) in paths {
380 if let Ok(instance) = self.resolve_path(path) {
385 // compute global if not cached
386 let val = match self.tcx.interpret_interner.borrow().get_cached(cid) {
388 None => eval_body(self.tcx, instance, ty::ParamEnv::empty(traits::Reveal::All))?.0,
390 let val = self.value_to_primval(ValTy { value: Value::ByRef(val), ty: args[0].ty })?.to_u64()?;
392 result = Some(path_value);
397 if let Some(result) = result {
398 self.write_primval(dest, result, dest_ty)?;
400 return err!(Unimplemented(
401 format!("Unimplemented sysconf name: {}", name),
406 // Hook pthread calls that go to the thread-local storage memory subsystem
407 "pthread_key_create" => {
408 let key_ptr = self.into_ptr(args[0].value)?;
410 // Extract the function type out of the signature (that seems easier than constructing it ourselves...)
411 let dtor = match self.into_ptr(args[1].value)?.into_inner_primval() {
412 PrimVal::Ptr(dtor_ptr) => Some(self.memory.get_fn(dtor_ptr)?),
413 PrimVal::Bytes(0) => None,
414 PrimVal::Bytes(_) => return err!(ReadBytesAsPointer),
415 PrimVal::Undef => return err!(ReadUndefBytes),
418 // Figure out how large a pthread TLS key actually is. This is libc::pthread_key_t.
419 let key_type = args[0].ty.builtin_deref(true, ty::LvaluePreference::NoPreference)
420 .ok_or(EvalErrorKind::AbiViolation("Wrong signature used for pthread_key_create: First argument must be a raw pointer.".to_owned()))?.ty;
421 let key_size = self.layout_of(key_type)?.size;
423 // Create key and write it into the memory where key_ptr wants it
424 let key = self.memory.create_tls_key(dtor) as u128;
425 if key_size.bits() < 128 && key >= (1u128 << key_size.bits() as u128) {
426 return err!(OutOfTls);
428 self.memory.write_primval(
435 // Return success (0)
436 self.write_null(dest, dest_ty)?;
438 "pthread_key_delete" => {
439 // The conversion into TlsKey here is a little fishy, but should work as long as usize >= libc::pthread_key_t
440 let key = self.value_to_primval(args[0])?.to_u64()? as TlsKey;
441 self.memory.delete_tls_key(key)?;
442 // Return success (0)
443 self.write_null(dest, dest_ty)?;
445 "pthread_getspecific" => {
446 // The conversion into TlsKey here is a little fishy, but should work as long as usize >= libc::pthread_key_t
447 let key = self.value_to_primval(args[0])?.to_u64()? as TlsKey;
448 let ptr = self.memory.load_tls(key)?;
449 self.write_ptr(dest, ptr, dest_ty)?;
451 "pthread_setspecific" => {
452 // The conversion into TlsKey here is a little fishy, but should work as long as usize >= libc::pthread_key_t
453 let key = self.value_to_primval(args[0])?.to_u64()? as TlsKey;
454 let new_ptr = self.into_ptr(args[1].value)?;
455 self.memory.store_tls(key, new_ptr)?;
457 // Return success (0)
458 self.write_null(dest, dest_ty)?;
461 // Stub out all the other pthread calls to just return 0
462 link_name if link_name.starts_with("pthread_") => {
463 info!("ignoring C ABI call: {}", link_name);
464 self.write_null(dest, dest_ty)?;
468 return err!(Unimplemented(
469 format!("can't call C ABI function: {}", link_name),
474 // Since we pushed no stack frame, the main loop will act
475 // as if the call just completed and it's returning to the
477 self.dump_local(dest);
478 self.goto_block(dest_block);
482 /// Get an instance for a path.
483 fn resolve_path(&self, path: &[&str]) -> EvalResult<'tcx, ty::Instance<'tcx>> {
487 .find(|&&krate| self.tcx.original_crate_name(krate) == path[0])
491 index: CRATE_DEF_INDEX,
493 let mut items = self.tcx.item_children(krate);
494 let mut path_it = path.iter().skip(1).peekable();
496 while let Some(segment) = path_it.next() {
497 for item in mem::replace(&mut items, Default::default()).iter() {
498 if item.ident.name == *segment {
499 if path_it.peek().is_none() {
500 return Some(ty::Instance::mono(self.tcx, item.def.def_id()));
503 items = self.tcx.item_children(item.def.def_id());
511 let path = path.iter().map(|&s| s.to_owned()).collect();
512 EvalErrorKind::PathNotFound(path).into()
518 instance: ty::Instance<'tcx>,
519 destination: Option<(Place, mir::BasicBlock)>,
520 args: &[ValTy<'tcx>],
521 sig: ty::FnSig<'tcx>,
523 ) -> EvalResult<'tcx> {
524 // In some cases in non-MIR libstd-mode, not having a destination is legit. Handle these early.
526 "std::panicking::rust_panic_with_hook" |
527 "core::panicking::panic_fmt::::panic_impl" |
528 "std::rt::begin_panic_fmt" => return err!(Panic),
532 let dest_ty = sig.output();
533 let (dest, dest_block) = destination.ok_or_else(
534 || EvalErrorKind::NoMirFor(path.clone()),
537 if sig.abi == Abi::C {
538 // An external C function
539 // TODO: That functions actually has a similar preamble to what follows here. May make sense to
540 // unify these two mechanisms for "hooking into missing functions".
552 // Allocators are magic. They have no MIR, even when the rest of libstd does.
553 "alloc::heap::::__rust_alloc" => {
554 let size = self.value_to_primval(args[0])?.to_u64()?;
555 let align = self.value_to_primval(args[1])?.to_u64()?;
557 return err!(HeapAllocZeroBytes);
559 if !align.is_power_of_two() {
560 return err!(HeapAllocNonPowerOfTwoAlignment(align));
562 let ptr = self.memory.allocate(size, align, Some(MemoryKind::Rust.into()))?;
563 self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?;
565 "alloc::heap::::__rust_alloc_zeroed" => {
566 let size = self.value_to_primval(args[0])?.to_u64()?;
567 let align = self.value_to_primval(args[1])?.to_u64()?;
569 return err!(HeapAllocZeroBytes);
571 if !align.is_power_of_two() {
572 return err!(HeapAllocNonPowerOfTwoAlignment(align));
574 let ptr = self.memory.allocate(size, align, Some(MemoryKind::Rust.into()))?;
575 self.memory.write_repeat(ptr.into(), 0, size)?;
576 self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?;
578 "alloc::heap::::__rust_dealloc" => {
579 let ptr = self.into_ptr(args[0].value)?.to_ptr()?;
580 let old_size = self.value_to_primval(args[1])?.to_u64()?;
581 let align = self.value_to_primval(args[2])?.to_u64()?;
583 return err!(HeapAllocZeroBytes);
585 if !align.is_power_of_two() {
586 return err!(HeapAllocNonPowerOfTwoAlignment(align));
588 self.memory.deallocate(
590 Some((old_size, align)),
591 MemoryKind::Rust.into(),
594 "alloc::heap::::__rust_realloc" => {
595 let ptr = self.into_ptr(args[0].value)?.to_ptr()?;
596 let old_size = self.value_to_primval(args[1])?.to_u64()?;
597 let old_align = self.value_to_primval(args[2])?.to_u64()?;
598 let new_size = self.value_to_primval(args[3])?.to_u64()?;
599 let new_align = self.value_to_primval(args[4])?.to_u64()?;
600 if old_size == 0 || new_size == 0 {
601 return err!(HeapAllocZeroBytes);
603 if !old_align.is_power_of_two() {
604 return err!(HeapAllocNonPowerOfTwoAlignment(old_align));
606 if !new_align.is_power_of_two() {
607 return err!(HeapAllocNonPowerOfTwoAlignment(new_align));
609 let new_ptr = self.memory.reallocate(
615 MemoryKind::Rust.into(),
617 self.write_primval(dest, PrimVal::Ptr(new_ptr), dest_ty)?;
620 // A Rust function is missing, which means we are running with MIR missing for libstd (or other dependencies).
621 // Still, we can make many things mostly work by "emulating" or ignoring some functions.
622 "std::io::_print" => {
624 "Ignoring output. To run programs that print, make sure you have a libstd with full MIR."
627 "std::thread::Builder::new" => {
628 return err!(Unimplemented("miri does not support threading".to_owned()))
630 "std::env::args" => {
631 return err!(Unimplemented(
632 "miri does not support program arguments".to_owned(),
635 "std::panicking::panicking" |
636 "std::rt::panicking" => {
637 // we abort on panic -> `std::rt::panicking` always returns false
638 let bool = self.tcx.types.bool;
639 self.write_primval(dest, PrimVal::from_bool(false), bool)?;
641 "std::sys::imp::c::::AddVectoredExceptionHandler" |
642 "std::sys::imp::c::::SetThreadStackGuarantee" => {
643 let usize = self.tcx.types.usize;
644 // any non zero value works for the stdlib. This is just used for stackoverflows anyway
645 self.write_primval(dest, PrimVal::Bytes(1), usize)?;
647 _ => return err!(NoMirFor(path)),
650 // Since we pushed no stack frame, the main loop will act
651 // as if the call just completed and it's returning to the
653 self.dump_local(dest);
654 self.goto_block(dest_block);
658 fn write_null(&mut self, dest: Place, dest_ty: Ty<'tcx>) -> EvalResult<'tcx> {
659 self.write_primval(dest, PrimVal::Bytes(0), dest_ty)