diff --git a/miri/missing_fns.rs b/miri/missing_fns.rs deleted file mode 100644 index 6d1e5a505f4b..000000000000 --- a/miri/missing_fns.rs +++ /dev/null @@ -1,518 +0,0 @@ -use rustc::ty::{self, Ty}; -use rustc::hir::def_id::{DefId, CRATE_DEF_INDEX}; -use rustc::mir; -use syntax::attr; -use syntax::abi::Abi; - -use std::mem; - -use rustc_miri::interpret::*; - -use super::{ - TlsKey, - EvalContext, - MemoryExt, - Evaluator, -}; - -pub trait EvalContextExt<'tcx> { - fn call_c_abi( - &mut self, - def_id: DefId, - arg_operands: &[mir::Operand<'tcx>], - dest: Lvalue<'tcx>, - dest_ty: Ty<'tcx>, - dest_block: mir::BasicBlock, - ) -> EvalResult<'tcx>; - - fn resolve_path(&self, path: &[&str]) -> EvalResult<'tcx, ty::Instance<'tcx>>; - - 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>; -} - -impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, Evaluator> { - fn call_c_abi( - &mut self, - def_id: DefId, - arg_operands: &[mir::Operand<'tcx>], - dest: Lvalue<'tcx>, - dest_ty: Ty<'tcx>, - 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(); - - let args_res: EvalResult> = arg_operands.iter() - .map(|arg| self.eval_operand(arg)) - .collect(); - let args = args_res?; - - let usize = self.tcx.types.usize; - - match &link_name[..] { - "malloc" => { - let size = self.value_to_primval(args[0], usize)?.to_u64()?; - if size == 0 { - self.write_null(dest, dest_ty)?; - } else { - let align = self.memory.pointer_size(); - let ptr = self.memory.allocate(size, align, Kind::C)?; - self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; - } - } - - "free" => { - let ptr = args[0].into_ptr(&mut self.memory)?; - if !ptr.is_null()? { - self.memory.deallocate(ptr.to_ptr()?, None, Kind::C)?; - } - } - - "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" => { - let _handle = args[0].into_ptr(&mut self.memory)?; - let symbol = args[1].into_ptr(&mut 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_maybe_catch_panic" => { - // fn __rust_maybe_catch_panic(f: fn(*mut u8), data: *mut u8, data_ptr: *mut usize, vtable_ptr: *mut usize) -> u32 - // 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); - let f = args[0].into_ptr(&mut self.memory)?.to_ptr()?; - let data = args[1].into_ptr(&mut self.memory)?; - let f_instance = self.memory.get_fn(f)?; - self.write_null(dest, dest_ty)?; - - // 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. - let mir = self.load_mir(f_instance.def)?; - self.push_stack_frame( - f_instance, - mir.span, - mir, - Lvalue::undef(), - 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()))?; - let arg_dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?; - self.write_ptr(arg_dest, data, u8_ptr_ty)?; - - // We ourselves return 0 - self.write_null(dest, dest_ty)?; - - // Don't fall through - return Ok(()); - } - - "__rust_start_panic" => { - return Err(EvalError::Panic); - } - - "memcmp" => { - let left = args[0].into_ptr(&mut self.memory)?; - let right = args[1].into_ptr(&mut 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) { - Less => -1i8, - Equal => 0, - Greater => 1, - } - }; - - self.write_primval(dest, PrimVal::Bytes(result as u128), dest_ty)?; - } - - "memrchr" => { - let ptr = args[0].into_ptr(&mut 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().rev().position(|&c| c == val) { - let new_ptr = ptr.offset(num - idx as u64 - 1, &self)?; - self.write_ptr(dest, new_ptr, dest_ty)?; - } else { - self.write_null(dest, dest_ty)?; - } - } - - "memchr" => { - let ptr = args[0].into_ptr(&mut 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)?; - self.write_ptr(dest, new_ptr, dest_ty)?; - } else { - self.write_null(dest, dest_ty)?; - } - } - - "getenv" => { - let result = { - let name_ptr = args[0].into_ptr(&mut self.memory)?.to_ptr()?; - let name = self.memory.read_c_str(name_ptr)?; - match self.machine_data.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].into_ptr(&mut 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.machine_data.env_vars.remove(name)); - } - } - } - if let Some(old) = success { - if let Some(var) = old { - self.memory.deallocate(var, None, Kind::Env)?; - } - self.write_null(dest, dest_ty)?; - } else { - self.write_primval(dest, PrimVal::from_i128(-1), dest_ty)?; - } - } - - "setenv" => { - let mut new = None; - { - let name_ptr = args[0].into_ptr(&mut self.memory)?; - let value_ptr = args[1].into_ptr(&mut 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, Kind::Env)?; - self.memory.write_bytes(value_copy.into(), &value)?; - let trailing_zero_ptr = value_copy.offset(value.len() as u64, &self)?.into(); - self.memory.write_bytes(trailing_zero_ptr, &[0])?; - if let Some(var) = self.machine_data.env_vars.insert(name.to_owned(), value_copy) { - self.memory.deallocate(var, None, Kind::Env)?; - } - self.write_null(dest, dest_ty)?; - } else { - self.write_primval(dest, PrimVal::from_i128(-1), dest_ty)?; - } - } - - "write" => { - let fd = self.value_to_primval(args[0], usize)?.to_u64()?; - let buf = args[1].into_ptr(&mut self.memory)?; - let n = self.value_to_primval(args[2], usize)?.to_u64()?; - trace!("Called write({:?}, {:?}, {:?})", fd, buf, n); - let result = if fd == 1 || fd == 2 { // stdout/stderr - use std::io::{self, Write}; - - let buf_cont = self.memory.read_bytes(buf, n)?; - 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)?; - } - - "strlen" => { - let ptr = args[0].into_ptr(&mut 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); - // cache the sysconf integers via miri's global cache - let paths = &[ - (&["libc", "_SC_PAGESIZE"], PrimVal::Bytes(4096)), - (&["libc", "_SC_GETPW_R_SIZE_MAX"], PrimVal::from_i128(-1)), - ]; - let mut result = None; - for &(path, path_value) in paths { - if let Ok(instance) = self.resolve_path(path) { - let cid = GlobalId { instance, promoted: None }; - // compute global if not cached - let val = match self.globals.get(&cid).map(|glob| glob.value) { - Some(value) => self.value_to_primval(value, usize)?.to_u64()?, - None => eval_body_as_primval(self.tcx, instance)?.0.to_u64()?, - }; - if val == name { - result = Some(path_value); - break; - } - } - } - if let Some(result) = result { - self.write_primval(dest, result, dest_ty)?; - } else { - return Err(EvalError::Unimplemented(format!("Unimplemented sysconf name: {}", name))); - } - } - - // Hook pthread calls that go to the thread-local storage memory subsystem - "pthread_key_create" => { - let key_ptr = args[0].into_ptr(&mut self.memory)?; - - // Extract the function type out of the signature (that seems easier than constructing it ourselves...) - let dtor = match args[1].into_ptr(&mut self.memory)?.into_inner_primval() { - PrimVal::Ptr(dtor_ptr) => Some(self.memory.get_fn(dtor_ptr)?), - PrimVal::Bytes(0) => None, - PrimVal::Bytes(_) => return Err(EvalError::ReadBytesAsPointer), - PrimVal::Undef => return Err(EvalError::ReadUndefBytes), - }; - - // 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) - }; - - // 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) { - return Err(EvalError::OutOfTls); - } - // TODO: Does this need checking for alignment? - self.memory.write_uint(key_ptr.to_ptr()?, key, key_size.bytes())?; - - // Return success (0) - self.write_null(dest, 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_null(dest, 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)?; - self.write_ptr(dest, ptr, dest_ty)?; - } - "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].into_ptr(&mut self.memory)?; - self.memory.store_tls(key, new_ptr)?; - - // Return success (0) - self.write_null(dest, 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_null(dest, 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. - self.dump_local(dest); - self.goto_block(dest_block); - Ok(()) - } - - fn resolve_path(&self, path: &[&str]) -> EvalResult<'tcx, ty::Instance<'tcx>> { - let cstore = &self.tcx.sess.cstore; - let crates = cstore.crates(); - crates.iter() - .find(|&&krate| cstore.crate_name(krate) == path[0]) - .and_then(|krate| { - let krate = DefId { - krate: *krate, - index: CRATE_DEF_INDEX, - }; - let mut items = cstore.item_children(krate, self.tcx.sess); - let mut path_it = path.iter().skip(1).peekable(); - - while let Some(segment) = path_it.next() { - for item in &mem::replace(&mut items, vec![]) { - if item.ident.name == *segment { - if path_it.peek().is_none() { - return Some(ty::Instance::mono(self.tcx, item.def.def_id())); - } - - items = cstore.item_children(item.def.def_id(), self.tcx.sess); - break; - } - } - } - None - }) - .ok_or_else(|| { - let path = path.iter() - .map(|&s| s.to_owned()) - .collect(); - EvalError::PathNotFound(path) - }) - } - - 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> { - // 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" | - "std::rt::begin_panic_fmt" => return Err(EvalError::Panic), - _ => {}, - } - - let dest_ty = sig.output(); - let (dest, dest_block) = destination.ok_or_else(|| EvalError::NoMirFor(path.clone()))?; - - if sig.abi == Abi::C { - // An external C function - // TODO: That functions actually has a similar preamble to what follows here. May make sense to - // unify these two mechanisms for "hooking into missing functions". - self.call_c_abi(instance.def_id(), arg_operands, dest, dest_ty, dest_block)?; - return Ok(()); - } - - let args_res: EvalResult> = arg_operands.iter() - .map(|arg| self.eval_operand(arg)) - .collect(); - let args = args_res?; - - let usize = self.tcx.types.usize; - - match &path[..] { - // Allocators are magic. They have no MIR, even when the rest of libstd does. - "alloc::heap::::__rust_alloc" => { - let size = self.value_to_primval(args[0], usize)?.to_u64()?; - let align = self.value_to_primval(args[1], usize)?.to_u64()?; - 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, Kind::Rust)?; - self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; - } - "alloc::heap::::__rust_alloc_zeroed" => { - let size = self.value_to_primval(args[0], usize)?.to_u64()?; - let align = self.value_to_primval(args[1], usize)?.to_u64()?; - 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, Kind::Rust)?; - self.memory.write_repeat(ptr.into(), 0, size)?; - self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?; - } - "alloc::heap::::__rust_dealloc" => { - let ptr = args[0].into_ptr(&mut self.memory)?.to_ptr()?; - 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)); - } - self.memory.deallocate(ptr, Some((old_size, align)), Kind::Rust)?; - } - "alloc::heap::::__rust_realloc" => { - let ptr = args[0].into_ptr(&mut self.memory)?.to_ptr()?; - let old_size = self.value_to_primval(args[1], usize)?.to_u64()?; - let old_align = self.value_to_primval(args[2], usize)?.to_u64()?; - let new_size = self.value_to_primval(args[3], usize)?.to_u64()?; - let new_align = self.value_to_primval(args[4], usize)?.to_u64()?; - if old_size == 0 || new_size == 0 { - return Err(EvalError::HeapAllocZeroBytes); - } - if !old_align.is_power_of_two() { - return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(old_align)); - } - if !new_align.is_power_of_two() { - return Err(EvalError::HeapAllocNonPowerOfTwoAlignment(new_align)); - } - let new_ptr = self.memory.reallocate(ptr, old_size, old_align, new_size, new_align, Kind::Rust)?; - self.write_primval(dest, PrimVal::Ptr(new_ptr), dest_ty)?; - } - - // 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" => { - trace!("Ignoring output. To run programs that print, make sure you have a libstd with full MIR."); - } - "std::thread::Builder::new" => return Err(EvalError::Unimplemented("miri does not support threading".to_owned())), - "std::env::args" => return Err(EvalError::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 - let bool = self.tcx.types.bool; - self.write_primval(dest, PrimVal::from_bool(false), bool)?; - } - _ => return Err(EvalError::NoMirFor(path)), - } - - // 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. - self.dump_local(dest); - self.goto_block(dest_block); - return Ok(()); - } -}