use std::iter;
use rustc_span::Symbol;
use rustc_target::abi::Size;
use rustc_target::spec::abi::Abi;
use crate::*;
use shims::foreign_items::EmulateByNameResult;
use shims::windows::handle::{EvalContextExt as _, Handle, PseudoHandle};
use shims::windows::sync::EvalContextExt as _;
use shims::windows::thread::EvalContextExt as _;
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
fn emulate_foreign_item_by_name(
&mut self,
link_name: Symbol,
abi: Abi,
args: &[OpTy<'tcx, Provenance>],
dest: &PlaceTy<'tcx, Provenance>,
) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> {
let this = self.eval_context_mut();
match link_name.as_str() {
"GetEnvironmentVariableW" => {
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(result, dest)?;
}
"SetEnvironmentVariableW" => {
let [name, value] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let result = this.SetEnvironmentVariableW(name, value)?;
this.write_scalar(result, dest)?;
}
"GetEnvironmentStringsW" => {
let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let result = this.GetEnvironmentStringsW()?;
this.write_pointer(result, dest)?;
}
"FreeEnvironmentStringsW" => {
let [env_block] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let result = this.FreeEnvironmentStringsW(env_block)?;
this.write_scalar(result, dest)?;
}
"GetCurrentDirectoryW" => {
let [size, buf] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let result = this.GetCurrentDirectoryW(size, buf)?;
this.write_scalar(result, dest)?;
}
"SetCurrentDirectoryW" => {
let [path] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let result = this.SetCurrentDirectoryW(path)?;
this.write_scalar(result, dest)?;
}
"NtWriteFile" => {
if !this.frame_in_std() {
throw_unsup_format!(
"`NtWriteFile` support is crude and just enough for stdout to work"
);
}
let [
handle,
_event,
_apc_routine,
_apc_context,
io_status_block,
buf,
n,
byte_offset,
_key,
] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let handle = this.read_target_isize(handle)?;
let buf = this.read_pointer(buf)?;
let n = this.read_scalar(n)?.to_u32()?;
let byte_offset = this.read_target_usize(byte_offset)?; let io_status_block = this
.deref_pointer_as(io_status_block, this.windows_ty_layout("IO_STATUS_BLOCK"))?;
if byte_offset != 0 {
throw_unsup_format!(
"`NtWriteFile` `ByteOffset` parameter is non-null, which is unsupported"
);
}
let written = if handle == -11 || handle == -12 {
use std::io::{self, Write};
let buf_cont =
this.read_bytes_ptr_strip_provenance(buf, Size::from_bytes(u64::from(n)))?;
let res = if this.machine.mute_stdout_stderr {
Ok(buf_cont.len())
} else if handle == -11 {
io::stdout().write(buf_cont)
} else {
io::stderr().write(buf_cont)
};
res.ok().map(|n| u32::try_from(n).unwrap())
} else {
throw_unsup_format!(
"on Windows, writing to anything except stdout/stderr is not supported"
)
};
if let Some(n) = written {
let io_status_information =
this.project_field_named(&io_status_block, "Information")?;
this.write_scalar(
Scalar::from_target_usize(n.into(), this),
&io_status_information,
)?;
}
this.write_scalar(
Scalar::from_u32(if written.is_some() { 0 } else { 0xC0000185u32 }),
dest,
)?;
}
"HeapAlloc" => {
let [handle, flags, size] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
this.read_target_isize(handle)?;
let flags = this.read_scalar(flags)?.to_u32()?;
let size = this.read_target_usize(size)?;
let heap_zero_memory = 0x00000008; let zero_init = (flags & heap_zero_memory) == heap_zero_memory;
let res = this.malloc(size, zero_init, MiriMemoryKind::WinHeap)?;
this.write_pointer(res, dest)?;
}
"HeapFree" => {
let [handle, flags, ptr] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
this.read_target_isize(handle)?;
this.read_scalar(flags)?.to_u32()?;
let ptr = this.read_pointer(ptr)?;
this.free(ptr, MiriMemoryKind::WinHeap)?;
this.write_scalar(Scalar::from_i32(1), dest)?;
}
"HeapReAlloc" => {
let [handle, flags, ptr, size] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
this.read_target_isize(handle)?;
this.read_scalar(flags)?.to_u32()?;
let ptr = this.read_pointer(ptr)?;
let size = this.read_target_usize(size)?;
let res = this.realloc(ptr, size, MiriMemoryKind::WinHeap)?;
this.write_pointer(res, dest)?;
}
"SetLastError" => {
let [error] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let error = this.read_scalar(error)?;
this.set_last_error(error)?;
}
"GetLastError" => {
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)?;
}
"GetSystemInfo" => {
let [system_info] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let system_info =
this.deref_pointer_as(system_info, this.windows_ty_layout("SYSTEM_INFO"))?;
this.write_bytes_ptr(
system_info.ptr(),
iter::repeat(0u8).take(system_info.layout.size.bytes_usize()),
)?;
this.write_int_fields_named(
&[
("dwPageSize", this.machine.page_size.into()),
("dwNumberOfProcessors", this.machine.num_cpus.into()),
],
&system_info,
)?;
}
"TlsAlloc" => {
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 [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 [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_data = this.read_scalar(new_ptr)?;
this.machine.tls.store_tls(key, active_thread, new_data, &*this.tcx)?;
this.write_scalar(Scalar::from_i32(1), dest)?;
}
"GetCommandLineW" => {
let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
this.write_pointer(
this.machine.cmd_line.expect("machine must be initialized"),
dest,
)?;
}
"GetSystemTimeAsFileTime" => {
#[allow(non_snake_case)]
let [LPFILETIME] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
this.GetSystemTimeAsFileTime(LPFILETIME)?;
}
"QueryPerformanceCounter" => {
#[allow(non_snake_case)]
let [lpPerformanceCount] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let result = this.QueryPerformanceCounter(lpPerformanceCount)?;
this.write_scalar(result, dest)?;
}
"QueryPerformanceFrequency" => {
#[allow(non_snake_case)]
let [lpFrequency] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let result = this.QueryPerformanceFrequency(lpFrequency)?;
this.write_scalar(result, dest)?;
}
"Sleep" => {
let [timeout] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
this.Sleep(timeout)?;
}
"AcquireSRWLockExclusive" => {
let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
this.AcquireSRWLockExclusive(ptr)?;
}
"ReleaseSRWLockExclusive" => {
let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
this.ReleaseSRWLockExclusive(ptr)?;
}
"TryAcquireSRWLockExclusive" => {
let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let ret = this.TryAcquireSRWLockExclusive(ptr)?;
this.write_scalar(ret, dest)?;
}
"AcquireSRWLockShared" => {
let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
this.AcquireSRWLockShared(ptr)?;
}
"ReleaseSRWLockShared" => {
let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
this.ReleaseSRWLockShared(ptr)?;
}
"TryAcquireSRWLockShared" => {
let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let ret = this.TryAcquireSRWLockShared(ptr)?;
this.write_scalar(ret, dest)?;
}
"InitOnceBeginInitialize" => {
let [ptr, flags, pending, context] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let result = this.InitOnceBeginInitialize(ptr, flags, pending, context)?;
this.write_scalar(result, dest)?;
}
"InitOnceComplete" => {
let [ptr, flags, context] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let result = this.InitOnceComplete(ptr, flags, context)?;
this.write_scalar(result, dest)?;
}
"SleepConditionVariableSRW" => {
let [condvar, lock, timeout, flags] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let result = this.SleepConditionVariableSRW(condvar, lock, timeout, flags, dest)?;
this.write_scalar(result, dest)?;
}
"WakeConditionVariable" => {
let [condvar] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
this.WakeConditionVariable(condvar)?;
}
"WakeAllConditionVariable" => {
let [condvar] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
this.WakeAllConditionVariable(condvar)?;
}
"GetProcAddress" => {
#[allow(non_snake_case)]
let [hModule, lpProcName] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
this.read_target_isize(hModule)?;
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.fn_ptr(FnVal::Other(dlsym));
this.write_pointer(ptr, dest)?;
} else {
this.write_null(dest)?;
}
}
"SystemFunction036" => {
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 [algorithm, ptr, len, flags] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let algorithm = this.read_scalar(algorithm)?;
let algorithm = algorithm.to_target_usize(this)?;
let ptr = this.read_pointer(ptr)?;
let len = this.read_scalar(len)?.to_u32()?;
let flags = this.read_scalar(flags)?.to_u32()?;
match flags {
0 => {
if algorithm != 0x81 {
throw_unsup_format!(
"BCryptGenRandom algorithm must be BCRYPT_RNG_ALG_HANDLE when the flag is 0"
);
}
}
2 => {
if algorithm != 0 {
throw_unsup_format!(
"BCryptGenRandom algorithm must be NULL when the flag is BCRYPT_USE_SYSTEM_PREFERRED_RNG"
);
}
}
_ => {
throw_unsup_format!(
"BCryptGenRandom is only supported with BCRYPT_USE_SYSTEM_PREFERRED_RNG or BCRYPT_RNG_ALG_HANDLE"
);
}
}
this.gen_random(ptr, len.into())?;
this.write_null(dest)?; }
"GetConsoleScreenBufferInfo" => {
let [console, buffer_info] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
this.read_target_isize(console)?;
this.deref_pointer(buffer_info)?;
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()?;
this.write_scalar(Scalar::from_target_isize(which.into(), this), dest)?;
}
"CloseHandle" => {
let [handle] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
this.CloseHandle(handle)?;
this.write_scalar(Scalar::from_u32(1), dest)?;
}
"GetModuleFileNameW" => {
let [handle, filename, size] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
this.check_no_isolation("`GetModuleFileNameW`")?;
let handle = this.read_target_usize(handle)?;
let filename = this.read_pointer(filename)?;
let size = this.read_scalar(size)?.to_u32()?;
if handle != 0 {
throw_unsup_format!("`GetModuleFileNameW` only supports the NULL handle");
}
let path = std::env::current_exe().unwrap();
let (all_written, size_needed) = this.write_path_to_wide_str(
&path,
filename,
size.into(),
true,
)?;
if all_written {
this.write_int(size_needed.checked_sub(1).unwrap(), dest)?;
} else {
this.write_int(size, dest)?;
let insufficient_buffer = this.eval_windows("c", "ERROR_INSUFFICIENT_BUFFER");
this.set_last_error(insufficient_buffer)?;
}
}
"CreateThread" => {
let [security, stacksize, start, arg, flags, thread] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let thread_id =
this.CreateThread(security, stacksize, start, arg, flags, thread)?;
this.write_scalar(Handle::Thread(thread_id).to_scalar(this), dest)?;
}
"WaitForSingleObject" => {
let [handle, timeout] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let ret = this.WaitForSingleObject(handle, timeout)?;
this.write_scalar(Scalar::from_u32(ret), dest)?;
}
"GetCurrentThread" => {
let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
this.write_scalar(
Handle::Pseudo(PseudoHandle::CurrentThread).to_scalar(this),
dest,
)?;
}
"GetProcessHeap" if this.frame_in_std() => {
let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
this.write_int(1, dest)?;
}
"GetModuleHandleA" if this.frame_in_std() => {
#[allow(non_snake_case)]
let [_lpModuleName] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
this.write_int(1, dest)?;
}
"SetConsoleTextAttribute" if this.frame_in_std() => {
#[allow(non_snake_case)]
let [_hConsoleOutput, _wAttribute] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
this.write_null(dest)?;
}
"GetConsoleMode" if this.frame_in_std() => {
let [console, mode] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
this.read_target_isize(console)?;
this.deref_pointer(mode)?;
this.write_null(dest)?;
}
"GetFileType" if this.frame_in_std() => {
#[allow(non_snake_case)]
let [_hFile] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
this.write_null(dest)?;
}
"AddVectoredExceptionHandler" if this.frame_in_std() => {
#[allow(non_snake_case)]
let [_First, _Handler] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
this.write_int(1, dest)?;
}
"SetThreadStackGuarantee" if this.frame_in_std() => {
#[allow(non_snake_case)]
let [_StackSizeInBytes] =
this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
this.write_int(1, dest)?;
}
"GetCurrentProcessId" if this.frame_in_std() => {
let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
let result = this.GetCurrentProcessId()?;
this.write_int(result, dest)?;
}
"SwitchToThread" if this.frame_in_std() => {
let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
this.yield_active_thread();
this.write_null(dest)?;
}
_ => return Ok(EmulateByNameResult::NotSupported),
}
Ok(EmulateByNameResult::NeedsJumping)
}
}