diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs index be6fc2ebb7a27..d2c10363eba04 100644 --- a/library/std/src/sys/windows/c.rs +++ b/library/std/src/sys/windows/c.rs @@ -286,6 +286,7 @@ pub const STATUS_INVALID_PARAMETER: NTSTATUS = 0xc000000d_u32 as _; pub const STATUS_PENDING: NTSTATUS = 0x103 as _; pub const STATUS_END_OF_FILE: NTSTATUS = 0xC0000011_u32 as _; +#[cfg(target_arch = "x86")] pub const STATUS_NOT_IMPLEMENTED: NTSTATUS = 0xC0000002_u32 as _; // Equivalent to the `NT_SUCCESS` C preprocessor macro. @@ -843,7 +844,8 @@ if #[cfg(not(target_vendor = "uwp"))] { ) -> BOOL; } - #[link(name = "userenv")] + #[cfg_attr(not(target_arch = "x86"), link(name = "userenv", kind = "raw-dylib"))] + #[cfg_attr(target_arch = "x86", link(name = "userenv"))] extern "system" { // Allowed but unused by UWP pub fn GetUserProfileDirectoryW( @@ -1158,7 +1160,8 @@ extern "system" { pub fn GetFileAttributesW(lpFileName: LPCWSTR) -> DWORD; } -#[link(name = "ws2_32")] +#[cfg_attr(not(target_arch = "x86"), link(name = "ws2_32", kind = "raw-dylib"))] +#[cfg_attr(target_arch = "x86", link(name = "ws2_32"))] extern "system" { pub fn WSAStartup(wVersionRequested: WORD, lpWSAData: LPWSADATA) -> c_int; pub fn WSACleanup() -> c_int; @@ -1251,7 +1254,8 @@ extern "system" { ) -> c_int; } -#[link(name = "bcrypt")] +#[cfg_attr(not(target_arch = "x86"), link(name = "bcrypt", kind = "raw-dylib"))] +#[cfg_attr(target_arch = "x86", link(name = "bcrypt"))] extern "system" { // >= Vista / Server 2008 // https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom @@ -1270,6 +1274,47 @@ extern "system" { pub fn BCryptCloseAlgorithmProvider(hAlgorithm: BCRYPT_ALG_HANDLE, dwFlags: ULONG) -> NTSTATUS; } +#[cfg(not(target_arch = "x86"))] +#[link(name = "ntdll", kind = "raw-dylib")] +extern "system" { + pub fn NtCreateFile( + FileHandle: *mut HANDLE, + DesiredAccess: ACCESS_MASK, + ObjectAttributes: *const OBJECT_ATTRIBUTES, + IoStatusBlock: *mut IO_STATUS_BLOCK, + AllocationSize: *mut i64, + FileAttributes: ULONG, + ShareAccess: ULONG, + CreateDisposition: ULONG, + CreateOptions: ULONG, + EaBuffer: *mut c_void, + EaLength: ULONG, + ) -> NTSTATUS; + pub fn NtReadFile( + FileHandle: BorrowedHandle<'_>, + Event: HANDLE, + ApcRoutine: Option, + ApcContext: *mut c_void, + IoStatusBlock: &mut IO_STATUS_BLOCK, + Buffer: *mut crate::mem::MaybeUninit, + Length: ULONG, + ByteOffset: Option<&LARGE_INTEGER>, + Key: Option<&ULONG>, + ) -> NTSTATUS; + pub fn NtWriteFile( + FileHandle: BorrowedHandle<'_>, + Event: HANDLE, + ApcRoutine: Option, + ApcContext: *mut c_void, + IoStatusBlock: &mut IO_STATUS_BLOCK, + Buffer: *const u8, + Length: ULONG, + ByteOffset: Option<&LARGE_INTEGER>, + Key: Option<&ULONG>, + ) -> NTSTATUS; + pub fn RtlNtStatusToDosError(Status: NTSTATUS) -> ULONG; +} + // Functions that aren't available on every version of Windows that we support, // but we still use them and just provide some form of a fallback implementation. compat_fn_with_fallback! { @@ -1310,6 +1355,7 @@ compat_fn_optional! { compat_fn_with_fallback! { pub static NTDLL: &CStr = ansi_str!("ntdll"); + #[cfg(target_arch = "x86")] pub fn NtCreateFile( FileHandle: *mut HANDLE, DesiredAccess: ACCESS_MASK, @@ -1325,6 +1371,7 @@ compat_fn_with_fallback! { ) -> NTSTATUS { STATUS_NOT_IMPLEMENTED } + #[cfg(target_arch = "x86")] pub fn NtReadFile( FileHandle: BorrowedHandle<'_>, Event: HANDLE, @@ -1338,6 +1385,7 @@ compat_fn_with_fallback! { ) -> NTSTATUS { STATUS_NOT_IMPLEMENTED } + #[cfg(target_arch = "x86")] pub fn NtWriteFile( FileHandle: BorrowedHandle<'_>, Event: HANDLE, @@ -1351,6 +1399,7 @@ compat_fn_with_fallback! { ) -> NTSTATUS { STATUS_NOT_IMPLEMENTED } + #[cfg(target_arch = "x86")] pub fn RtlNtStatusToDosError( Status: NTSTATUS ) -> ULONG { diff --git a/src/tools/miri/src/shims/windows/dlsym.rs b/src/tools/miri/src/shims/windows/dlsym.rs index 4b2a90723c79c..42bb694b77c82 100644 --- a/src/tools/miri/src/shims/windows/dlsym.rs +++ b/src/tools/miri/src/shims/windows/dlsym.rs @@ -1,5 +1,5 @@ +use super::foreign_items::nt_write_file; use rustc_middle::mir; -use rustc_target::abi::Size; use rustc_target::spec::abi::Abi; use log::trace; @@ -56,64 +56,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { ); } - let [ - handle, - _event, - _apc_routine, - _apc_context, - io_status_block, - buf, - n, - byte_offset, - _key, - ] = check_arg_count(args)?; - let handle = this.read_scalar(handle)?.to_machine_isize(this)?; - let buf = this.read_pointer(buf)?; - let n = this.read_scalar(n)?.to_u32()?; - let byte_offset = this.read_scalar(byte_offset)?.to_machine_usize(this)?; // is actually a pointer - let io_status_block = this.deref_operand(io_status_block)?; - - if byte_offset != 0 { - throw_unsup_format!( - "`NtWriteFile` `ByteOffset` paremeter is non-null, which is unsupported" - ); - } - - let written = if handle == -11 || handle == -12 { - // stdout/stderr - 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) - }; - // We write at most `n` bytes, which is a `u32`, so we cannot have written more than that. - res.ok().map(|n| u32::try_from(n).unwrap()) - } else { - throw_unsup_format!( - "on Windows, writing to anything except stdout/stderr is not supported" - ) - }; - // We have to put the result into io_status_block. - if let Some(n) = written { - let io_status_information = - this.mplace_field_named(&io_status_block, "Information")?; - this.write_scalar( - Scalar::from_machine_usize(n.into(), this), - &io_status_information.into(), - )?; - } - // Return whether this was a success. >= 0 is success. - // For the error code we arbitrarily pick 0xC0000185, STATUS_IO_DEVICE_ERROR. - this.write_scalar( - Scalar::from_u32(if written.is_some() { 0 } else { 0xC0000185u32 }), - dest, - )?; + nt_write_file(this, args, dest)?; } Dlsym::SetThreadDescription => { let [handle, name] = check_arg_count(args)?; diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs index 2a34a3a47bbb5..530c8d7ab2680 100644 --- a/src/tools/miri/src/shims/windows/foreign_items.rs +++ b/src/tools/miri/src/shims/windows/foreign_items.rs @@ -4,6 +4,7 @@ use rustc_span::Symbol; use rustc_target::abi::Size; use rustc_target::spec::abi::Abi; +use crate::helpers::check_arg_count; use crate::*; use shims::foreign_items::EmulateByNameResult; use shims::windows::handle::{EvalContextExt as _, Handle, PseudoHandle}; @@ -453,6 +454,14 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { // FIXME: this should return a nonzero value if this call does result in switching to another thread. this.write_null(dest)?; } + "NtWriteFile" if this.frame_in_std() => { + this.check_abi_and_shim_symbol_clash( + abi, + Abi::System { unwind: false }, + link_name, + )?; + nt_write_file(this, args, dest)?; + } _ => return Ok(EmulateByNameResult::NotSupported), } @@ -460,3 +469,55 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> { Ok(EmulateByNameResult::NeedsJumping) } } + +// Incomplete implementation of `NtWriteFile`. +pub(super) fn nt_write_file<'a, 'mir, 'tcx>( + this: &'a mut MiriInterpCx<'mir, 'tcx>, + args: &[OpTy<'tcx, Provenance>], + dest: &PlaceTy<'tcx, Provenance>, +) -> InterpResult<'tcx, ()> { + let [handle, _event, _apc_routine, _apc_context, io_status_block, buf, n, byte_offset, _key] = + check_arg_count(args)?; + + let handle = this.read_scalar(handle)?.to_machine_isize(this)?; + let buf = this.read_pointer(buf)?; + let n = this.read_scalar(n)?.to_u32()?; + let byte_offset = this.read_scalar(byte_offset)?.to_machine_usize(this)?; // is actually a pointer + let io_status_block = this.deref_operand(io_status_block)?; + + if byte_offset != 0 { + throw_unsup_format!( + "`NtWriteFile` `ByteOffset` paremeter is non-null, which is unsupported" + ); + } + + let written = if handle == -11 || handle == -12 { + // stdout/stderr + 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) + }; + // We write at most `n` bytes, which is a `u32`, so we cannot have written more than that. + res.ok().map(|n| u32::try_from(n).unwrap()) + } else { + throw_unsup_format!("on Windows, writing to anything except stdout/stderr is not supported") + }; + // We have to put the result into io_status_block. + if let Some(n) = written { + let io_status_information = this.mplace_field_named(&io_status_block, "Information")?; + this.write_scalar( + Scalar::from_machine_usize(n.into(), this), + &io_status_information.into(), + )?; + } + // Return whether this was a success. >= 0 is success. + // For the error code we arbitrarily pick 0xC0000185, STATUS_IO_DEVICE_ERROR. + this.write_scalar(Scalar::from_u32(if written.is_some() { 0 } else { 0xC0000185u32 }), dest)?; + Ok(()) +}