rust/src/terminator/mod.rs

923 lines
42 KiB
Rust
Raw Normal View History

use rustc::hir::def_id::DefId;
use rustc::mir;
use rustc::ty::{self, TypeVariants, Ty};
2017-03-22 13:13:52 +01:00
use rustc::ty::layout::Layout;
use syntax::codemap::Span;
use syntax::attr;
use syntax::abi::Abi;
use error::{EvalError, EvalResult};
2017-02-09 18:12:59 +01:00
use eval_context::{EvalContext, IntegerExt, StackPopCleanup, is_inhabited};
use lvalue::Lvalue;
2017-05-25 16:40:13 -07:00
use memory::{Pointer, TlsKey};
2016-12-10 16:23:07 -08:00
use value::PrimVal;
use value::Value;
2017-03-23 13:36:13 +01:00
use rustc_data_structures::indexed_vec::Idx;
mod drop;
mod intrinsic;
2016-09-20 16:05:30 +02:00
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
pub(super) fn goto_block(&mut self, target: mir::BasicBlock) {
self.frame_mut().block = target;
self.frame_mut().stmt = 0;
}
pub(super) fn eval_terminator(
&mut self,
terminator: &mir::Terminator<'tcx>,
) -> EvalResult<'tcx> {
use rustc::mir::TerminatorKind::*;
match terminator.kind {
2016-11-26 19:13:22 -08:00
Return => {
2017-06-19 13:29:04 +02:00
self.dump_local(self.frame().return_lvalue);
2016-11-26 19:13:22 -08:00
self.pop_stack_frame()?
}
Goto { target } => self.goto_block(target),
SwitchInt { ref discr, ref values, ref targets, .. } => {
2017-06-28 09:40:25 +02:00
if self.const_env() {
return Err(EvalError::NeedsRfc("branching (if, match, loop, ...)".to_string()));
}
2017-02-24 10:39:55 +01:00
let discr_val = self.eval_operand(discr)?;
let discr_ty = self.operand_ty(discr);
2016-09-27 11:10:25 +02:00
let discr_prim = self.value_to_primval(discr_val, discr_ty)?;
// Branch to the `otherwise` case by default, if no match is found.
let mut target_block = targets[targets.len() - 1];
2017-02-24 10:39:55 +01:00
for (index, const_int) in values.iter().enumerate() {
let prim = PrimVal::Bytes(const_int.to_u128_unchecked());
if discr_prim.to_bytes()? == prim.to_bytes()? {
target_block = targets[index];
break;
}
}
self.goto_block(target_block);
}
Call { ref func, ref args, ref destination, .. } => {
let destination = match *destination {
Some((ref lv, target)) => Some((self.eval_lvalue(lv)?, target)),
None => None,
};
let func_ty = self.operand_ty(func);
2017-03-22 14:19:29 +01:00
let (fn_def, sig) = match func_ty.sty {
2017-03-22 13:13:52 +01:00
ty::TyFnPtr(sig) => {
let fn_ptr = self.eval_operand_to_primval(func)?.to_ptr()?;
let instance = self.memory.get_fn(fn_ptr)?;
let instance_ty = instance.def.def_ty(self.tcx);
let instance_ty = self.monomorphize(instance_ty, instance.substs);
match instance_ty.sty {
2017-03-23 15:17:02 +01:00
ty::TyFnDef(_, _, real_sig) => {
let sig = self.erase_lifetimes(&sig);
let real_sig = self.erase_lifetimes(&real_sig);
if !self.check_sig_compat(sig, real_sig)? {
return Err(EvalError::FunctionPointerTyMismatch(real_sig, sig));
2017-03-23 15:17:02 +01:00
}
},
ref other => bug!("instance def ty: {:?}", other),
}
(instance, sig)
},
2017-03-22 14:19:29 +01:00
ty::TyFnDef(def_id, substs, sig) => (::eval_context::resolve(self.tcx, def_id, substs), sig),
2016-11-26 22:58:01 -08:00
_ => {
let msg = format!("can't handle callee of type {:?}", func_ty);
return Err(EvalError::Unimplemented(msg));
}
};
2017-03-22 14:19:29 +01:00
let sig = self.erase_lifetimes(&sig);
self.eval_fn_call(fn_def, destination, args, terminator.source_info.span, sig)?;
}
2017-03-22 13:13:52 +01:00
Drop { ref location, target, .. } => {
2017-03-22 17:32:20 +01:00
trace!("TerminatorKind::drop: {:?}, {:?}", location, self.substs());
2017-06-28 09:40:25 +02:00
if self.const_env() {
return Err(EvalError::NeedsRfc("invoking `Drop::drop`".to_string()));
}
2017-03-22 13:13:52 +01:00
let lval = self.eval_lvalue(location)?;
let ty = self.lvalue_ty(location);
2017-03-22 17:32:20 +01:00
self.goto_block(target);
2017-03-22 16:16:23 +01:00
let ty = ::eval_context::apply_param_substs(self.tcx, self.substs(), &ty);
2017-03-22 13:13:52 +01:00
let instance = ::eval_context::resolve_drop_in_place(self.tcx, ty);
self.drop_lvalue(lval, instance, ty, terminator.source_info.span)?;
2017-03-22 13:13:52 +01:00
}
Assert { ref cond, expected, ref msg, target, .. } => {
let cond_val = self.eval_operand_to_primval(cond)?.to_bool()?;
if expected == cond_val {
self.goto_block(target);
} else {
return match *msg {
mir::AssertMessage::BoundsCheck { ref len, ref index } => {
let span = terminator.source_info.span;
2016-11-26 22:58:01 -08:00
let len = self.eval_operand_to_primval(len)
.expect("can't eval len")
.to_u64()?;
let index = self.eval_operand_to_primval(index)
.expect("can't eval index")
.to_u64()?;
Err(EvalError::ArrayIndexOutOfBounds(span, len, index))
},
mir::AssertMessage::Math(ref err) =>
Err(EvalError::Math(terminator.source_info.span, err.clone())),
}
}
},
DropAndReplace { .. } => unimplemented!(),
Resume => unimplemented!(),
Unreachable => return Err(EvalError::Unreachable),
}
Ok(())
}
2017-05-30 10:24:37 -07:00
/// Decides whether it is okay to call the method with signature `real_sig` using signature `sig`.
/// FIXME: This should take into account the platform-dependent ABI description.
fn check_sig_compat(
&mut self,
sig: ty::FnSig<'tcx>,
real_sig: ty::FnSig<'tcx>,
) -> EvalResult<'tcx, bool> {
fn check_ty_compat<'tcx>(
ty: ty::Ty<'tcx>,
real_ty: ty::Ty<'tcx>,
) -> bool {
if ty == real_ty { return true; } // This is actually a fast pointer comparison
return match (&ty.sty, &real_ty.sty) {
// Permit changing the pointer type of raw pointers and references as well as
// mutability of raw pointers.
// TODO: Should not be allowed when fat pointers are involved.
(&TypeVariants::TyRawPtr(_), &TypeVariants::TyRawPtr(_)) => true,
(&TypeVariants::TyRef(_, _), &TypeVariants::TyRef(_, _)) =>
ty.is_mutable_pointer() == real_ty.is_mutable_pointer(),
// rule out everything else
_ => false
}
}
if sig.abi == real_sig.abi &&
sig.variadic == real_sig.variadic &&
sig.inputs_and_output.len() == real_sig.inputs_and_output.len() &&
sig.inputs_and_output.iter().zip(real_sig.inputs_and_output).all(|(ty, real_ty)| check_ty_compat(ty, real_ty)) {
// Definitely good.
return Ok(true);
}
if sig.variadic || real_sig.variadic {
// We're not touching this
return Ok(false);
}
// We need to allow what comes up when a non-capturing closure is cast to a fn().
match (sig.abi, real_sig.abi) {
(Abi::Rust, Abi::RustCall) // check the ABIs. This makes the test here non-symmetric.
if check_ty_compat(sig.output(), real_sig.output()) && real_sig.inputs_and_output.len() == 3 => {
// First argument of real_sig must be a ZST
let fst_ty = real_sig.inputs_and_output[0];
let layout = self.type_layout(fst_ty)?;
let size = layout.size(&self.tcx.data_layout).bytes();
if size == 0 {
// Second argument must be a tuple matching the argument list of sig
let snd_ty = real_sig.inputs_and_output[1];
match snd_ty.sty {
TypeVariants::TyTuple(tys, _) if sig.inputs().len() == tys.len() =>
if sig.inputs().iter().zip(tys).all(|(ty, real_ty)| check_ty_compat(ty, real_ty)) {
return Ok(true)
},
_ => {}
}
}
}
_ => {}
};
// Nope, this doesn't work.
return Ok(false);
}
fn eval_fn_call(
&mut self,
2017-03-21 13:53:55 +01:00
instance: ty::Instance<'tcx>,
destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>,
2016-09-19 19:40:56 -06:00
arg_operands: &[mir::Operand<'tcx>],
span: Span,
2017-03-22 14:19:29 +01:00
sig: ty::FnSig<'tcx>,
) -> EvalResult<'tcx> {
2017-03-22 13:13:52 +01:00
trace!("eval_fn_call: {:#?}", instance);
match instance.def {
ty::InstanceDef::Intrinsic(..) => {
let (ret, target) = match destination {
2017-03-22 14:19:29 +01:00
Some(dest) => dest,
_ => return Err(EvalError::Unreachable),
};
2017-03-22 14:19:29 +01:00
let ty = sig.output();
if !is_inhabited(self.tcx, ty) {
return Err(EvalError::Unreachable);
}
let layout = self.type_layout(ty)?;
2017-03-21 13:53:55 +01:00
self.call_intrinsic(instance, arg_operands, ret, ty, layout, target)?;
self.dump_local(ret);
2017-03-22 14:19:29 +01:00
Ok(())
},
2017-03-22 13:13:52 +01:00
ty::InstanceDef::ClosureOnceShim{..} => {
2016-09-19 19:40:56 -06:00
let mut args = Vec::new();
for arg in arg_operands {
let arg_val = self.eval_operand(arg)?;
let arg_ty = self.operand_ty(arg);
args.push((arg_val, arg_ty));
}
if self.eval_fn_call_inner(
2017-03-22 13:13:52 +01:00
instance,
destination,
arg_operands,
2017-03-22 13:13:52 +01:00
span,
sig,
)? {
return Ok(());
}
2017-03-23 13:36:13 +01:00
let mut arg_locals = self.frame().mir.args_iter();
match sig.abi {
// closure as closure once
Abi::RustCall => {
for (arg_local, (arg_val, arg_ty)) in arg_locals.zip(args) {
let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?;
self.write_value(arg_val, dest, arg_ty)?;
}
},
// non capture closure as fn ptr
// need to inject zst ptr for closure object (aka do nothing)
// and need to pack arguments
Abi::Rust => {
trace!("arg_locals: {:?}", self.frame().mir.args_iter().collect::<Vec<_>>());
trace!("arg_operands: {:?}", arg_operands);
let local = arg_locals.nth(1).unwrap();
for (i, (arg_val, arg_ty)) in args.into_iter().enumerate() {
let dest = self.eval_lvalue(&mir::Lvalue::Local(local).field(mir::Field::new(i), arg_ty))?;
self.write_value(arg_val, dest, arg_ty)?;
}
},
_ => bug!("bad ABI for ClosureOnceShim: {:?}", sig.abi),
}
Ok(())
2017-03-22 13:13:52 +01:00
}
ty::InstanceDef::Item(_) => {
2017-03-23 13:36:13 +01:00
let mut args = Vec::new();
for arg in arg_operands {
let arg_val = self.eval_operand(arg)?;
let arg_ty = self.operand_ty(arg);
args.push((arg_val, arg_ty));
}
2017-05-25 22:38:07 -07:00
// Push the stack frame, and potentially be entirely done if the call got hooked
if self.eval_fn_call_inner(
2017-03-21 13:53:55 +01:00
instance,
destination,
arg_operands,
span,
sig,
)? {
return Ok(());
}
2017-03-23 13:36:13 +01:00
2017-05-25 22:38:07 -07:00
// Pass the arguments
2017-03-23 13:36:13 +01:00
let mut arg_locals = self.frame().mir.args_iter();
2017-03-23 16:09:36 +01:00
trace!("ABI: {:?}", sig.abi);
trace!("arg_locals: {:?}", self.frame().mir.args_iter().collect::<Vec<_>>());
trace!("arg_operands: {:?}", arg_operands);
2017-03-23 13:36:13 +01:00
match sig.abi {
Abi::RustCall => {
assert_eq!(args.len(), 2);
{ // write first argument
let first_local = arg_locals.next().unwrap();
let dest = self.eval_lvalue(&mir::Lvalue::Local(first_local))?;
let (arg_val, arg_ty) = args.remove(0);
self.write_value(arg_val, dest, arg_ty)?;
}
// unpack and write all other args
let (arg_val, arg_ty) = args.remove(0);
let layout = self.type_layout(arg_ty)?;
if let (&ty::TyTuple(fields, _), &Layout::Univariant { ref variant, .. }) = (&arg_ty.sty, layout) {
2017-03-23 16:09:36 +01:00
trace!("fields: {:?}", fields);
2017-03-23 15:07:33 +01:00
if self.frame().mir.args_iter().count() == fields.len() + 1 {
let offsets = variant.offsets.iter().map(|s| s.bytes());
2017-03-23 16:09:36 +01:00
match arg_val {
Value::ByRef(ptr) => {
for ((offset, ty), arg_local) in offsets.zip(fields).zip(arg_locals) {
let arg = Value::ByRef(ptr.offset(offset, self.memory.layout)?);
2017-03-23 16:09:36 +01:00
let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?;
trace!("writing arg {:?} to {:?} (type: {})", arg, dest, ty);
self.write_value(arg, dest, ty)?;
}
},
Value::ByVal(PrimVal::Undef) => {},
other => {
assert_eq!(fields.len(), 1);
let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_locals.next().unwrap()))?;
self.write_value(other, dest, fields[0])?;
2017-03-23 15:07:33 +01:00
}
2017-03-23 13:36:13 +01:00
}
2017-03-23 15:07:33 +01:00
} else {
2017-03-23 16:09:36 +01:00
trace!("manual impl of rust-call ABI");
2017-03-23 15:07:33 +01:00
// called a manual impl of a rust-call function
let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_locals.next().unwrap()))?;
self.write_value(arg_val, dest, arg_ty)?;
2017-03-23 13:36:13 +01:00
}
} else {
bug!("rust-call ABI tuple argument was {:?}, {:?}", arg_ty, layout);
}
2017-05-30 10:24:37 -07:00
},
_ => {
for (arg_local, (arg_val, arg_ty)) in arg_locals.zip(args) {
let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?;
self.write_value(arg_val, dest, arg_ty)?;
}
2017-03-23 13:36:13 +01:00
}
}
Ok(())
},
ty::InstanceDef::DropGlue(..) => {
assert_eq!(arg_operands.len(), 1);
assert_eq!(sig.abi, Abi::Rust);
let val = self.eval_operand(&arg_operands[0])?;
let ty = self.operand_ty(&arg_operands[0]);
let (_, target) = destination.expect("diverging drop glue");
self.goto_block(target);
// FIXME: deduplicate these matches
let pointee_type = match ty.sty {
ty::TyRawPtr(ref tam) |
ty::TyRef(_, ref tam) => tam.ty,
2017-04-26 12:15:42 +02:00
ty::TyAdt(def, _) if def.is_box() => ty.boxed_ty(),
_ => bug!("can only deref pointer types"),
};
self.drop(val, instance, pointee_type, span)
2017-03-23 10:04:08 +01:00
},
ty::InstanceDef::FnPtrShim(..) => {
2017-03-23 13:36:13 +01:00
trace!("ABI: {}", sig.abi);
2017-03-23 10:04:08 +01:00
let mut args = Vec::new();
for arg in arg_operands {
let arg_val = self.eval_operand(arg)?;
let arg_ty = self.operand_ty(arg);
args.push((arg_val, arg_ty));
}
if self.eval_fn_call_inner(
2017-03-23 13:36:13 +01:00
instance,
destination,
arg_operands,
2017-03-23 13:36:13 +01:00
span,
sig,
)? {
return Ok(());
}
2017-03-23 13:36:13 +01:00
let arg_locals = self.frame().mir.args_iter();
2017-03-23 10:04:08 +01:00
match sig.abi {
Abi::Rust => {
args.remove(0);
},
Abi::RustCall => {},
_ => unimplemented!(),
2017-03-23 13:36:13 +01:00
};
for (arg_local, (arg_val, arg_ty)) in arg_locals.zip(args) {
let dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?;
self.write_value(arg_val, dest, arg_ty)?;
2017-03-23 10:04:08 +01:00
}
2017-03-23 13:36:13 +01:00
Ok(())
2017-03-23 14:24:02 +01:00
},
ty::InstanceDef::Virtual(_, idx) => {
let ptr_size = self.memory.pointer_size();
2017-03-23 14:57:11 +01:00
let (_, vtable) = self.eval_operand(&arg_operands[0])?.expect_ptr_vtable_pair(&self.memory)?;
let fn_ptr = self.memory.read_ptr(vtable.offset(ptr_size * (idx as u64 + 3), self.memory.layout)?)?;
let instance = self.memory.get_fn(fn_ptr.to_ptr()?)?;
let mut arg_operands = arg_operands.to_vec();
let ty = self.operand_ty(&arg_operands[0]);
let ty = self.get_field_ty(ty, 0)?;
match arg_operands[0] {
mir::Operand::Consume(ref mut lval) => *lval = lval.clone().field(mir::Field::new(0), ty),
_ => bug!("virtual call first arg cannot be a constant"),
}
2017-03-23 14:57:11 +01:00
// recurse with concrete function
self.eval_fn_call(
2017-03-23 14:24:02 +01:00
instance,
destination,
&arg_operands,
2017-03-23 14:24:02 +01:00
span,
2017-03-23 14:57:11 +01:00
sig,
)
2017-03-23 14:24:02 +01:00
},
}
}
/// Returns Ok(true) when the function was handled completely due to mir not being available
fn eval_fn_call_inner(
&mut self,
2017-03-21 13:53:55 +01:00
instance: ty::Instance<'tcx>,
destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>,
arg_operands: &[mir::Operand<'tcx>],
span: Span,
sig: ty::FnSig<'tcx>,
) -> EvalResult<'tcx, bool> {
2017-03-23 13:36:13 +01:00
trace!("eval_fn_call_inner: {:#?}, {:#?}", instance, destination);
2017-03-22 13:13:52 +01:00
// Only trait methods can have a Self parameter.
2017-03-21 13:53:55 +01:00
let mir = match self.load_mir(instance.def) {
Ok(mir) => mir,
Err(EvalError::NoMirFor(path)) => {
2017-06-28 09:40:25 +02:00
if self.const_env() {
return Err(EvalError::NeedsRfc(format!("calling extern function `{}`", path)));
}
self.call_missing_fn(instance, destination, arg_operands, sig, path)?;
return Ok(true);
},
Err(other) => return Err(other),
};
2017-06-28 09:40:25 +02:00
if self.const_env() && !self.tcx.is_const_fn(instance.def_id()) {
return Err(EvalError::NotConst(format!("calling non-const fn `{}`", instance)));
}
let (return_lvalue, return_to_block) = match destination {
2017-06-19 13:29:04 +02:00
Some((lvalue, block)) => (lvalue, StackPopCleanup::Goto(block)),
None => (Lvalue::undef(), StackPopCleanup::None),
};
self.push_stack_frame(
2017-03-21 13:53:55 +01:00
instance,
span,
mir,
return_lvalue,
return_to_block,
)?;
Ok(false)
}
2017-02-24 10:39:55 +01:00
pub fn read_discriminant_value(&self, adt_ptr: Pointer, adt_ty: Ty<'tcx>) -> EvalResult<'tcx, u128> {
use rustc::ty::layout::Layout::*;
let adt_layout = self.type_layout(adt_ty)?;
trace!("read_discriminant_value {:#?}", adt_layout);
let discr_val = match *adt_layout {
General { discr, .. } | CEnum { discr, signed: false, .. } => {
let discr_size = discr.size().bytes();
self.memory.read_uint(adt_ptr, discr_size)?
}
CEnum { discr, signed: true, .. } => {
let discr_size = discr.size().bytes();
2017-01-12 08:28:42 +01:00
self.memory.read_int(adt_ptr, discr_size)? as u128
}
RawNullablePointer { nndiscr, value } => {
let discr_size = value.size(&self.tcx.data_layout).bytes();
trace!("rawnullablepointer with size {}", discr_size);
2017-01-12 08:28:42 +01:00
self.read_nonnull_discriminant_value(adt_ptr, nndiscr as u128, discr_size)?
}
StructWrappedNullablePointer { nndiscr, ref discrfield, .. } => {
let (offset, ty) = self.nonnull_offset_and_ty(adt_ty, nndiscr, discrfield)?;
let nonnull = adt_ptr.offset(offset.bytes(), self.memory.layout)?;
trace!("struct wrapped nullable pointer type: {}", ty);
// only the pointer part of a fat pointer is used for this space optimization
let discr_size = self.type_size(ty)?.expect("bad StructWrappedNullablePointer discrfield");
2017-01-12 08:28:42 +01:00
self.read_nonnull_discriminant_value(nonnull, nndiscr as u128, discr_size)?
}
// The discriminant_value intrinsic returns 0 for non-sum types.
Array { .. } | FatPointer { .. } | Scalar { .. } | Univariant { .. } |
2016-09-07 10:12:15 +02:00
Vector { .. } | UntaggedUnion { .. } => 0,
};
Ok(discr_val)
}
2017-01-12 08:28:42 +01:00
fn read_nonnull_discriminant_value(&self, ptr: Pointer, nndiscr: u128, discr_size: u64) -> EvalResult<'tcx, u128> {
trace!("read_nonnull_discriminant_value: {:?}, {}, {}", ptr, nndiscr, discr_size);
let not_null = match self.memory.read_uint(ptr, discr_size) {
Ok(0) => false,
Ok(_) | Err(EvalError::ReadPointerAsBytes) => true,
Err(e) => return Err(e),
};
assert!(nndiscr == 0 || nndiscr == 1);
Ok(if not_null { nndiscr } else { 1 - nndiscr })
}
/// Returns Ok() when the function was handled, fail otherwise
fn call_missing_fn(
&mut self,
instance: ty::Instance<'tcx>,
destination: Option<(Lvalue<'tcx>, mir::BasicBlock)>,
arg_operands: &[mir::Operand<'tcx>],
sig: ty::FnSig<'tcx>,
path: String,
) -> EvalResult<'tcx> {
if sig.abi == Abi::C {
// An external C function
let ty = sig.output();
let (ret, target) = destination.unwrap();
2017-05-26 17:27:39 -07:00
self.call_c_abi(instance.def_id(), arg_operands, ret, ty, target)?;
return Ok(());
}
// 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.
match &path[..] {
"std::io::_print" => {
2017-05-30 10:24:37 -07:00
trace!("Ignoring output. To run programs that print, make sure you have a libstd with full MIR.");
self.goto_block(destination.unwrap().1);
Ok(())
},
"std::thread::Builder::new" => Err(EvalError::Unimplemented("miri does not support threading".to_owned())),
"std::env::args" => Err(EvalError::Unimplemented("miri does not support program arguments".to_owned())),
"std::panicking::rust_panic_with_hook" |
"std::rt::begin_panic_fmt" => Err(EvalError::Panic),
"std::panicking::panicking" |
"std::rt::panicking" => {
let (lval, block) = destination.expect("std::rt::panicking does not diverge");
// we abort on panic -> `std::rt::panicking` always returns false
let bool = self.tcx.types.bool;
self.write_primval(lval, PrimVal::from_bool(false), bool)?;
self.goto_block(block);
Ok(())
}
_ => Err(EvalError::NoMirFor(path)),
}
}
fn call_c_abi(
&mut self,
def_id: DefId,
2017-05-25 16:40:13 -07:00
arg_operands: &[mir::Operand<'tcx>],
dest: Lvalue<'tcx>,
2016-11-26 22:58:01 -08:00
dest_ty: Ty<'tcx>,
2017-05-26 17:27:39 -07:00
dest_block: mir::BasicBlock,
) -> EvalResult<'tcx> {
let name = self.tcx.item_name(def_id);
let attrs = self.tcx.get_attrs(def_id);
let link_name = attr::first_attr_value_str_by_name(&attrs, "link_name")
.unwrap_or(name)
.as_str();
2017-05-25 16:40:13 -07:00
let args_res: EvalResult<Vec<Value>> = arg_operands.iter()
.map(|arg| self.eval_operand(arg))
.collect();
let args = args_res?;
let usize = self.tcx.types.usize;
match &link_name[..] {
2017-06-21 15:51:42 +02:00
"malloc" => {
let size = self.value_to_primval(args[0], usize)?.to_u64()?;
if size == 0 {
self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?;
} else {
let align = self.memory.pointer_size();
let ptr = self.memory.allocate(size, align)?;
self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?;
}
2017-06-21 15:51:42 +02:00
}
"free" => {
let ptr = args[0].read_ptr(&self.memory)?;
2017-06-22 11:40:06 +02:00
if !ptr.is_null()? {
self.memory.deallocate(ptr.to_ptr()?)?;
}
2017-06-21 15:51:42 +02:00
}
"syscall" => {
match self.value_to_primval(args[0], usize)?.to_u64()? {
511 => return Err(EvalError::Unimplemented("miri does not support random number generators".to_owned())),
id => return Err(EvalError::Unimplemented(format!("miri does not support syscall id {}", id))),
}
}
"dlsym" => {
2017-06-22 11:33:39 -07:00
let _handle = args[0].read_ptr(&self.memory)?;
let symbol = args[1].read_ptr(&self.memory)?.to_ptr()?;
let symbol_name = self.memory.read_c_str(symbol)?;
let err = format!("bad c unicode symbol: {:?}", symbol_name);
let symbol_name = ::std::str::from_utf8(symbol_name).unwrap_or(&err);
return Err(EvalError::Unimplemented(format!("miri does not support dynamically loading libraries (requested symbol: {})", symbol_name)));
}
"__rust_allocate" => {
let size = self.value_to_primval(args[0], usize)?.to_u64()?;
let align = self.value_to_primval(args[1], usize)?.to_u64()?;
2017-06-23 12:55:49 +02:00
if size == 0 {
return Err(EvalError::HeapAllocZeroBytes);
}
if !align.is_power_of_two() {
return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align));
}
let ptr = self.memory.allocate(size, align)?;
self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?;
}
"__rust_allocate_zeroed" => {
let size = self.value_to_primval(args[0], usize)?.to_u64()?;
let align = self.value_to_primval(args[1], usize)?.to_u64()?;
2017-06-23 12:55:49 +02:00
if size == 0 {
return Err(EvalError::HeapAllocZeroBytes);
}
if !align.is_power_of_two() {
return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align));
}
let ptr = self.memory.allocate(size, align)?;
self.memory.write_repeat(ptr, 0, size)?;
self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?;
}
2016-11-03 17:32:06 +01:00
"__rust_deallocate" => {
2017-06-19 10:58:59 +02:00
let ptr = args[0].read_ptr(&self.memory)?.to_ptr()?;
2016-11-03 17:32:06 +01:00
// FIXME: insert sanity check for size and align?
2017-06-23 12:55:49 +02:00
let old_size = self.value_to_primval(args[1], usize)?.to_u64()?;
let align = self.value_to_primval(args[2], usize)?.to_u64()?;
if old_size == 0 {
return Err(EvalError::HeapAllocZeroBytes);
}
if !align.is_power_of_two() {
return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align));
}
2016-11-03 17:32:06 +01:00
self.memory.deallocate(ptr)?;
},
"__rust_reallocate" => {
2017-06-19 10:58:59 +02:00
let ptr = args[0].read_ptr(&self.memory)?.to_ptr()?;
let size = self.value_to_primval(args[2], usize)?.to_u64()?;
let align = self.value_to_primval(args[3], usize)?.to_u64()?;
2017-06-23 12:55:49 +02:00
if size == 0 {
return Err(EvalError::HeapAllocZeroBytes);
}
if !align.is_power_of_two() {
return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(align));
}
let new_ptr = self.memory.reallocate(ptr, size, align)?;
self.write_primval(dest, PrimVal::Ptr(new_ptr), dest_ty)?;
}
2017-05-26 17:27:39 -07:00
"__rust_maybe_catch_panic" => {
2017-05-30 10:24:37 -07:00
// fn __rust_maybe_catch_panic(f: fn(*mut u8), data: *mut u8, data_ptr: *mut usize, vtable_ptr: *mut usize) -> u32
2017-05-26 17:27:39 -07:00
// We abort on panic, so not much is going on here, but we still have to call the closure
let u8_ptr_ty = self.tcx.mk_mut_ptr(self.tcx.types.u8);
2017-06-19 10:58:59 +02:00
let f = args[0].read_ptr(&self.memory)?.to_ptr()?;
2017-05-30 10:24:37 -07:00
let data = args[1].read_ptr(&self.memory)?;
let f_instance = self.memory.get_fn(f)?;
2017-05-26 17:27:39 -07:00
self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?;
2017-05-30 10:24:37 -07:00
// Now we make a function call. TODO: Consider making this re-usable? EvalContext::step does sth. similar for the TLS dtors,
// and of course eval_main.
2017-05-26 17:27:39 -07:00
let mir = self.load_mir(f_instance.def)?;
self.push_stack_frame(
f_instance,
mir.span,
mir,
2017-06-23 12:55:49 +02:00
Lvalue::undef(),
2017-05-26 17:27:39 -07:00
StackPopCleanup::Goto(dest_block),
)?;
let arg_local = self.frame().mir.args_iter().next().ok_or(EvalError::AbiViolation("Argument to __rust_maybe_catch_panic does not take enough arguments.".to_owned()))?;
2017-05-30 10:24:37 -07:00
let arg_dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?;
2017-06-19 10:58:59 +02:00
self.write_primval(arg_dest, data, u8_ptr_ty)?;
2017-05-30 10:24:37 -07:00
// We ourselves return 0
2017-05-30 10:24:37 -07:00
self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?;
2017-05-26 17:27:39 -07:00
// Don't fall through
return Ok(());
}
"__rust_start_panic" => {
return Err(EvalError::Panic);
}
"memcmp" => {
2017-06-20 16:26:53 +02:00
let left = args[0].read_ptr(&self.memory)?;
let right = args[1].read_ptr(&self.memory)?;
let n = self.value_to_primval(args[2], usize)?.to_u64()?;
let result = {
let left_bytes = self.memory.read_bytes(left, n)?;
let right_bytes = self.memory.read_bytes(right, n)?;
use std::cmp::Ordering::*;
match left_bytes.cmp(right_bytes) {
2017-01-12 08:28:42 +01:00
Less => -1i8,
Equal => 0,
Greater => 1,
}
};
2017-01-12 08:28:42 +01:00
self.write_primval(dest, PrimVal::Bytes(result as u128), dest_ty)?;
}
2017-01-12 09:26:22 +01:00
"memrchr" => {
2017-06-20 16:26:53 +02:00
let ptr = args[0].read_ptr(&self.memory)?;
2017-01-12 09:30:18 +01:00
let val = self.value_to_primval(args[1], usize)?.to_u64()? as u8;
let num = self.value_to_primval(args[2], usize)?.to_u64()?;
2017-01-12 09:26:22 +01:00
if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().rev().position(|&c| c == val) {
let new_ptr = ptr.offset(num - idx as u64 - 1, self.memory.layout)?;
2017-06-20 16:26:53 +02:00
self.write_primval(dest, new_ptr, dest_ty)?;
2017-01-12 09:26:22 +01:00
} else {
2017-05-25 16:40:13 -07:00
self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?;
2017-01-12 09:26:22 +01:00
}
}
"memchr" => {
2017-06-20 16:26:53 +02:00
let ptr = args[0].read_ptr(&self.memory)?;
let val = self.value_to_primval(args[1], usize)?.to_u64()? as u8;
let num = self.value_to_primval(args[2], usize)?.to_u64()?;
if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().position(|&c| c == val) {
let new_ptr = ptr.offset(idx as u64, self.memory.layout)?;
2017-06-20 16:26:53 +02:00
self.write_primval(dest, new_ptr, dest_ty)?;
} else {
2017-05-25 16:40:13 -07:00
self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?;
}
}
"getenv" => {
2017-06-21 14:06:23 +02:00
let result = {
2017-06-19 10:58:59 +02:00
let name_ptr = args[0].read_ptr(&self.memory)?.to_ptr()?;
let name = self.memory.read_c_str(name_ptr)?;
2017-06-21 14:06:23 +02:00
match self.env_vars.get(name) {
Some(&var) => PrimVal::Ptr(var),
None => PrimVal::Bytes(0),
}
};
self.write_primval(dest, result, dest_ty)?;
}
"unsetenv" => {
let mut success = None;
{
let name_ptr = args[0].read_ptr(&self.memory)?;
if !name_ptr.is_null()? {
let name = self.memory.read_c_str(name_ptr.to_ptr()?)?;
if !name.is_empty() && !name.contains(&b'=') {
success = Some(self.env_vars.remove(name));
}
}
}
if let Some(old) = success {
if let Some(var) = old {
self.memory.deallocate(var)?;
}
self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?;
} else {
self.write_primval(dest, PrimVal::from_i128(-1), dest_ty)?;
}
}
"setenv" => {
let mut new = None;
{
let name_ptr = args[0].read_ptr(&self.memory)?;
let value_ptr = args[1].read_ptr(&self.memory)?.to_ptr()?;
let value = self.memory.read_c_str(value_ptr)?;
if !name_ptr.is_null()? {
let name = self.memory.read_c_str(name_ptr.to_ptr()?)?;
if !name.is_empty() && !name.contains(&b'=') {
new = Some((name.to_owned(), value.to_owned()));
}
}
}
if let Some((name, value)) = new {
// +1 for the null terminator
let value_copy = self.memory.allocate((value.len() + 1) as u64, 1)?;
self.memory.write_bytes(value_copy, &value)?;
self.memory.write_bytes(value_copy.offset(value.len() as u64, self.memory.layout)?, &[0])?;
if let Some(var) = self.env_vars.insert(name.to_owned(), value_copy) {
self.memory.deallocate(var)?;
}
self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?;
} else {
self.write_primval(dest, PrimVal::from_i128(-1), dest_ty)?;
}
}
2017-05-26 17:27:39 -07:00
"write" => {
let fd = self.value_to_primval(args[0], usize)?.to_u64()?;
2017-06-20 16:26:53 +02:00
let buf = args[1].read_ptr(&self.memory)?;
let n = self.value_to_primval(args[2], usize)?.to_u64()?;
trace!("Called write({:?}, {:?}, {:?})", fd, buf, n);
2017-05-24 21:01:13 -07:00
let result = if fd == 1 || fd == 2 { // stdout/stderr
use std::io::{self, Write};
let buf_cont = self.memory.read_bytes(buf, n)?;
2017-05-24 21:01:13 -07:00
let res = if fd == 1 { io::stdout().write(buf_cont) } else { io::stderr().write(buf_cont) };
match res { Ok(n) => n as isize, Err(_) => -1 }
} else {
info!("Ignored output to FD {}", fd);
n as isize // pretend it all went well
}; // now result is the value we return back to the program
self.write_primval(dest, PrimVal::Bytes(result as u128), dest_ty)?;
}
2017-06-21 14:06:23 +02:00
"strlen" => {
let ptr = args[0].read_ptr(&self.memory)?.to_ptr()?;
let n = self.memory.read_c_str(ptr)?.len();
self.write_primval(dest, PrimVal::Bytes(n as u128), dest_ty)?;
}
// Some things needed for sys::thread initialization to go through
"signal" | "sigaction" | "sigaltstack" => {
self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?;
}
"sysconf" => {
let name = self.value_to_primval(args[0], usize)?.to_u64()?;
trace!("sysconf() called with name {}", name);
let result = match name {
2017-06-21 14:06:23 +02:00
30 => PrimVal::Bytes(4096), // _SC_PAGESIZE
70 => PrimVal::from_i128(-1), // _SC_GETPW_R_SIZE_MAX
_ => return Err(EvalError::Unimplemented(format!("Unimplemented sysconf name: {}", name)))
};
2017-06-21 14:06:23 +02:00
self.write_primval(dest, result, dest_ty)?;
}
"mmap" => {
// This is a horrible hack, but well... the guard page mechanism calls mmap and expects a particular return value, so we give it that value
let addr = args[0].read_ptr(&self.memory)?;
2017-06-19 10:58:59 +02:00
self.write_primval(dest, addr, dest_ty)?;
}
2017-05-25 16:40:13 -07:00
// Hook pthread calls that go to the thread-local storage memory subsystem
"pthread_key_create" => {
let key_ptr = args[0].read_ptr(&self.memory)?;
2017-05-25 22:38:07 -07:00
// Extract the function type out of the signature (that seems easier than constructing it ourselves...)
2017-06-19 10:58:59 +02:00
let dtor = match args[1].read_ptr(&self.memory)? {
PrimVal::Ptr(dtor_ptr) => Some(self.memory.get_fn(dtor_ptr)?),
2017-06-19 10:58:59 +02:00
PrimVal::Bytes(0) => None,
PrimVal::Bytes(_) => return Err(EvalError::ReadBytesAsPointer),
PrimVal::Undef => return Err(EvalError::ReadUndefBytes),
};
2017-05-25 16:40:13 -07:00
// Figure out how large a pthread TLS key actually is. This is libc::pthread_key_t.
let key_type = self.operand_ty(&arg_operands[0]).builtin_deref(true, ty::LvaluePreference::NoPreference)
.ok_or(EvalError::AbiViolation("Wrong signature used for pthread_key_create: First argument must be a raw pointer.".to_owned()))?.ty;
let key_size = {
let layout = self.type_layout(key_type)?;
layout.size(&self.tcx.data_layout)
2017-05-25 16:40:13 -07:00
};
2017-05-25 22:38:07 -07:00
// Create key and write it into the memory where key_ptr wants it
let key = self.memory.create_tls_key(dtor) as u128;
if key_size.bits() < 128 && key >= (1u128 << key_size.bits() as u128) {
2017-05-25 16:40:13 -07:00
return Err(EvalError::OutOfTls);
}
// TODO: Does this need checking for alignment?
2017-06-19 10:58:59 +02:00
self.memory.write_uint(key_ptr.to_ptr()?, key, key_size.bytes())?;
2017-05-25 16:40:13 -07:00
// Return success (0)
self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?;
}
"pthread_key_delete" => {
// The conversion into TlsKey here is a little fishy, but should work as long as usize >= libc::pthread_key_t
let key = self.value_to_primval(args[0], usize)?.to_u64()? as TlsKey;
self.memory.delete_tls_key(key)?;
// Return success (0)
self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?;
}
"pthread_getspecific" => {
// The conversion into TlsKey here is a little fishy, but should work as long as usize >= libc::pthread_key_t
let key = self.value_to_primval(args[0], usize)?.to_u64()? as TlsKey;
let ptr = self.memory.load_tls(key)?;
2017-06-19 10:58:59 +02:00
self.write_primval(dest, ptr, dest_ty)?;
2017-05-25 16:40:13 -07:00
}
"pthread_setspecific" => {
// The conversion into TlsKey here is a little fishy, but should work as long as usize >= libc::pthread_key_t
let key = self.value_to_primval(args[0], usize)?.to_u64()? as TlsKey;
let new_ptr = args[1].read_ptr(&self.memory)?;
self.memory.store_tls(key, new_ptr)?;
// Return success (0)
self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?;
}
// Stub out all the other pthread calls to just return 0
link_name if link_name.starts_with("pthread_") => {
warn!("ignoring C ABI call: {}", link_name);
self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?;
},
_ => {
return Err(EvalError::Unimplemented(format!("can't call C ABI function: {}", link_name)));
}
}
// Since we pushed no stack frame, the main loop will act
// as if the call just completed and it's returning to the
// current frame.
2017-05-26 17:27:39 -07:00
self.dump_local(dest);
self.goto_block(dest_block);
Ok(())
2017-05-26 17:27:39 -07:00
}
2016-09-20 12:52:01 +02:00
}