From 47f8218d0dfcd9f31185f2628d07a993ba6780f5 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 12 Mar 2022 11:21:45 -0500 Subject: [PATCH] add write_int_fields to replace write_packed_immediates for stat, lookup fields by name --- src/helpers.rs | 122 +++++++++++++------------- src/shims/posix/fs.rs | 194 +++++++++++++++++++++--------------------- src/shims/time.rs | 35 ++------ 3 files changed, 169 insertions(+), 182 deletions(-) diff --git a/src/helpers.rs b/src/helpers.rs index 84792c2394e..ba12e0a7e39 100644 --- a/src/helpers.rs +++ b/src/helpers.rs @@ -57,8 +57,8 @@ fn resolve_path(&self, path: &[&str]) -> ty::Instance<'tcx> { /// Evaluates the scalar at the specified path. Returns Some(val) /// if the path could be resolved, and None otherwise - fn eval_path_scalar(&mut self, path: &[&str]) -> InterpResult<'tcx, Scalar> { - let this = self.eval_context_mut(); + fn eval_path_scalar(&self, path: &[&str]) -> InterpResult<'tcx, Scalar> { + let this = self.eval_context_ref(); let instance = this.resolve_path(path); let cid = GlobalId { instance, promoted: None }; let const_val = this.eval_to_allocation(cid)?; @@ -67,51 +67,98 @@ fn eval_path_scalar(&mut self, path: &[&str]) -> InterpResult<'tcx, Scalar> } /// Helper function to get a `libc` constant as a `Scalar`. - fn eval_libc(&mut self, name: &str) -> InterpResult<'tcx, Scalar> { - self.eval_context_mut().eval_path_scalar(&["libc", name]) + fn eval_libc(&self, name: &str) -> InterpResult<'tcx, Scalar> { + self.eval_path_scalar(&["libc", name]) } /// Helper function to get a `libc` constant as an `i32`. - fn eval_libc_i32(&mut self, name: &str) -> InterpResult<'tcx, i32> { + fn eval_libc_i32(&self, name: &str) -> InterpResult<'tcx, i32> { // TODO: Cache the result. self.eval_libc(name)?.to_i32() } /// Helper function to get a `windows` constant as a `Scalar`. - fn eval_windows(&mut self, module: &str, name: &str) -> InterpResult<'tcx, Scalar> { - self.eval_context_mut().eval_path_scalar(&["std", "sys", "windows", module, name]) + fn eval_windows(&self, module: &str, name: &str) -> InterpResult<'tcx, Scalar> { + self.eval_context_ref().eval_path_scalar(&["std", "sys", "windows", module, name]) } /// Helper function to get a `windows` constant as a `u64`. - fn eval_windows_u64(&mut self, module: &str, name: &str) -> InterpResult<'tcx, u64> { + fn eval_windows_u64(&self, module: &str, name: &str) -> InterpResult<'tcx, u64> { // TODO: Cache the result. self.eval_windows(module, name)?.to_u64() } /// Helper function to get the `TyAndLayout` of a `libc` type - fn libc_ty_layout(&mut self, name: &str) -> InterpResult<'tcx, TyAndLayout<'tcx>> { - let this = self.eval_context_mut(); + fn libc_ty_layout(&self, name: &str) -> InterpResult<'tcx, TyAndLayout<'tcx>> { + let this = self.eval_context_ref(); let ty = this.resolve_path(&["libc", name]).ty(*this.tcx, ty::ParamEnv::reveal_all()); this.layout_of(ty) } /// Helper function to get the `TyAndLayout` of a `windows` type - fn windows_ty_layout(&mut self, name: &str) -> InterpResult<'tcx, TyAndLayout<'tcx>> { - let this = self.eval_context_mut(); + fn windows_ty_layout(&self, name: &str) -> InterpResult<'tcx, TyAndLayout<'tcx>> { + let this = self.eval_context_ref(); let ty = this .resolve_path(&["std", "sys", "windows", "c", name]) .ty(*this.tcx, ty::ParamEnv::reveal_all()); this.layout_of(ty) } - /// Write a uint of the appropriate size to `dest`. - fn write_uint(&mut self, i: impl Into, dest: &PlaceTy<'tcx, Tag>) -> InterpResult<'tcx> { - self.eval_context_mut().write_scalar(Scalar::from_uint(i, dest.layout.size), dest) + /// Project to the given *named* field of the mplace (which must be a struct or union type). + fn mplace_field_named( + &self, + mplace: &MPlaceTy<'tcx, Tag>, + name: &str, + ) -> InterpResult<'tcx, MPlaceTy<'tcx, Tag>> { + let this = self.eval_context_ref(); + let adt = mplace.layout.ty.ty_adt_def().unwrap(); + for (idx, field) in adt.non_enum_variant().fields.iter().enumerate() { + if field.name.as_str() == name { + return this.mplace_field(mplace, idx); + } + } + bug!("No field named {} in type {}", name, mplace.layout.ty); } - /// Write an int of the appropriate size to `dest`. + /// Write an int of the appropriate size to `dest`. The target type may be signed or unsigned, + /// we try to do the right thing anyway. `i128` can fit all integer types except for `u128` so + /// this method is fine for almost all integer types. fn write_int(&mut self, i: impl Into, dest: &PlaceTy<'tcx, Tag>) -> InterpResult<'tcx> { - self.eval_context_mut().write_scalar(Scalar::from_int(i, dest.layout.size), dest) + assert!(dest.layout.abi.is_scalar(), "write_int on non-scalar type {}", dest.layout.ty); + let val = if dest.layout.abi.is_signed() { + Scalar::from_int(i, dest.layout.size) + } else { + Scalar::from_uint(u64::try_from(i.into()).unwrap(), dest.layout.size) + }; + self.eval_context_mut().write_scalar(val, dest) + } + + /// Write the first N fields of the given place. + fn write_int_fields( + &mut self, + values: &[i128], + dest: &MPlaceTy<'tcx, Tag>, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + for (idx, &val) in values.iter().enumerate() { + let field = this.mplace_field(dest, idx)?; + this.write_int(val, &field.into())?; + } + Ok(()) + } + + /// Write the given fields of the given place. + fn write_int_fields_named( + &mut self, + values: &[(&str, i128)], + dest: &MPlaceTy<'tcx, Tag>, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + for &(name, val) in values.iter() { + let field = this.mplace_field_named(dest, name)?; + this.write_int(val, &field.into())?; + } + Ok(()) } /// Write a 0 of the appropriate size to `dest`. @@ -383,27 +430,6 @@ fn visit_union( } } - // Writes several `ImmTy`s contiguously 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 mut offset = Size::from_bytes(0); - - for &imm in imms { - this.write_immediate( - *imm, - &place.offset(offset, MemPlaceMeta::None, imm.layout, &*this.tcx)?.into(), - )?; - offset += imm.layout.size; - } - Ok(()) - } - /// Helper function used inside the shims of foreign functions to check that isolation is /// disabled. It returns an error using the `name` of the foreign function if this is not the /// case. @@ -750,26 +776,6 @@ pub fn isolation_abort_error(name: &str) -> InterpResult<'static> { ))) } -pub fn immty_from_int_checked<'tcx>( - int: impl Into, - layout: TyAndLayout<'tcx>, -) -> InterpResult<'tcx, ImmTy<'tcx, Tag>> { - let int = int.into(); - Ok(ImmTy::try_from_int(int, layout).ok_or_else(|| { - err_unsup_format!("signed value {:#x} does not fit in {} bits", int, layout.size.bits()) - })?) -} - -pub fn immty_from_uint_checked<'tcx>( - int: impl Into, - layout: TyAndLayout<'tcx>, -) -> InterpResult<'tcx, ImmTy<'tcx, Tag>> { - let int = int.into(); - Ok(ImmTy::try_from_uint(int, layout).ok_or_else(|| { - err_unsup_format!("unsigned value {:#x} does not fit in {} bits", int, layout.size.bits()) - })?) -} - pub fn bool_to_simd_element(b: bool, size: Size) -> Scalar { // SIMD uses all-1 as pattern for "true" let val = if b { -1 } else { 0 }; diff --git a/src/shims/posix/fs.rs b/src/shims/posix/fs.rs index efb11b3bee9..b71f53cce56 100644 --- a/src/shims/posix/fs.rs +++ b/src/shims/posix/fs.rs @@ -15,7 +15,7 @@ use rustc_target::abi::{Align, Size}; use crate::*; -use helpers::{check_arg_count, immty_from_int_checked, immty_from_uint_checked}; +use helpers::check_arg_count; use shims::os_str::os_str_to_bytes; use shims::time::system_time_to_duration; @@ -318,45 +318,32 @@ fn macos_stat_write_buf( let (created_sec, created_nsec) = metadata.created.unwrap_or((0, 0)); let (modified_sec, modified_nsec) = metadata.modified.unwrap_or((0, 0)); - let dev_t_layout = this.libc_ty_layout("dev_t")?; - let mode_t_layout = this.libc_ty_layout("mode_t")?; - let nlink_t_layout = this.libc_ty_layout("nlink_t")?; - let ino_t_layout = this.libc_ty_layout("ino_t")?; - let uid_t_layout = this.libc_ty_layout("uid_t")?; - let gid_t_layout = this.libc_ty_layout("gid_t")?; - let time_t_layout = this.libc_ty_layout("time_t")?; - let long_layout = this.libc_ty_layout("c_long")?; - let off_t_layout = this.libc_ty_layout("off_t")?; - let blkcnt_t_layout = this.libc_ty_layout("blkcnt_t")?; - let blksize_t_layout = this.libc_ty_layout("blksize_t")?; - let uint32_t_layout = this.libc_ty_layout("uint32_t")?; - - let imms = [ - immty_from_uint_checked(0u128, dev_t_layout)?, // st_dev - immty_from_uint_checked(mode, mode_t_layout)?, // st_mode - immty_from_uint_checked(0u128, nlink_t_layout)?, // st_nlink - immty_from_uint_checked(0u128, ino_t_layout)?, // st_ino - immty_from_uint_checked(0u128, uid_t_layout)?, // st_uid - immty_from_uint_checked(0u128, gid_t_layout)?, // st_gid - immty_from_uint_checked(0u128, dev_t_layout)?, // st_rdev - immty_from_uint_checked(0u128, uint32_t_layout)?, // padding - immty_from_uint_checked(access_sec, time_t_layout)?, // st_atime - immty_from_uint_checked(access_nsec, long_layout)?, // st_atime_nsec - immty_from_uint_checked(modified_sec, time_t_layout)?, // st_mtime - immty_from_uint_checked(modified_nsec, long_layout)?, // st_mtime_nsec - immty_from_uint_checked(0u128, time_t_layout)?, // st_ctime - immty_from_uint_checked(0u128, long_layout)?, // st_ctime_nsec - immty_from_uint_checked(created_sec, time_t_layout)?, // st_birthtime - immty_from_uint_checked(created_nsec, long_layout)?, // st_birthtime_nsec - immty_from_uint_checked(metadata.size, off_t_layout)?, // st_size - immty_from_uint_checked(0u128, blkcnt_t_layout)?, // st_blocks - immty_from_uint_checked(0u128, blksize_t_layout)?, // st_blksize - immty_from_uint_checked(0u128, uint32_t_layout)?, // st_flags - immty_from_uint_checked(0u128, uint32_t_layout)?, // st_gen - ]; - let buf = this.deref_operand(buf_op)?; - this.write_packed_immediates(&buf, &imms)?; + this.write_int_fields_named( + &[ + ("st_dev", 0), + ("st_mode", mode.into()), + ("st_nlink", 0), + ("st_ino", 0), + ("st_uid", 0), + ("st_gid", 0), + ("st_rdev", 0), + ("st_atime", access_sec.into()), + ("st_atime_nsec", access_nsec.into()), + ("st_mtime", modified_sec.into()), + ("st_mtime_nsec", modified_nsec.into()), + ("st_ctime", 0), + ("st_ctime_nsec", 0), + ("st_birthtime", created_sec.into()), + ("st_birthtime_nsec", created_nsec.into()), + ("st_size", metadata.size.into()), + ("st_blocks", 0), + ("st_blksize", 0), + ("st_flags", 0), + ("st_gen", 0), + ], + &buf, + )?; Ok(0) } @@ -954,7 +941,7 @@ fn linux_statx( // `syscall` function is untyped. This means that all the `statx` parameters are provided // as `isize`s instead of having the proper types. Thus, we have to recover the layout of // `statxbuf_op` by using the `libc::statx` struct type. - let statxbuf_place = { + let statxbuf = { // FIXME: This long path is required because `libc::statx` is an struct and also a // function and `resolve_path` is returning the latter. let statx_ty = this @@ -1064,44 +1051,55 @@ fn linux_statx( }) .unwrap_or(Ok((0, 0)))?; - let __u32_layout = this.libc_ty_layout("__u32")?; - let __u64_layout = this.libc_ty_layout("__u64")?; - let __u16_layout = this.libc_ty_layout("__u16")?; - - // Now we transform all this fields into `ImmTy`s and write them to `statxbuf`. We write a - // zero for the unavailable fields. - let imms = [ - immty_from_uint_checked(mask, __u32_layout)?, // stx_mask - immty_from_uint_checked(0u128, __u32_layout)?, // stx_blksize - immty_from_uint_checked(0u128, __u64_layout)?, // stx_attributes - immty_from_uint_checked(0u128, __u32_layout)?, // stx_nlink - immty_from_uint_checked(0u128, __u32_layout)?, // stx_uid - immty_from_uint_checked(0u128, __u32_layout)?, // stx_gid - immty_from_uint_checked(mode, __u16_layout)?, // stx_mode - immty_from_uint_checked(0u128, __u16_layout)?, // statx padding - immty_from_uint_checked(0u128, __u64_layout)?, // stx_ino - immty_from_uint_checked(metadata.size, __u64_layout)?, // stx_size - immty_from_uint_checked(0u128, __u64_layout)?, // stx_blocks - immty_from_uint_checked(0u128, __u64_layout)?, // stx_attributes - immty_from_uint_checked(access_sec, __u64_layout)?, // stx_atime.tv_sec - immty_from_uint_checked(access_nsec, __u32_layout)?, // stx_atime.tv_nsec - immty_from_uint_checked(0u128, __u32_layout)?, // statx_timestamp padding - immty_from_uint_checked(created_sec, __u64_layout)?, // stx_btime.tv_sec - immty_from_uint_checked(created_nsec, __u32_layout)?, // stx_btime.tv_nsec - immty_from_uint_checked(0u128, __u32_layout)?, // statx_timestamp padding - immty_from_uint_checked(0u128, __u64_layout)?, // stx_ctime.tv_sec - immty_from_uint_checked(0u128, __u32_layout)?, // stx_ctime.tv_nsec - immty_from_uint_checked(0u128, __u32_layout)?, // statx_timestamp padding - immty_from_uint_checked(modified_sec, __u64_layout)?, // stx_mtime.tv_sec - immty_from_uint_checked(modified_nsec, __u32_layout)?, // stx_mtime.tv_nsec - immty_from_uint_checked(0u128, __u32_layout)?, // statx_timestamp padding - immty_from_uint_checked(0u128, __u64_layout)?, // stx_rdev_major - immty_from_uint_checked(0u128, __u64_layout)?, // stx_rdev_minor - immty_from_uint_checked(0u128, __u64_layout)?, // stx_dev_major - immty_from_uint_checked(0u128, __u64_layout)?, // stx_dev_minor - ]; - - this.write_packed_immediates(&statxbuf_place, &imms)?; + // Now we write everything to `statxbuf`. We write a zero for the unavailable fields. + this.write_int_fields_named( + &[ + ("stx_mask", mask.into()), + ("stx_blksize", 0), + ("stx_attributes", 0), + ("stx_nlink", 0), + ("stx_uid", 0), + ("stx_gid", 0), + ("stx_mode", mode.into()), + ("stx_ino", 0), + ("stx_size", metadata.size.into()), + ("stx_blocks", 0), + ("stx_attributes_mask", 0), + ("stx_rdev_major", 0), + ("stx_rdev_minor", 0), + ("stx_dev_major", 0), + ("stx_dev_minor", 0), + ], + &statxbuf, + )?; + this.write_int_fields( + &[ + access_sec.into(), // stx_atime.tv_sec + access_nsec.into(), // stx_atime.tv_nsec + ], + &this.mplace_field_named(&statxbuf, "stx_atime")?, + )?; + this.write_int_fields( + &[ + created_sec.into(), // stx_btime.tv_sec + created_nsec.into(), // stx_btime.tv_nsec + ], + &this.mplace_field_named(&statxbuf, "stx_btime")?, + )?; + this.write_int_fields( + &[ + 0.into(), // stx_ctime.tv_sec + 0.into(), // stx_ctime.tv_nsec + ], + &this.mplace_field_named(&statxbuf, "stx_ctime")?, + )?; + this.write_int_fields( + &[ + modified_sec.into(), // stx_mtime.tv_sec + modified_nsec.into(), // stx_mtime.tv_nsec + ], + &this.mplace_field_named(&statxbuf, "stx_mtime")?, + )?; Ok(0) } @@ -1247,7 +1245,7 @@ fn linux_readdir64(&mut self, dirp_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, S Some(Ok(dir_entry)) => { // Write the directory entry into a newly allocated buffer. // The name is written with write_bytes, while the rest of the - // dirent64 struct is written using write_packed_immediates. + // dirent64 struct is written using write_int_fields. // For reference: // pub struct dirent64 { @@ -1279,11 +1277,15 @@ fn linux_readdir64(&mut self, dirp_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx, S let file_type = this.file_type_to_d_type(dir_entry.file_type())?; - let entry_place = MPlaceTy::from_aligned_ptr(entry, dirent64_layout); - this.write_uint(ino, &this.mplace_field(&entry_place, 0)?.into())?; // d_ino - this.write_uint(0u128, &this.mplace_field(&entry_place, 1)?.into())?; // d_off - this.write_uint(size, &this.mplace_field(&entry_place, 2)?.into())?; // d_reclen - this.write_int(file_type, &this.mplace_field(&entry_place, 3)?.into())?; // d_type + this.write_int_fields( + &[ + ino.into(), // d_ino + 0, // d_off + size.into(), // d_reclen + file_type.into(), // d_type + ], + &MPlaceTy::from_aligned_ptr(entry, dirent64_layout), + )?; let name_ptr = entry.offset(Size::from_bytes(d_name_offset), this)?; this.memory.write_bytes(name_ptr, name_bytes.iter().copied())?; @@ -1333,7 +1335,7 @@ fn macos_readdir_r( Some(Ok(dir_entry)) => { // Write into entry, write pointer to result, return 0 on success. // The name is written with write_os_str_to_c_str, while the rest of the - // dirent struct is written using write_packed_Immediates. + // dirent struct is written using write_int_fields. // For reference: // pub struct dirent { @@ -1361,10 +1363,6 @@ fn macos_readdir_r( } let entry_place = this.deref_operand(entry_op)?; - let ino_t_layout = this.libc_ty_layout("ino_t")?; - let off_t_layout = this.libc_ty_layout("off_t")?; - let c_ushort_layout = this.libc_ty_layout("c_ushort")?; - let c_uchar_layout = this.libc_ty_layout("c_uchar")?; // If the host is a Unix system, fill in the inode number with its real value. // If not, use 0 as a fallback value. @@ -1375,14 +1373,16 @@ fn macos_readdir_r( let file_type = this.file_type_to_d_type(dir_entry.file_type())?; - let imms = [ - immty_from_uint_checked(ino, ino_t_layout)?, // d_ino - immty_from_uint_checked(0u128, off_t_layout)?, // d_seekoff - immty_from_uint_checked(0u128, c_ushort_layout)?, // d_reclen - immty_from_uint_checked(file_name_len, c_ushort_layout)?, // d_namlen - immty_from_int_checked(file_type, c_uchar_layout)?, // d_type - ]; - this.write_packed_immediates(&entry_place, &imms)?; + this.write_int_fields( + &[ + ino.into(), // d_ino + 0, // d_seekoff + 0, // d_reclen + file_name_len.into(), // d_namlen + file_type.into(), // d_type + ], + &entry_place, + )?; let result_place = this.deref_operand(result_op)?; this.write_scalar(this.read_scalar(entry_op)?, &result_place.into())?; diff --git a/src/shims/time.rs b/src/shims/time.rs index 1db9d85debd..0acd697fa40 100644 --- a/src/shims/time.rs +++ b/src/shims/time.rs @@ -2,7 +2,6 @@ use std::time::{Duration, Instant, SystemTime}; use crate::*; -use helpers::{immty_from_int_checked, immty_from_uint_checked}; use thread::Time; /// Returns the time elapsed between the provided time and the unix epoch as a `Duration`. @@ -24,7 +23,6 @@ fn clock_gettime( this.check_no_isolation("`clock_gettime`")?; let clk_id = this.read_scalar(clk_id_op)?.to_i32()?; - let tp = this.deref_operand(tp_op)?; let duration = if clk_id == this.eval_libc_i32("CLOCK_REALTIME")? { system_time_to_duration(&SystemTime::now())? @@ -41,12 +39,7 @@ fn clock_gettime( let tv_sec = duration.as_secs(); let tv_nsec = duration.subsec_nanos(); - let imms = [ - immty_from_int_checked(tv_sec, this.libc_ty_layout("time_t")?)?, - immty_from_int_checked(tv_nsec, this.libc_ty_layout("c_long")?)?, - ]; - - this.write_packed_immediates(&tp, &imms)?; + this.write_int_fields(&[tv_sec.into(), tv_nsec.into()], &this.deref_operand(tp_op)?)?; Ok(0) } @@ -69,18 +62,11 @@ fn gettimeofday( return Ok(-1); } - let tv = this.deref_operand(tv_op)?; - let duration = system_time_to_duration(&SystemTime::now())?; let tv_sec = duration.as_secs(); let tv_usec = duration.subsec_micros(); - let imms = [ - immty_from_int_checked(tv_sec, this.libc_ty_layout("time_t")?)?, - immty_from_int_checked(tv_usec, this.libc_ty_layout("suseconds_t")?)?, - ]; - - this.write_packed_immediates(&tv, &imms)?; + this.write_int_fields(&[tv_sec.into(), tv_usec.into()], &this.deref_operand(tv_op)?)?; Ok(0) } @@ -105,12 +91,11 @@ fn GetSystemTimeAsFileTime(&mut self, LPFILETIME_op: &OpTy<'tcx, Tag>) -> Interp let dwLowDateTime = u32::try_from(duration_ticks & 0x00000000FFFFFFFF).unwrap(); let dwHighDateTime = u32::try_from((duration_ticks & 0xFFFFFFFF00000000) >> 32).unwrap(); - let DWORD_tylayout = this.machine.layouts.u32; - let imms = [ - immty_from_uint_checked(dwLowDateTime, DWORD_tylayout)?, - immty_from_uint_checked(dwHighDateTime, DWORD_tylayout)?, - ]; - this.write_packed_immediates(&this.deref_operand(LPFILETIME_op)?, &imms)?; + this.write_int_fields( + &[dwLowDateTime.into(), dwHighDateTime.into()], + &this.deref_operand(LPFILETIME_op)?, + )?; + Ok(()) } @@ -185,12 +170,8 @@ fn mach_timebase_info(&mut self, info_op: &OpTy<'tcx, Tag>) -> InterpResult<'tcx // Since our emulated ticks in `mach_absolute_time` *are* nanoseconds, // no scaling needs to happen. let (numer, denom) = (1, 1); - let imms = [ - immty_from_int_checked(numer, this.machine.layouts.u32)?, - immty_from_int_checked(denom, this.machine.layouts.u32)?, - ]; + this.write_int_fields(&[numer.into(), denom.into()], &info)?; - this.write_packed_immediates(&info, &imms)?; Ok(0) // KERN_SUCCESS } -- 2.44.0