miri/shims/windows/
foreign_items.rs

1use std::ffi::OsStr;
2use std::path::{self, Path, PathBuf};
3use std::{io, iter, str};
4
5use rustc_abi::{Align, Size};
6use rustc_middle::ty::Ty;
7use rustc_span::Symbol;
8use rustc_target::callconv::{Conv, FnAbi};
9
10use self::shims::windows::handle::{Handle, PseudoHandle};
11use crate::shims::os_str::bytes_to_os_str;
12use crate::shims::windows::handle::HandleError;
13use crate::shims::windows::*;
14use crate::*;
15
16// The NTSTATUS STATUS_INVALID_HANDLE (0xC0000008) encoded as a HRESULT by setting the N bit.
17// (https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/0642cb2f-2075-4469-918c-4441e69c548a)
18const STATUS_INVALID_HANDLE: u32 = 0xD0000008;
19
20pub fn is_dyn_sym(name: &str) -> bool {
21    // std does dynamic detection for these symbols
22    matches!(
23        name,
24        "SetThreadDescription" | "GetThreadDescription" | "WaitOnAddress" | "WakeByAddressSingle"
25    )
26}
27
28#[cfg(windows)]
29fn win_absolute<'tcx>(path: &Path) -> InterpResult<'tcx, io::Result<PathBuf>> {
30    // We are on Windows so we can simply let the host do this.
31    interp_ok(path::absolute(path))
32}
33
34#[cfg(unix)]
35#[expect(clippy::get_first, clippy::arithmetic_side_effects)]
36fn win_absolute<'tcx>(path: &Path) -> InterpResult<'tcx, io::Result<PathBuf>> {
37    // We are on Unix, so we need to implement parts of the logic ourselves.
38    let bytes = path.as_os_str().as_encoded_bytes();
39    // If it starts with `//` (these were backslashes but are already converted)
40    // then this is a magic special path, we just leave it unchanged.
41    if bytes.get(0).copied() == Some(b'/') && bytes.get(1).copied() == Some(b'/') {
42        return interp_ok(Ok(path.into()));
43    };
44    // Special treatment for Windows' magic filenames: they are treated as being relative to `\\.\`.
45    let magic_filenames = &[
46        "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7", "COM8",
47        "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
48    ];
49    if magic_filenames.iter().any(|m| m.as_bytes() == bytes) {
50        let mut result: Vec<u8> = br"//./".into();
51        result.extend(bytes);
52        return interp_ok(Ok(bytes_to_os_str(&result)?.into()));
53    }
54    // Otherwise we try to do something kind of close to what Windows does, but this is probably not
55    // right in all cases. We iterate over the components between `/`, and remove trailing `.`,
56    // except that trailing `..` remain unchanged.
57    let mut result = vec![];
58    let mut bytes = bytes; // the remaining bytes to process
59    loop {
60        let len = bytes.iter().position(|&b| b == b'/').unwrap_or(bytes.len());
61        let mut component = &bytes[..len];
62        if len >= 2 && component[len - 1] == b'.' && component[len - 2] != b'.' {
63            // Strip trailing `.`
64            component = &component[..len - 1];
65        }
66        // Add this component to output.
67        result.extend(component);
68        // Prepare next iteration.
69        if len < bytes.len() {
70            // There's a component after this; add `/` and process remaining bytes.
71            result.push(b'/');
72            bytes = &bytes[len + 1..];
73            continue;
74        } else {
75            // This was the last component and it did not have a trailing `/`.
76            break;
77        }
78    }
79    // Let the host `absolute` function do working-dir handling
80    interp_ok(path::absolute(bytes_to_os_str(&result)?))
81}
82
83impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
84pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
85    fn emulate_foreign_item_inner(
86        &mut self,
87        link_name: Symbol,
88        abi: &FnAbi<'tcx, Ty<'tcx>>,
89        args: &[OpTy<'tcx>],
90        dest: &MPlaceTy<'tcx>,
91    ) -> InterpResult<'tcx, EmulateItemResult> {
92        let this = self.eval_context_mut();
93
94        // According to
95        // https://github.com/rust-lang/rust/blob/fb00adbdb69266f10df95a4527b767b0ad35ea48/compiler/rustc_target/src/spec/mod.rs#L2766-L2768,
96        // x86-32 Windows uses a different calling convention than other Windows targets
97        // for the "system" ABI.
98        let sys_conv = if this.tcx.sess.target.arch == "x86" { Conv::X86Stdcall } else { Conv::C };
99
100        // See `fn emulate_foreign_item_inner` in `shims/foreign_items.rs` for the general pattern.
101
102        // Windows API stubs.
103        // HANDLE = isize
104        // NTSTATUS = LONH = i32
105        // DWORD = ULONG = u32
106        // BOOL = i32
107        // BOOLEAN = u8
108        match link_name.as_str() {
109            // Environment related shims
110            "GetEnvironmentVariableW" => {
111                let [name, buf, size] = this.check_shim(abi, sys_conv, link_name, args)?;
112                let result = this.GetEnvironmentVariableW(name, buf, size)?;
113                this.write_scalar(result, dest)?;
114            }
115            "SetEnvironmentVariableW" => {
116                let [name, value] = this.check_shim(abi, sys_conv, link_name, args)?;
117                let result = this.SetEnvironmentVariableW(name, value)?;
118                this.write_scalar(result, dest)?;
119            }
120            "GetEnvironmentStringsW" => {
121                let [] = this.check_shim(abi, sys_conv, link_name, args)?;
122                let result = this.GetEnvironmentStringsW()?;
123                this.write_pointer(result, dest)?;
124            }
125            "FreeEnvironmentStringsW" => {
126                let [env_block] = this.check_shim(abi, sys_conv, link_name, args)?;
127                let result = this.FreeEnvironmentStringsW(env_block)?;
128                this.write_scalar(result, dest)?;
129            }
130            "GetCurrentDirectoryW" => {
131                let [size, buf] = this.check_shim(abi, sys_conv, link_name, args)?;
132                let result = this.GetCurrentDirectoryW(size, buf)?;
133                this.write_scalar(result, dest)?;
134            }
135            "SetCurrentDirectoryW" => {
136                let [path] = this.check_shim(abi, sys_conv, link_name, args)?;
137                let result = this.SetCurrentDirectoryW(path)?;
138                this.write_scalar(result, dest)?;
139            }
140            "GetUserProfileDirectoryW" => {
141                let [token, buf, size] = this.check_shim(abi, sys_conv, link_name, args)?;
142                let result = this.GetUserProfileDirectoryW(token, buf, size)?;
143                this.write_scalar(result, dest)?;
144            }
145            "GetCurrentProcessId" => {
146                let [] = this.check_shim(abi, sys_conv, link_name, args)?;
147                let result = this.GetCurrentProcessId()?;
148                this.write_scalar(result, dest)?;
149            }
150
151            // File related shims
152            "NtWriteFile" => {
153                if !this.frame_in_std() {
154                    throw_unsup_format!(
155                        "`NtWriteFile` support is crude and just enough for stdout to work"
156                    );
157                }
158
159                let [
160                    handle,
161                    _event,
162                    _apc_routine,
163                    _apc_context,
164                    io_status_block,
165                    buf,
166                    n,
167                    byte_offset,
168                    _key,
169                ] = this.check_shim(abi, sys_conv, link_name, args)?;
170                let handle = this.read_target_isize(handle)?;
171                let buf = this.read_pointer(buf)?;
172                let n = this.read_scalar(n)?.to_u32()?;
173                let byte_offset = this.read_target_usize(byte_offset)?; // is actually a pointer
174                let io_status_block = this
175                    .deref_pointer_as(io_status_block, this.windows_ty_layout("IO_STATUS_BLOCK"))?;
176
177                if byte_offset != 0 {
178                    throw_unsup_format!(
179                        "`NtWriteFile` `ByteOffset` parameter is non-null, which is unsupported"
180                    );
181                }
182
183                let written = if handle == -11 || handle == -12 {
184                    // stdout/stderr
185                    use io::Write;
186
187                    let buf_cont =
188                        this.read_bytes_ptr_strip_provenance(buf, Size::from_bytes(u64::from(n)))?;
189                    let res = if this.machine.mute_stdout_stderr {
190                        Ok(buf_cont.len())
191                    } else if handle == -11 {
192                        io::stdout().write(buf_cont)
193                    } else {
194                        io::stderr().write(buf_cont)
195                    };
196                    // We write at most `n` bytes, which is a `u32`, so we cannot have written more than that.
197                    res.ok().map(|n| u32::try_from(n).unwrap())
198                } else {
199                    throw_unsup_format!(
200                        "on Windows, writing to anything except stdout/stderr is not supported"
201                    )
202                };
203                // We have to put the result into io_status_block.
204                if let Some(n) = written {
205                    let io_status_information =
206                        this.project_field_named(&io_status_block, "Information")?;
207                    this.write_scalar(
208                        Scalar::from_target_usize(n.into(), this),
209                        &io_status_information,
210                    )?;
211                }
212                // Return whether this was a success. >= 0 is success.
213                // For the error code we arbitrarily pick 0xC0000185, STATUS_IO_DEVICE_ERROR.
214                this.write_scalar(
215                    Scalar::from_u32(if written.is_some() { 0 } else { 0xC0000185u32 }),
216                    dest,
217                )?;
218            }
219            "GetFullPathNameW" => {
220                let [filename, size, buffer, filepart] =
221                    this.check_shim(abi, sys_conv, link_name, args)?;
222                this.check_no_isolation("`GetFullPathNameW`")?;
223
224                let filename = this.read_pointer(filename)?;
225                let size = this.read_scalar(size)?.to_u32()?;
226                let buffer = this.read_pointer(buffer)?;
227                let filepart = this.read_pointer(filepart)?;
228
229                if !this.ptr_is_null(filepart)? {
230                    throw_unsup_format!("GetFullPathNameW: non-null `lpFilePart` is not supported");
231                }
232
233                let filename = this.read_path_from_wide_str(filename)?;
234                let result = match win_absolute(&filename)? {
235                    Err(err) => {
236                        this.set_last_error(err)?;
237                        Scalar::from_u32(0) // return zero upon failure
238                    }
239                    Ok(abs_filename) => {
240                        Scalar::from_u32(helpers::windows_check_buffer_size(
241                            this.write_path_to_wide_str(&abs_filename, buffer, size.into())?,
242                        ))
243                        // This can in fact return 0. It is up to the caller to set last_error to 0
244                        // beforehand and check it afterwards to exclude that case.
245                    }
246                };
247                this.write_scalar(result, dest)?;
248            }
249
250            // Allocation
251            "HeapAlloc" => {
252                let [handle, flags, size] = this.check_shim(abi, sys_conv, link_name, args)?;
253                this.read_target_isize(handle)?;
254                let flags = this.read_scalar(flags)?.to_u32()?;
255                let size = this.read_target_usize(size)?;
256                const HEAP_ZERO_MEMORY: u32 = 0x00000008;
257                let init = if (flags & HEAP_ZERO_MEMORY) == HEAP_ZERO_MEMORY {
258                    AllocInit::Zero
259                } else {
260                    AllocInit::Uninit
261                };
262                // Alignment is twice the pointer size.
263                // Source: <https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heapalloc>
264                let align = this.tcx.pointer_size().bytes().strict_mul(2);
265                let ptr = this.allocate_ptr(
266                    Size::from_bytes(size),
267                    Align::from_bytes(align).unwrap(),
268                    MiriMemoryKind::WinHeap.into(),
269                    init,
270                )?;
271                this.write_pointer(ptr, dest)?;
272            }
273            "HeapFree" => {
274                let [handle, flags, ptr] = this.check_shim(abi, sys_conv, link_name, args)?;
275                this.read_target_isize(handle)?;
276                this.read_scalar(flags)?.to_u32()?;
277                let ptr = this.read_pointer(ptr)?;
278                // "This pointer can be NULL." It doesn't say what happens then, but presumably nothing.
279                // (https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heapfree)
280                if !this.ptr_is_null(ptr)? {
281                    this.deallocate_ptr(ptr, None, MiriMemoryKind::WinHeap.into())?;
282                }
283                this.write_scalar(Scalar::from_i32(1), dest)?;
284            }
285            "HeapReAlloc" => {
286                let [handle, flags, old_ptr, size] =
287                    this.check_shim(abi, sys_conv, link_name, args)?;
288                this.read_target_isize(handle)?;
289                this.read_scalar(flags)?.to_u32()?;
290                let old_ptr = this.read_pointer(old_ptr)?;
291                let size = this.read_target_usize(size)?;
292                let align = this.tcx.pointer_size().bytes().strict_mul(2); // same as above
293                // The docs say that `old_ptr` must come from an earlier HeapAlloc or HeapReAlloc,
294                // so unlike C `realloc` we do *not* allow a NULL here.
295                // (https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heaprealloc)
296                let new_ptr = this.reallocate_ptr(
297                    old_ptr,
298                    None,
299                    Size::from_bytes(size),
300                    Align::from_bytes(align).unwrap(),
301                    MiriMemoryKind::WinHeap.into(),
302                    AllocInit::Uninit,
303                )?;
304                this.write_pointer(new_ptr, dest)?;
305            }
306            "LocalFree" => {
307                let [ptr] = this.check_shim(abi, sys_conv, link_name, args)?;
308                let ptr = this.read_pointer(ptr)?;
309                // "If the hMem parameter is NULL, LocalFree ignores the parameter and returns NULL."
310                // (https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-localfree)
311                if !this.ptr_is_null(ptr)? {
312                    this.deallocate_ptr(ptr, None, MiriMemoryKind::WinLocal.into())?;
313                }
314                this.write_null(dest)?;
315            }
316
317            // errno
318            "SetLastError" => {
319                let [error] = this.check_shim(abi, sys_conv, link_name, args)?;
320                let error = this.read_scalar(error)?;
321                this.set_last_error(error)?;
322            }
323            "GetLastError" => {
324                let [] = this.check_shim(abi, sys_conv, link_name, args)?;
325                let last_error = this.get_last_error()?;
326                this.write_scalar(last_error, dest)?;
327            }
328
329            // Querying system information
330            "GetSystemInfo" => {
331                // Also called from `page_size` crate.
332                let [system_info] = this.check_shim(abi, sys_conv, link_name, args)?;
333                let system_info =
334                    this.deref_pointer_as(system_info, this.windows_ty_layout("SYSTEM_INFO"))?;
335                // Initialize with `0`.
336                this.write_bytes_ptr(
337                    system_info.ptr(),
338                    iter::repeat_n(0u8, system_info.layout.size.bytes_usize()),
339                )?;
340                // Set selected fields.
341                this.write_int_fields_named(
342                    &[
343                        ("dwPageSize", this.machine.page_size.into()),
344                        ("dwNumberOfProcessors", this.machine.num_cpus.into()),
345                    ],
346                    &system_info,
347                )?;
348            }
349
350            // Thread-local storage
351            "TlsAlloc" => {
352                // This just creates a key; Windows does not natively support TLS destructors.
353
354                // Create key and return it.
355                let [] = this.check_shim(abi, sys_conv, link_name, args)?;
356                let key = this.machine.tls.create_tls_key(None, dest.layout.size)?;
357                this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?;
358            }
359            "TlsGetValue" => {
360                let [key] = this.check_shim(abi, sys_conv, link_name, args)?;
361                let key = u128::from(this.read_scalar(key)?.to_u32()?);
362                let active_thread = this.active_thread();
363                let ptr = this.machine.tls.load_tls(key, active_thread, this)?;
364                this.write_scalar(ptr, dest)?;
365            }
366            "TlsSetValue" => {
367                let [key, new_ptr] = this.check_shim(abi, sys_conv, link_name, args)?;
368                let key = u128::from(this.read_scalar(key)?.to_u32()?);
369                let active_thread = this.active_thread();
370                let new_data = this.read_scalar(new_ptr)?;
371                this.machine.tls.store_tls(key, active_thread, new_data, &*this.tcx)?;
372
373                // Return success (`1`).
374                this.write_int(1, dest)?;
375            }
376            "TlsFree" => {
377                let [key] = this.check_shim(abi, sys_conv, link_name, args)?;
378                let key = u128::from(this.read_scalar(key)?.to_u32()?);
379                this.machine.tls.delete_tls_key(key)?;
380
381                // Return success (`1`).
382                this.write_int(1, dest)?;
383            }
384
385            // Access to command-line arguments
386            "GetCommandLineW" => {
387                let [] = this.check_shim(abi, sys_conv, link_name, args)?;
388                this.write_pointer(
389                    this.machine.cmd_line.expect("machine must be initialized"),
390                    dest,
391                )?;
392            }
393
394            // Time related shims
395            "GetSystemTimeAsFileTime" | "GetSystemTimePreciseAsFileTime" => {
396                #[allow(non_snake_case)]
397                let [LPFILETIME] = this.check_shim(abi, sys_conv, link_name, args)?;
398                this.GetSystemTimeAsFileTime(link_name.as_str(), LPFILETIME)?;
399            }
400            "QueryPerformanceCounter" => {
401                #[allow(non_snake_case)]
402                let [lpPerformanceCount] = this.check_shim(abi, sys_conv, link_name, args)?;
403                let result = this.QueryPerformanceCounter(lpPerformanceCount)?;
404                this.write_scalar(result, dest)?;
405            }
406            "QueryPerformanceFrequency" => {
407                #[allow(non_snake_case)]
408                let [lpFrequency] = this.check_shim(abi, sys_conv, link_name, args)?;
409                let result = this.QueryPerformanceFrequency(lpFrequency)?;
410                this.write_scalar(result, dest)?;
411            }
412            "Sleep" => {
413                let [timeout] = this.check_shim(abi, sys_conv, link_name, args)?;
414
415                this.Sleep(timeout)?;
416            }
417            "CreateWaitableTimerExW" => {
418                let [attributes, name, flags, access] =
419                    this.check_shim(abi, sys_conv, link_name, args)?;
420                this.read_pointer(attributes)?;
421                this.read_pointer(name)?;
422                this.read_scalar(flags)?.to_u32()?;
423                this.read_scalar(access)?.to_u32()?;
424                // Unimplemented. Always return failure.
425                let not_supported = this.eval_windows("c", "ERROR_NOT_SUPPORTED");
426                this.set_last_error(not_supported)?;
427                this.write_null(dest)?;
428            }
429
430            // Synchronization primitives
431            "InitOnceBeginInitialize" => {
432                let [ptr, flags, pending, context] =
433                    this.check_shim(abi, sys_conv, link_name, args)?;
434                this.InitOnceBeginInitialize(ptr, flags, pending, context, dest)?;
435            }
436            "InitOnceComplete" => {
437                let [ptr, flags, context] = this.check_shim(abi, sys_conv, link_name, args)?;
438                let result = this.InitOnceComplete(ptr, flags, context)?;
439                this.write_scalar(result, dest)?;
440            }
441            "WaitOnAddress" => {
442                let [ptr_op, compare_op, size_op, timeout_op] =
443                    this.check_shim(abi, sys_conv, link_name, args)?;
444
445                this.WaitOnAddress(ptr_op, compare_op, size_op, timeout_op, dest)?;
446            }
447            "WakeByAddressSingle" => {
448                let [ptr_op] = this.check_shim(abi, sys_conv, link_name, args)?;
449
450                this.WakeByAddressSingle(ptr_op)?;
451            }
452            "WakeByAddressAll" => {
453                let [ptr_op] = this.check_shim(abi, sys_conv, link_name, args)?;
454
455                this.WakeByAddressAll(ptr_op)?;
456            }
457
458            // Dynamic symbol loading
459            "GetProcAddress" => {
460                #[allow(non_snake_case)]
461                let [hModule, lpProcName] = this.check_shim(abi, sys_conv, link_name, args)?;
462                this.read_target_isize(hModule)?;
463                let name = this.read_c_str(this.read_pointer(lpProcName)?)?;
464                if let Ok(name) = str::from_utf8(name)
465                    && is_dyn_sym(name)
466                {
467                    let ptr = this.fn_ptr(FnVal::Other(DynSym::from_str(name)));
468                    this.write_pointer(ptr, dest)?;
469                } else {
470                    this.write_null(dest)?;
471                }
472            }
473
474            // Threading
475            "CreateThread" => {
476                let [security, stacksize, start, arg, flags, thread] =
477                    this.check_shim(abi, sys_conv, link_name, args)?;
478
479                let thread_id =
480                    this.CreateThread(security, stacksize, start, arg, flags, thread)?;
481
482                this.write_scalar(Handle::Thread(thread_id).to_scalar(this), dest)?;
483            }
484            "WaitForSingleObject" => {
485                let [handle, timeout] = this.check_shim(abi, sys_conv, link_name, args)?;
486
487                let ret = this.WaitForSingleObject(handle, timeout)?;
488                this.write_scalar(ret, dest)?;
489            }
490            "GetCurrentThread" => {
491                let [] = this.check_shim(abi, sys_conv, link_name, args)?;
492
493                this.write_scalar(
494                    Handle::Pseudo(PseudoHandle::CurrentThread).to_scalar(this),
495                    dest,
496                )?;
497            }
498            "SetThreadDescription" => {
499                let [handle, name] = this.check_shim(abi, sys_conv, link_name, args)?;
500
501                let handle = this.read_scalar(handle)?;
502                let name = this.read_wide_str(this.read_pointer(name)?)?;
503
504                let thread = match Handle::try_from_scalar(handle, this)? {
505                    Ok(Handle::Thread(thread)) => Ok(thread),
506                    Ok(Handle::Pseudo(PseudoHandle::CurrentThread)) => Ok(this.active_thread()),
507                    Ok(_) | Err(HandleError::InvalidHandle) =>
508                        this.invalid_handle("SetThreadDescription")?,
509                    Err(HandleError::ThreadNotFound(e)) => Err(e),
510                };
511                let res = match thread {
512                    Ok(thread) => {
513                        // FIXME: use non-lossy conversion
514                        this.set_thread_name(thread, String::from_utf16_lossy(&name).into_bytes());
515                        Scalar::from_u32(0)
516                    }
517                    Err(_) => Scalar::from_u32(STATUS_INVALID_HANDLE),
518                };
519
520                this.write_scalar(res, dest)?;
521            }
522            "GetThreadDescription" => {
523                let [handle, name_ptr] = this.check_shim(abi, sys_conv, link_name, args)?;
524
525                let handle = this.read_scalar(handle)?;
526                let name_ptr = this.deref_pointer_as(name_ptr, this.machine.layouts.mut_raw_ptr)?; // the pointer where we should store the ptr to the name
527
528                let thread = match Handle::try_from_scalar(handle, this)? {
529                    Ok(Handle::Thread(thread)) => Ok(thread),
530                    Ok(Handle::Pseudo(PseudoHandle::CurrentThread)) => Ok(this.active_thread()),
531                    Ok(_) | Err(HandleError::InvalidHandle) =>
532                        this.invalid_handle("GetThreadDescription")?,
533                    Err(HandleError::ThreadNotFound(e)) => Err(e),
534                };
535                let (name, res) = match thread {
536                    Ok(thread) => {
537                        // Looks like the default thread name is empty.
538                        let name = this.get_thread_name(thread).unwrap_or(b"").to_owned();
539                        let name = this.alloc_os_str_as_wide_str(
540                            bytes_to_os_str(&name)?,
541                            MiriMemoryKind::WinLocal.into(),
542                        )?;
543                        (Scalar::from_maybe_pointer(name, this), Scalar::from_u32(0))
544                    }
545                    Err(_) => (Scalar::null_ptr(this), Scalar::from_u32(STATUS_INVALID_HANDLE)),
546                };
547
548                this.write_scalar(name, &name_ptr)?;
549                this.write_scalar(res, dest)?;
550            }
551
552            // Miscellaneous
553            "ExitProcess" => {
554                let [code] = this.check_shim(abi, sys_conv, link_name, args)?;
555                // Windows technically uses u32, but we unify everything to a Unix-style i32.
556                let code = this.read_scalar(code)?.to_i32()?;
557                throw_machine_stop!(TerminationInfo::Exit { code, leak_check: false });
558            }
559            "SystemFunction036" => {
560                // used by getrandom 0.1
561                // This is really 'RtlGenRandom'.
562                let [ptr, len] = this.check_shim(abi, sys_conv, link_name, args)?;
563                let ptr = this.read_pointer(ptr)?;
564                let len = this.read_scalar(len)?.to_u32()?;
565                this.gen_random(ptr, len.into())?;
566                this.write_scalar(Scalar::from_bool(true), dest)?;
567            }
568            "ProcessPrng" => {
569                // used by `std`
570                let [ptr, len] = this.check_shim(abi, sys_conv, link_name, args)?;
571                let ptr = this.read_pointer(ptr)?;
572                let len = this.read_target_usize(len)?;
573                this.gen_random(ptr, len)?;
574                this.write_int(1, dest)?;
575            }
576            "BCryptGenRandom" => {
577                // used by getrandom 0.2
578                let [algorithm, ptr, len, flags] =
579                    this.check_shim(abi, sys_conv, link_name, args)?;
580                let algorithm = this.read_scalar(algorithm)?;
581                let algorithm = algorithm.to_target_usize(this)?;
582                let ptr = this.read_pointer(ptr)?;
583                let len = this.read_scalar(len)?.to_u32()?;
584                let flags = this.read_scalar(flags)?.to_u32()?;
585                match flags {
586                    0 => {
587                        if algorithm != 0x81 {
588                            // BCRYPT_RNG_ALG_HANDLE
589                            throw_unsup_format!(
590                                "BCryptGenRandom algorithm must be BCRYPT_RNG_ALG_HANDLE when the flag is 0"
591                            );
592                        }
593                    }
594                    2 => {
595                        // BCRYPT_USE_SYSTEM_PREFERRED_RNG
596                        if algorithm != 0 {
597                            throw_unsup_format!(
598                                "BCryptGenRandom algorithm must be NULL when the flag is BCRYPT_USE_SYSTEM_PREFERRED_RNG"
599                            );
600                        }
601                    }
602                    _ => {
603                        throw_unsup_format!(
604                            "BCryptGenRandom is only supported with BCRYPT_USE_SYSTEM_PREFERRED_RNG or BCRYPT_RNG_ALG_HANDLE"
605                        );
606                    }
607                }
608                this.gen_random(ptr, len.into())?;
609                this.write_null(dest)?; // STATUS_SUCCESS
610            }
611            "GetConsoleScreenBufferInfo" => {
612                // `term` needs this, so we fake it.
613                let [console, buffer_info] = this.check_shim(abi, sys_conv, link_name, args)?;
614                this.read_target_isize(console)?;
615                // FIXME: this should use deref_pointer_as, but CONSOLE_SCREEN_BUFFER_INFO is not in std
616                this.deref_pointer(buffer_info)?;
617                // Indicate an error.
618                // FIXME: we should set last_error, but to what?
619                this.write_null(dest)?;
620            }
621            "GetStdHandle" => {
622                let [which] = this.check_shim(abi, sys_conv, link_name, args)?;
623                let which = this.read_scalar(which)?.to_i32()?;
624                // We just make this the identity function, so we know later in `NtWriteFile` which
625                // one it is. This is very fake, but libtest needs it so we cannot make it a
626                // std-only shim.
627                // FIXME: this should return real HANDLEs when io support is added
628                this.write_scalar(Scalar::from_target_isize(which.into(), this), dest)?;
629            }
630            "CloseHandle" => {
631                let [handle] = this.check_shim(abi, sys_conv, link_name, args)?;
632
633                let ret = this.CloseHandle(handle)?;
634
635                this.write_scalar(ret, dest)?;
636            }
637            "GetModuleFileNameW" => {
638                let [handle, filename, size] = this.check_shim(abi, sys_conv, link_name, args)?;
639                this.check_no_isolation("`GetModuleFileNameW`")?;
640
641                let handle = this.read_target_usize(handle)?;
642                let filename = this.read_pointer(filename)?;
643                let size = this.read_scalar(size)?.to_u32()?;
644
645                if handle != 0 {
646                    throw_unsup_format!("`GetModuleFileNameW` only supports the NULL handle");
647                }
648
649                // Using the host current_exe is a bit off, but consistent with Linux
650                // (where stdlib reads /proc/self/exe).
651                let path = std::env::current_exe().unwrap();
652                let (all_written, size_needed) =
653                    this.write_path_to_wide_str_truncated(&path, filename, size.into())?;
654
655                if all_written {
656                    // If the function succeeds, the return value is the length of the string that
657                    // is copied to the buffer, in characters, not including the terminating null
658                    // character.
659                    this.write_int(size_needed.strict_sub(1), dest)?;
660                } else {
661                    // If the buffer is too small to hold the module name, the string is truncated
662                    // to nSize characters including the terminating null character, the function
663                    // returns nSize, and the function sets the last error to
664                    // ERROR_INSUFFICIENT_BUFFER.
665                    this.write_int(size, dest)?;
666                    let insufficient_buffer = this.eval_windows("c", "ERROR_INSUFFICIENT_BUFFER");
667                    this.set_last_error(insufficient_buffer)?;
668                }
669            }
670            "FormatMessageW" => {
671                let [flags, module, message_id, language_id, buffer, size, arguments] =
672                    this.check_shim(abi, sys_conv, link_name, args)?;
673
674                let flags = this.read_scalar(flags)?.to_u32()?;
675                let _module = this.read_pointer(module)?; // seems to contain a module name
676                let message_id = this.read_scalar(message_id)?;
677                let _language_id = this.read_scalar(language_id)?.to_u32()?;
678                let buffer = this.read_pointer(buffer)?;
679                let size = this.read_scalar(size)?.to_u32()?;
680                let _arguments = this.read_pointer(arguments)?;
681
682                // We only support `FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS`
683                // This also means `arguments` can be ignored.
684                if flags != 4096u32 | 512u32 {
685                    throw_unsup_format!("FormatMessageW: unsupported flags {flags:#x}");
686                }
687
688                let error = this.try_errnum_to_io_error(message_id)?;
689                let formatted = match error {
690                    Some(err) => format!("{err}"),
691                    None => format!("<unknown error in FormatMessageW: {message_id}>"),
692                };
693                let (complete, length) =
694                    this.write_os_str_to_wide_str(OsStr::new(&formatted), buffer, size.into())?;
695                if !complete {
696                    // The API docs don't say what happens when the buffer is not big enough...
697                    // Let's just bail.
698                    throw_unsup_format!("FormatMessageW: buffer not big enough");
699                }
700                // The return value is the number of characters stored *excluding* the null terminator.
701                this.write_int(length.strict_sub(1), dest)?;
702            }
703
704            // Incomplete shims that we "stub out" just to get pre-main initialization code to work.
705            // These shims are enabled only when the caller is in the standard library.
706            "GetProcessHeap" if this.frame_in_std() => {
707                let [] = this.check_shim(abi, sys_conv, link_name, args)?;
708                // Just fake a HANDLE
709                // It's fine to not use the Handle type here because its a stub
710                this.write_int(1, dest)?;
711            }
712            "GetModuleHandleA" if this.frame_in_std() => {
713                #[allow(non_snake_case)]
714                let [_lpModuleName] = this.check_shim(abi, sys_conv, link_name, args)?;
715                // We need to return something non-null here to make `compat_fn!` work.
716                this.write_int(1, dest)?;
717            }
718            "SetConsoleTextAttribute" if this.frame_in_std() => {
719                #[allow(non_snake_case)]
720                let [_hConsoleOutput, _wAttribute] =
721                    this.check_shim(abi, sys_conv, link_name, args)?;
722                // Pretend these does not exist / nothing happened, by returning zero.
723                this.write_null(dest)?;
724            }
725            "GetConsoleMode" if this.frame_in_std() => {
726                let [console, mode] = this.check_shim(abi, sys_conv, link_name, args)?;
727                this.read_target_isize(console)?;
728                this.deref_pointer_as(mode, this.machine.layouts.u32)?;
729                // Indicate an error.
730                this.write_null(dest)?;
731            }
732            "GetFileType" if this.frame_in_std() => {
733                #[allow(non_snake_case)]
734                let [_hFile] = this.check_shim(abi, sys_conv, link_name, args)?;
735                // Return unknown file type.
736                this.write_null(dest)?;
737            }
738            "AddVectoredExceptionHandler" if this.frame_in_std() => {
739                #[allow(non_snake_case)]
740                let [_First, _Handler] = this.check_shim(abi, sys_conv, link_name, args)?;
741                // Any non zero value works for the stdlib. This is just used for stack overflows anyway.
742                this.write_int(1, dest)?;
743            }
744            "SetThreadStackGuarantee" if this.frame_in_std() => {
745                #[allow(non_snake_case)]
746                let [_StackSizeInBytes] = this.check_shim(abi, sys_conv, link_name, args)?;
747                // Any non zero value works for the stdlib. This is just used for stack overflows anyway.
748                this.write_int(1, dest)?;
749            }
750            // this is only callable from std because we know that std ignores the return value
751            "SwitchToThread" if this.frame_in_std() => {
752                let [] = this.check_shim(abi, sys_conv, link_name, args)?;
753
754                this.yield_active_thread();
755
756                // FIXME: this should return a nonzero value if this call does result in switching to another thread.
757                this.write_null(dest)?;
758            }
759
760            "_Unwind_RaiseException" => {
761                // This is not formally part of POSIX, but it is very wide-spread on POSIX systems.
762                // It was originally specified as part of the Itanium C++ ABI:
763                // https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html#base-throw.
764                // MinGW implements _Unwind_RaiseException on top of SEH exceptions.
765                if this.tcx.sess.target.env != "gnu" {
766                    throw_unsup_format!(
767                        "`_Unwind_RaiseException` is not supported on non-MinGW Windows",
768                    );
769                }
770                // This function looks and behaves excatly like miri_start_unwind.
771                let [payload] = this.check_shim(abi, Conv::C, link_name, args)?;
772                this.handle_miri_start_unwind(payload)?;
773                return interp_ok(EmulateItemResult::NeedsUnwind);
774            }
775
776            _ => return interp_ok(EmulateItemResult::NotSupported),
777        }
778
779        interp_ok(EmulateItemResult::NeedsReturn)
780    }
781}