use crate::*;
use shims::foreign_items::EmulateByNameResult;
use shims::windows::sync::EvalContextExt as _;
+use smallvec::SmallVec;
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
fn emulate_foreign_item_by_name(
&mut self,
- link_name: &str,
- link_name_sym: Symbol,
+ link_name: Symbol,
abi: Abi,
args: &[OpTy<'tcx, Tag>],
dest: &PlaceTy<'tcx, Tag>,
_ret: mir::BasicBlock,
- ) -> InterpResult<'tcx, EmulateByNameResult> {
+ ) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> {
let this = self.eval_context_mut();
// Windows API stubs.
// HANDLE = isize
+ // NTSTATUS = LONH = i32
// DWORD = ULONG = u32
// BOOL = i32
// BOOLEAN = u8
- match link_name {
+ match link_name.as_str() {
// Environment related shims
"GetEnvironmentVariableW" => {
- let &[ref name, ref buf, ref size] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
+ let [name, buf, size] =
+ this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let result = this.GetEnvironmentVariableW(name, buf, size)?;
this.write_scalar(Scalar::from_u32(result), dest)?;
}
"SetEnvironmentVariableW" => {
- let &[ref name, ref value] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
+ let [name, value] =
+ this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let result = this.SetEnvironmentVariableW(name, value)?;
this.write_scalar(Scalar::from_i32(result), dest)?;
}
"GetEnvironmentStringsW" => {
- let &[] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
+ let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let result = this.GetEnvironmentStringsW()?;
- this.write_scalar(result, dest)?;
+ this.write_pointer(result, dest)?;
}
"FreeEnvironmentStringsW" => {
- let &[ref env_block] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
+ let [env_block] =
+ this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let result = this.FreeEnvironmentStringsW(env_block)?;
this.write_scalar(Scalar::from_i32(result), dest)?;
}
"GetCurrentDirectoryW" => {
- let &[ref size, ref buf] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
+ let [size, buf] =
+ this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let result = this.GetCurrentDirectoryW(size, buf)?;
this.write_scalar(Scalar::from_u32(result), dest)?;
}
"SetCurrentDirectoryW" => {
- let &[ref path] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
+ let [path] =
+ this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let result = this.SetCurrentDirectoryW(path)?;
this.write_scalar(Scalar::from_i32(result), dest)?;
}
- // File related shims
- "GetStdHandle" => {
- let &[ref which] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
- let which = this.read_scalar(which)?.to_i32()?;
- // We just make this the identity function, so we know later in `WriteFile`
- // which one it is.
- this.write_scalar(Scalar::from_machine_isize(which.into(), this), dest)?;
- }
- "WriteFile" => {
- let &[ref handle, ref buf, ref n, ref written_ptr, ref overlapped] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
- this.read_scalar(overlapped)?.to_machine_usize(this)?; // this is a poiner, that we ignore
- let handle = this.read_scalar(handle)?.to_machine_isize(this)?;
- let buf = this.read_scalar(buf)?.check_init()?;
- let n = this.read_scalar(n)?.to_u32()?;
- let written_place = this.deref_operand(written_ptr)?;
- // Spec says to always write `0` first.
- this.write_null(&written_place.into())?;
- let written = if handle == -11 || handle == -12 {
- // stdout/stderr
- use std::io::{self, Write};
-
- let buf_cont = this.memory.read_bytes(buf, Size::from_bytes(u64::from(n)))?;
- let res = if handle == -11 {
- io::stdout().write(buf_cont)
- } else {
- io::stderr().write(buf_cont)
- };
- res.ok().map(|n| n as u32)
- } else {
- throw_unsup_format!(
- "on Windows, writing to anything except stdout/stderr is not supported"
- )
- };
- // If there was no error, write back how much was written.
- if let Some(n) = written {
- this.write_scalar(Scalar::from_u32(n), &written_place.into())?;
- }
- // Return whether this was a success.
- this.write_scalar(Scalar::from_i32(if written.is_some() { 1 } else { 0 }), dest)?;
- }
-
// Allocation
"HeapAlloc" => {
- let &[ref handle, ref flags, ref size] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
+ let [handle, flags, size] =
+ this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
this.read_scalar(handle)?.to_machine_isize(this)?;
let flags = this.read_scalar(flags)?.to_u32()?;
let size = this.read_scalar(size)?.to_machine_usize(this)?;
let zero_init = (flags & 0x00000008) != 0; // HEAP_ZERO_MEMORY
- let res = this.malloc(size, zero_init, MiriMemoryKind::WinHeap);
- this.write_scalar(res, dest)?;
+ let res = this.malloc(size, zero_init, MiriMemoryKind::WinHeap)?;
+ this.write_pointer(res, dest)?;
}
"HeapFree" => {
- let &[ref handle, ref flags, ref ptr] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
+ let [handle, flags, ptr] =
+ this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
this.read_scalar(handle)?.to_machine_isize(this)?;
this.read_scalar(flags)?.to_u32()?;
- let ptr = this.read_scalar(ptr)?.check_init()?;
+ let ptr = this.read_pointer(ptr)?;
this.free(ptr, MiriMemoryKind::WinHeap)?;
this.write_scalar(Scalar::from_i32(1), dest)?;
}
"HeapReAlloc" => {
- let &[ref handle, ref flags, ref ptr, ref size] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
+ let [handle, flags, ptr, size] =
+ this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
this.read_scalar(handle)?.to_machine_isize(this)?;
this.read_scalar(flags)?.to_u32()?;
- let ptr = this.read_scalar(ptr)?.check_init()?;
+ let ptr = this.read_pointer(ptr)?;
let size = this.read_scalar(size)?.to_machine_usize(this)?;
let res = this.realloc(ptr, size, MiriMemoryKind::WinHeap)?;
- this.write_scalar(res, dest)?;
+ this.write_pointer(res, dest)?;
}
// errno
"SetLastError" => {
- let &[ref error] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
+ let [error] =
+ this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let error = this.read_scalar(error)?.check_init()?;
this.set_last_error(error)?;
}
"GetLastError" => {
- let &[] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
+ let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let last_error = this.get_last_error()?;
this.write_scalar(last_error, dest)?;
}
// Querying system information
"GetSystemInfo" => {
- let &[ref system_info] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
+ let [system_info] =
+ this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let system_info = this.deref_operand(system_info)?;
// Initialize with `0`.
- this.memory.write_bytes(
+ this.write_bytes_ptr(
system_info.ptr,
iter::repeat(0u8).take(system_info.layout.size.bytes() as usize),
)?;
+ // Set selected fields.
+ let word_layout = this.machine.layouts.u16;
+ let dword_layout = this.machine.layouts.u32;
+ let usize_layout = this.machine.layouts.usize;
+
+ // Using `mplace_field` is error-prone, see: https://github.com/rust-lang/miri/issues/2136.
+ // Pointer fields have different sizes on different targets.
+ // To avoid all these issue we calculate the offsets ourselves.
+ let field_sizes = [
+ word_layout.size, // 0, wProcessorArchitecture : WORD
+ word_layout.size, // 1, wReserved : WORD
+ dword_layout.size, // 2, dwPageSize : DWORD
+ usize_layout.size, // 3, lpMinimumApplicationAddress : LPVOID
+ usize_layout.size, // 4, lpMaximumApplicationAddress : LPVOID
+ usize_layout.size, // 5, dwActiveProcessorMask : DWORD_PTR
+ dword_layout.size, // 6, dwNumberOfProcessors : DWORD
+ dword_layout.size, // 7, dwProcessorType : DWORD
+ dword_layout.size, // 8, dwAllocationGranularity : DWORD
+ word_layout.size, // 9, wProcessorLevel : WORD
+ word_layout.size, // 10, wProcessorRevision : WORD
+ ];
+ let field_offsets: SmallVec<[Size; 11]> = field_sizes
+ .iter()
+ .copied()
+ .scan(Size::ZERO, |a, x| {
+ let res = Some(*a);
+ *a += x;
+ res
+ })
+ .collect();
+
+ // Set page size.
+ let page_size = system_info.offset(
+ field_offsets[2],
+ MemPlaceMeta::None,
+ dword_layout,
+ &this.tcx,
+ )?;
+ this.write_scalar(
+ Scalar::from_int(PAGE_SIZE, dword_layout.size),
+ &page_size.into(),
+ )?;
// Set number of processors.
- let dword_size = Size::from_bytes(4);
- let num_cpus = this.mplace_field(&system_info, 6)?;
- this.write_scalar(Scalar::from_int(NUM_CPUS, dword_size), &num_cpus.into())?;
+ let num_cpus = system_info.offset(
+ field_offsets[6],
+ MemPlaceMeta::None,
+ dword_layout,
+ &this.tcx,
+ )?;
+ this.write_scalar(Scalar::from_int(NUM_CPUS, dword_layout.size), &num_cpus.into())?;
}
// Thread-local storage
// This just creates a key; Windows does not natively support TLS destructors.
// Create key and return it.
- let &[] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
+ let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let key = this.machine.tls.create_tls_key(None, dest.layout.size)?;
this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?;
}
"TlsGetValue" => {
- let &[ref key] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
+ let [key] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let key = u128::from(this.read_scalar(key)?.to_u32()?);
let active_thread = this.get_active_thread();
let ptr = this.machine.tls.load_tls(key, active_thread, this)?;
this.write_scalar(ptr, dest)?;
}
"TlsSetValue" => {
- let &[ref key, ref new_ptr] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
+ let [key, new_ptr] =
+ this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let key = u128::from(this.read_scalar(key)?.to_u32()?);
let active_thread = this.get_active_thread();
- let new_ptr = this.read_scalar(new_ptr)?.check_init()?;
- this.machine.tls.store_tls(key, active_thread, this.test_null(new_ptr)?)?;
+ let new_data = this.read_scalar(new_ptr)?.check_init()?;
+ this.machine.tls.store_tls(key, active_thread, new_data, &*this.tcx)?;
// Return success (`1`).
this.write_scalar(Scalar::from_i32(1), dest)?;
// Access to command-line arguments
"GetCommandLineW" => {
- let &[] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
- this.write_scalar(
- this.machine.cmd_line.expect("machine must be initialized"),
+ let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+ this.write_pointer(
+ this.machine.cmd_line.expect("machine must be initialized").ptr,
dest,
)?;
}
// Time related shims
"GetSystemTimeAsFileTime" => {
#[allow(non_snake_case)]
- let &[ref LPFILETIME] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
+ let [LPFILETIME] =
+ this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
this.GetSystemTimeAsFileTime(LPFILETIME)?;
}
"QueryPerformanceCounter" => {
#[allow(non_snake_case)]
- let &[ref lpPerformanceCount] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
+ let [lpPerformanceCount] =
+ this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let result = this.QueryPerformanceCounter(lpPerformanceCount)?;
this.write_scalar(Scalar::from_i32(result), dest)?;
}
"QueryPerformanceFrequency" => {
#[allow(non_snake_case)]
- let &[ref lpFrequency] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
+ let [lpFrequency] =
+ this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let result = this.QueryPerformanceFrequency(lpFrequency)?;
this.write_scalar(Scalar::from_i32(result), dest)?;
}
// Synchronization primitives
"AcquireSRWLockExclusive" => {
- let &[ref ptr] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
+ let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
this.AcquireSRWLockExclusive(ptr)?;
}
"ReleaseSRWLockExclusive" => {
- let &[ref ptr] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
+ let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
this.ReleaseSRWLockExclusive(ptr)?;
}
"TryAcquireSRWLockExclusive" => {
- let &[ref ptr] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
+ let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let ret = this.TryAcquireSRWLockExclusive(ptr)?;
this.write_scalar(Scalar::from_u8(ret), dest)?;
}
"AcquireSRWLockShared" => {
- let &[ref ptr] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
+ let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
this.AcquireSRWLockShared(ptr)?;
}
"ReleaseSRWLockShared" => {
- let &[ref ptr] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
+ let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
this.ReleaseSRWLockShared(ptr)?;
}
"TryAcquireSRWLockShared" => {
- let &[ref ptr] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
+ let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let ret = this.TryAcquireSRWLockShared(ptr)?;
this.write_scalar(Scalar::from_u8(ret), dest)?;
}
// Dynamic symbol loading
"GetProcAddress" => {
#[allow(non_snake_case)]
- let &[ref hModule, ref lpProcName] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
+ let [hModule, lpProcName] =
+ this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
this.read_scalar(hModule)?.to_machine_isize(this)?;
- let name = this.read_c_str(this.read_scalar(lpProcName)?.check_init()?)?;
+ let name = this.read_c_str(this.read_pointer(lpProcName)?)?;
if let Some(dlsym) = Dlsym::from_str(name, &this.tcx.sess.target.os)? {
- let ptr = this.memory.create_fn_alloc(FnVal::Other(dlsym));
- this.write_scalar(Scalar::from(ptr), dest)?;
+ let ptr = this.create_fn_alloc_ptr(FnVal::Other(dlsym));
+ this.write_pointer(ptr, dest)?;
} else {
this.write_null(dest)?;
}
// Miscellaneous
"SystemFunction036" => {
// This is really 'RtlGenRandom'.
- let &[ref ptr, ref len] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
- let ptr = this.read_scalar(ptr)?.check_init()?;
+ let [ptr, len] =
+ this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+ let ptr = this.read_pointer(ptr)?;
let len = this.read_scalar(len)?.to_u32()?;
this.gen_random(ptr, len.into())?;
this.write_scalar(Scalar::from_bool(true), dest)?;
}
"BCryptGenRandom" => {
- let &[ref algorithm, ref ptr, ref len, ref flags] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
+ let [algorithm, ptr, len, flags] =
+ this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let algorithm = this.read_scalar(algorithm)?;
- let ptr = this.read_scalar(ptr)?.check_init()?;
+ let ptr = this.read_pointer(ptr)?;
let len = this.read_scalar(len)?.to_u32()?;
let flags = this.read_scalar(flags)?.to_u32()?;
if flags != 2 {
}
"GetConsoleScreenBufferInfo" => {
// `term` needs this, so we fake it.
- let &[ref console, ref buffer_info] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
+ let [console, buffer_info] =
+ this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
this.read_scalar(console)?.to_machine_isize(this)?;
this.deref_operand(buffer_info)?;
// Indicate an error.
}
"GetConsoleMode" => {
// Windows "isatty" (in libtest) needs this, so we fake it.
- let &[ref console, ref mode] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
+ let [console, mode] =
+ this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
this.read_scalar(console)?.to_machine_isize(this)?;
this.deref_operand(mode)?;
// Indicate an error.
this.write_null(dest)?;
}
"SwitchToThread" => {
- let &[] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
+ let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
// Note that once Miri supports concurrency, this will need to return a nonzero
// value if this call does result in switching to another thread.
this.write_null(dest)?;
}
+ "GetStdHandle" => {
+ let [which] =
+ this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+ let which = this.read_scalar(which)?.to_i32()?;
+ // We just make this the identity function, so we know later in `NtWriteFile` which
+ // one it is. This is very fake, but libtest needs it so we cannot make it a
+ // std-only shim.
+ this.write_scalar(Scalar::from_machine_isize(which.into(), this), dest)?;
+ }
// Better error for attempts to create a thread
"CreateThread" => {
- this.check_abi_and_shim_symbol_clash(
- abi,
- Abi::System { unwind: false },
- link_name_sym,
- )?;
+ let [_, _, _, _, _, _] =
+ this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
this.handle_unsupported("can't create threads on Windows")?;
return Ok(EmulateByNameResult::AlreadyJumped);
// Incomplete shims that we "stub out" just to get pre-main initialization code to work.
// These shims are enabled only when the caller is in the standard library.
"GetProcessHeap" if this.frame_in_std() => {
- let &[] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
+ let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
// Just fake a HANDLE
this.write_scalar(Scalar::from_machine_isize(1, this), dest)?;
}
+ "GetModuleHandleA" if this.frame_in_std() => {
+ #[allow(non_snake_case)]
+ let [_lpModuleName] =
+ this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+ // We need to return something non-null here to make `compat_fn!` work.
+ this.write_scalar(Scalar::from_machine_isize(1, this), dest)?;
+ }
"SetConsoleTextAttribute" if this.frame_in_std() => {
#[allow(non_snake_case)]
- let &[ref _hConsoleOutput, ref _wAttribute] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
+ let [_hConsoleOutput, _wAttribute] =
+ this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
// Pretend these does not exist / nothing happened, by returning zero.
this.write_null(dest)?;
}
"AddVectoredExceptionHandler" if this.frame_in_std() => {
#[allow(non_snake_case)]
- let &[ref _First, ref _Handler] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
+ let [_First, _Handler] =
+ this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
// Any non zero value works for the stdlib. This is just used for stack overflows anyway.
this.write_scalar(Scalar::from_machine_usize(1, this), dest)?;
}
"SetThreadStackGuarantee" if this.frame_in_std() => {
#[allow(non_snake_case)]
- let &[_StackSizeInBytes] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
+ let [_StackSizeInBytes] =
+ this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
// Any non zero value works for the stdlib. This is just used for stack overflows anyway.
this.write_scalar(Scalar::from_u32(1), dest)?;
}
if this.frame_in_std() =>
{
#[allow(non_snake_case)]
- let &[ref _lpCriticalSection] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
+ let [_lpCriticalSection] =
+ this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
assert_eq!(
this.get_total_thread_count(),
1,
}
"TryEnterCriticalSection" if this.frame_in_std() => {
#[allow(non_snake_case)]
- let &[ref _lpCriticalSection] =
- this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
+ let [_lpCriticalSection] =
+ this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
assert_eq!(
this.get_total_thread_count(),
1,
// There is only one thread, so this always succeeds and returns TRUE.
this.write_scalar(Scalar::from_i32(1), dest)?;
}
+ "GetCurrentThread" if this.frame_in_std() => {
+ let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+ this.write_scalar(Scalar::from_machine_isize(1, this), dest)?;
+ }
_ => return Ok(EmulateByNameResult::NotSupported),
}