diff --git a/src/helpers.rs b/src/helpers.rs index c7b7853388c..9107958e010 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -1,8 +1,11 @@ use std::mem; -use rustc::ty::{self, layout::{self, Size, Align}}; use rustc::hir::def_id::{DefId, CRATE_DEF_INDEX}; use rustc::mir; +use rustc::ty::{ + self, + layout::{self, Align, LayoutOf, Size, TyLayout}, +}; use rand::RngCore; @@ -304,4 +307,36 @@ fn eval_libc(&mut self, name: &str) -> InterpResult<'tcx, Scalar> { fn eval_libc_i32(&mut self, name: &str) -> InterpResult<'tcx, i32> { self.eval_libc(name)?.to_i32() } + + /// Helper function to get the `TyLayout` of a `libc` type + fn libc_ty_layout(&mut self, name: &str) -> InterpResult<'tcx, TyLayout<'tcx>> { + let this = self.eval_context_mut(); + let tcx = &{ this.tcx.tcx }; + let ty = this.resolve_path(&["libc", name])?.ty(*tcx); + this.layout_of(ty) + } + + // Writes several `ImmTy`s contiguosly into memory. This is useful when you have to pack + // different values into a struct. + fn write_packed_immediates( + &mut self, + place: &MPlaceTy<'tcx, Tag>, + imms: &[ImmTy<'tcx, Tag>], + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + + let tcx = &{ this.tcx.tcx }; + + let mut offset = Size::from_bytes(0); + + for &imm in imms { + this.write_immediate_to_mplace( + *imm, + place.offset(offset, None, imm.layout, tcx)?, + )?; + offset += imm.layout.size; + } + + Ok(()) + } } diff --git a/src/lib.rs b/src/lib.rs index baae26d91ad..06ec33a914b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,6 +32,7 @@ pub use crate::shims::foreign_items::EvalContextExt as ForeignItemsEvalContextExt; pub use crate::shims::intrinsics::EvalContextExt as IntrinsicsEvalContextExt; pub use crate::shims::tls::{EvalContextExt as TlsEvalContextExt, TlsData}; +pub use crate::shims::time::{EvalContextExt as TimeEvalContextExt}; pub use crate::shims::dlsym::{Dlsym, EvalContextExt as DlsymEvalContextExt}; pub use crate::shims::env::{EnvVars, EvalContextExt as EnvEvalContextExt}; pub use crate::shims::fs::{FileHandler, EvalContextExt as FileEvalContextExt}; diff --git a/src/shims/foreign_items.rs b/src/shims/foreign_items.rs index 00160156312..f6195961ba0 100644 --- a/src/shims/foreign_items.rs +++ b/src/shims/foreign_items.rs @@ -507,6 +507,16 @@ fn emulate_foreign_item( this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; } + "clock_gettime" => { + let result = this.clock_gettime(args[0], args[1])?; + this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; + } + + "gettimeofday" => { + let result = this.gettimeofday(args[0], args[1])?; + this.write_scalar(Scalar::from_int(result, dest.layout.size), dest)?; + } + "strlen" => { let ptr = this.read_scalar(args[0])?.not_undef()?; let n = this.memory().read_c_str(ptr)?.len(); diff --git a/src/shims/mod.rs b/src/shims/mod.rs index 6d80af46850..95bb8b70370 100644 --- a/src/shims/mod.rs +++ b/src/shims/mod.rs @@ -4,6 +4,7 @@ pub mod intrinsics; pub mod tls; pub mod fs; +pub mod time; use rustc::{mir, ty}; diff --git a/src/shims/time.rs b/src/shims/time.rs new file mode 100644 index 00000000000..0153c1a912d --- /dev/null +++ b/src/shims/time.rs @@ -0,0 +1,104 @@ +use std::time::{Duration, SystemTime}; + +use rustc::ty::layout::TyLayout; + +use crate::stacked_borrows::Tag; +use crate::*; + +// Returns the time elapsed between now and the unix epoch as a `Duration` and the sign of the time +// interval +fn get_time<'tcx>() -> InterpResult<'tcx, Duration> { + SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .map_err(|_| err_unsup_format!("Times before the Unix epoch are not supported").into()) +} + +fn int_to_immty_checked<'tcx>( + int: i128, + layout: TyLayout<'tcx>, +) -> InterpResult<'tcx, ImmTy<'tcx, Tag>> { + // If `int` does not fit in `size` bits, we error instead of letting + // `ImmTy::from_int` panic. + let size = layout.size; + let truncated = truncate(int as u128, size); + if sign_extend(truncated, size) as i128 != int { + throw_unsup_format!( + "Signed value {:#x} does not fit in {} bits", + int, + size.bits() + ) + } + Ok(ImmTy::from_int(int, layout)) +} + +impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} +pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { + // Foreign function used by linux + fn clock_gettime( + &mut self, + clk_id_op: OpTy<'tcx, Tag>, + tp_op: OpTy<'tcx, Tag>, + ) -> InterpResult<'tcx, i32> { + let this = self.eval_context_mut(); + + if !this.machine.communicate { + throw_unsup_format!("`clock_gettime` not available when isolation is enabled") + } + + let clk_id = this.read_scalar(clk_id_op)?.to_i32()?; + if clk_id != this.eval_libc_i32("CLOCK_REALTIME")? { + let einval = this.eval_libc("EINVAL")?; + this.set_last_error(einval)?; + return Ok(-1); + } + + let tp = this.deref_operand(tp_op)?; + + let duration = get_time()?; + let tv_sec = duration.as_secs() as i128; + let tv_nsec = duration.subsec_nanos() as i128; + + let imms = [ + int_to_immty_checked(tv_sec, this.libc_ty_layout("time_t")?)?, + int_to_immty_checked(tv_nsec, this.libc_ty_layout("c_long")?)?, + ]; + + this.write_packed_immediates(&tp, &imms)?; + + Ok(0) + } + // Foreign function used by generic unix (in particular macOS) + fn gettimeofday( + &mut self, + tv_op: OpTy<'tcx, Tag>, + tz_op: OpTy<'tcx, Tag>, + ) -> InterpResult<'tcx, i32> { + let this = self.eval_context_mut(); + + if !this.machine.communicate { + throw_unsup_format!("`gettimeofday` not available when isolation is enabled") + } + // Using tz is obsolete and should always be null + let tz = this.read_scalar(tz_op)?.not_undef()?; + if !this.is_null(tz)? { + let einval = this.eval_libc("EINVAL")?; + this.set_last_error(einval)?; + return Ok(-1); + } + + let tv = this.deref_operand(tv_op)?; + + let duration = get_time()?; + let tv_sec = duration.as_secs() as i128; + let tv_usec = duration.subsec_micros() as i128; + + let imms = [ + int_to_immty_checked(tv_sec, this.libc_ty_layout("time_t")?)?, + int_to_immty_checked(tv_usec, this.libc_ty_layout("suseconds_t")?)?, + ]; + + this.write_packed_immediates(&tv, &imms)?; + + Ok(0) + } +} diff --git a/tests/run-pass/clock.rs b/tests/run-pass/clock.rs new file mode 100644 index 00000000000..23f45f91ada --- /dev/null +++ b/tests/run-pass/clock.rs @@ -0,0 +1,8 @@ +// ignore-windows: TODO clock shims are not implemented on Windows +// compile-flags: -Zmiri-disable-isolation + +use std::time::SystemTime; + +fn main() { + let _now = SystemTime::now(); +}