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