ret: mir::BasicBlock,
) -> EvalResult<'tcx>;
- /// Emulate a function that should have MIR but does not.
- /// This is solely to support execution without full MIR.
- /// Fail if emulating this function is not supported.
- /// This function will handle `goto_block` if needed.
- fn emulate_missing_fn(
- &mut self,
- path: String,
- args: &[OpTy<'tcx, Borrow>],
- dest: Option<PlaceTy<'tcx, Borrow>>,
- ret: Option<mir::BasicBlock>,
- ) -> EvalResult<'tcx>;
-
fn find_fn(
&mut self,
instance: ty::Instance<'tcx>,
return Ok(None);
}
- // Otherwise we really want to see the MIR -- but if we do not have it, maybe we can
- // emulate something. This is a HACK to support running without a full-MIR libstd.
- let mir = match self.load_mir(instance.def) {
- Ok(mir) => mir,
- Err(EvalError { kind: EvalErrorKind::NoMirFor(path), .. }) => {
- self.emulate_missing_fn(
- path,
- args,
- dest,
- ret,
- )?;
- // `goto_block` already handled
- return Ok(None);
- }
- Err(other) => return Err(other),
- };
-
- Ok(Some(mir))
+ // Otherwise, load the MIR
+ Ok(Some(self.load_mir(instance.def)?))
}
fn emulate_foreign_item(
Ok(())
}
- fn emulate_missing_fn(
- &mut self,
- path: String,
- _args: &[OpTy<'tcx, Borrow>],
- dest: Option<PlaceTy<'tcx, Borrow>>,
- ret: Option<mir::BasicBlock>,
- ) -> EvalResult<'tcx> {
- // In some cases in non-MIR libstd-mode, not having a destination is legit. Handle these early.
- match &path[..] {
- "std::panicking::rust_panic_with_hook" |
- "core::panicking::panic_fmt::::panic_impl" |
- "std::rt::begin_panic_fmt" =>
- return err!(MachineError("the evaluated program panicked".to_string())),
- _ => {}
- }
-
- let dest = dest.ok_or_else(
- // Must be some function we do not support
- || EvalErrorKind::NoMirFor(path.clone()),
- )?;
-
- match &path[..] {
- // A Rust function is missing, which means we are running with MIR missing for libstd (or other dependencies).
- // Still, we can make many things mostly work by "emulating" or ignoring some functions.
- "std::io::_print" |
- "std::io::_eprint" => {
- warn!(
- "Ignoring output. To run programs that prints, make sure you have a libstd with full MIR."
- );
- }
- "std::thread::Builder::new" => {
- return err!(Unimplemented("miri does not support threading".to_owned()))
- }
- "std::env::args" => {
- return err!(Unimplemented(
- "miri does not support program arguments".to_owned(),
- ))
- }
- "std::panicking::panicking" |
- "std::rt::panicking" => {
- // we abort on panic -> `std::rt::panicking` always returns false
- self.write_scalar(Scalar::from_bool(false), dest)?;
- }
-
- _ => return err!(NoMirFor(path)),
- }
-
- self.goto_block(ret)?;
- self.dump_place(*dest);
- Ok(())
- }
-
fn write_null(&mut self, dest: PlaceTy<'tcx, Borrow>) -> EvalResult<'tcx> {
self.write_scalar(Scalar::from_int(0, dest.layout.size), dest)
}
));
}
- let libstd_has_mir = {
- let rustc_panic = ecx.resolve_path(&["std", "panicking", "rust_panic"])?;
- ecx.load_mir(rustc_panic.def).is_ok()
- };
-
- if libstd_has_mir {
- let start_id = tcx.lang_items().start_fn().unwrap();
- let main_ret_ty = tcx.fn_sig(main_id).output();
- let main_ret_ty = main_ret_ty.no_bound_vars().unwrap();
- let start_instance = ty::Instance::resolve(
- ecx.tcx.tcx,
- ty::ParamEnv::reveal_all(),
- start_id,
- ecx.tcx.mk_substs(
- ::std::iter::once(ty::subst::Kind::from(main_ret_ty)))
- ).unwrap();
- let start_mir = ecx.load_mir(start_instance.def)?;
-
- if start_mir.arg_count != 3 {
- return err!(AbiViolation(format!(
- "'start' lang item should have three arguments, but has {}",
- start_mir.arg_count
- )));
- }
-
- // Return value (in static memory so that it does not count as leak)
- let ret = ecx.layout_of(start_mir.return_ty())?;
- let ret_ptr = ecx.allocate(ret, MiriMemoryKind::MutStatic.into())?;
-
- // Push our stack frame
- ecx.push_stack_frame(
- start_instance,
- DUMMY_SP, // there is no call site, we want no span
- start_mir,
- Some(ret_ptr.into()),
- StackPopCleanup::None { cleanup: true },
- )?;
-
- let mut args = ecx.frame().mir.args_iter();
-
- // First argument: pointer to main()
- let main_ptr = ecx.memory_mut().create_fn_alloc(main_instance).with_default_tag();
- let dest = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?;
- ecx.write_scalar(Scalar::Ptr(main_ptr), dest)?;
-
- // Second argument (argc): 1
- let dest = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?;
- ecx.write_scalar(Scalar::from_int(1, dest.layout.size), dest)?;
-
- // FIXME: extract main source file path
- // Third argument (argv): &[b"foo"]
- let dest = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?;
- let foo = ecx.memory_mut().allocate_static_bytes(b"foo\0").with_default_tag();
- let foo_ty = ecx.tcx.mk_imm_ptr(ecx.tcx.types.u8);
- let foo_layout = ecx.layout_of(foo_ty)?;
- let foo_place = ecx.allocate(foo_layout, MiriMemoryKind::Env.into())?;
- ecx.write_scalar(Scalar::Ptr(foo), foo_place.into())?;
- ecx.memory_mut().mark_immutable(foo_place.to_ptr()?.alloc_id)?;
- ecx.write_scalar(foo_place.ptr, dest)?;
-
- assert!(args.next().is_none(), "start lang item has more arguments than expected");
- } else {
- let ret_place = MPlaceTy::dangling(ecx.layout_of(tcx.mk_unit())?, &ecx).into();
- ecx.push_stack_frame(
- main_instance,
- DUMMY_SP, // there is no call site, we want no span
- main_mir,
- Some(ret_place),
- StackPopCleanup::None { cleanup: true },
- )?;
-
- // No arguments
- let mut args = ecx.frame().mir.args_iter();
- assert!(args.next().is_none(), "main function must not have arguments");
+ let start_id = tcx.lang_items().start_fn().unwrap();
+ let main_ret_ty = tcx.fn_sig(main_id).output();
+ let main_ret_ty = main_ret_ty.no_bound_vars().unwrap();
+ let start_instance = ty::Instance::resolve(
+ ecx.tcx.tcx,
+ ty::ParamEnv::reveal_all(),
+ start_id,
+ ecx.tcx.mk_substs(
+ ::std::iter::once(ty::subst::Kind::from(main_ret_ty)))
+ ).unwrap();
+ let start_mir = ecx.load_mir(start_instance.def)?;
+
+ if start_mir.arg_count != 3 {
+ return err!(AbiViolation(format!(
+ "'start' lang item should have three arguments, but has {}",
+ start_mir.arg_count
+ )));
}
+ // Return value (in static memory so that it does not count as leak)
+ let ret = ecx.layout_of(start_mir.return_ty())?;
+ let ret_ptr = ecx.allocate(ret, MiriMemoryKind::MutStatic.into())?;
+
+ // Push our stack frame
+ ecx.push_stack_frame(
+ start_instance,
+ DUMMY_SP, // there is no call site, we want no span
+ start_mir,
+ Some(ret_ptr.into()),
+ StackPopCleanup::None { cleanup: true },
+ )?;
+
+ let mut args = ecx.frame().mir.args_iter();
+
+ // First argument: pointer to main()
+ let main_ptr = ecx.memory_mut().create_fn_alloc(main_instance).with_default_tag();
+ let dest = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?;
+ ecx.write_scalar(Scalar::Ptr(main_ptr), dest)?;
+
+ // Second argument (argc): 1
+ let dest = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?;
+ ecx.write_scalar(Scalar::from_int(1, dest.layout.size), dest)?;
+
+ // FIXME: extract main source file path
+ // Third argument (argv): &[b"foo"]
+ let dest = ecx.eval_place(&mir::Place::Local(args.next().unwrap()))?;
+ let foo = ecx.memory_mut().allocate_static_bytes(b"foo\0").with_default_tag();
+ let foo_ty = ecx.tcx.mk_imm_ptr(ecx.tcx.types.u8);
+ let foo_layout = ecx.layout_of(foo_ty)?;
+ let foo_place = ecx.allocate(foo_layout, MiriMemoryKind::Env.into())?;
+ ecx.write_scalar(Scalar::Ptr(foo), foo_place.into())?;
+ ecx.memory_mut().mark_immutable(foo_place.to_ptr()?.alloc_id)?;
+ ecx.write_scalar(foo_place.ptr, dest)?;
+
+ assert!(args.next().is_none(), "start lang item has more arguments than expected");
+
Ok(ecx)
}