Skip to main content

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::FnAbi;
9use rustc_target::spec::Env;
10
11use self::shims::windows::handle::{Handle, PseudoHandle};
12use crate::shims::os_str::bytes_to_os_str;
13use crate::shims::windows::*;
14use crate::*;
15
16pub fn is_dyn_sym(name: &str) -> bool {
17    // std does dynamic detection for these symbols
18    matches!(
19        name,
20        "SetThreadDescription" | "GetThreadDescription" | "WaitOnAddress" | "WakeByAddressSingle"
21    )
22}
23
24#[cfg(windows)]
25fn win_get_full_path_name<'tcx>(path: &Path) -> InterpResult<'tcx, io::Result<PathBuf>> {
26    // We are on Windows so we can simply let the host do this.
27    interp_ok(path::absolute(path))
28}
29
30#[cfg(unix)]
31#[expect(clippy::get_first, clippy::arithmetic_side_effects)]
32fn win_get_full_path_name<'tcx>(path: &Path) -> InterpResult<'tcx, io::Result<PathBuf>> {
33    use std::sync::LazyLock;
34
35    use rustc_data_structures::fx::FxHashSet;
36
37    // We are on Unix, so we need to implement parts of the logic ourselves. `path` will use `/`
38    // separators, and the result should also use `/`.
39    // See <https://chrisdenton.github.io/omnipath/Overview.html#absolute-win32-paths> for more
40    // information about Windows paths.
41    // This does not handle all corner cases correctly, see
42    // <https://github.com/rust-lang/miri/pull/4262#issuecomment-2792168853> for more cursed
43    // examples.
44    let bytes = path.as_os_str().as_encoded_bytes();
45    // If it starts with `//./` or `//?/` then this is a magic special path, we just leave it
46    // unchanged.
47    if bytes.get(0).copied() == Some(b'/')
48        && bytes.get(1).copied() == Some(b'/')
49        && matches!(bytes.get(2), Some(b'.' | b'?'))
50        && bytes.get(3).copied() == Some(b'/')
51    {
52        return interp_ok(Ok(path.into()));
53    };
54    let is_unc = bytes.starts_with(b"//");
55    // Special treatment for Windows' magic filenames: they are treated as being relative to `//./`.
56    static MAGIC_FILENAMES: LazyLock<FxHashSet<&'static str>> = LazyLock::new(|| {
57        FxHashSet::from_iter([
58            "CON", "PRN", "AUX", "NUL", "COM1", "COM2", "COM3", "COM4", "COM5", "COM6", "COM7",
59            "COM8", "COM9", "LPT1", "LPT2", "LPT3", "LPT4", "LPT5", "LPT6", "LPT7", "LPT8", "LPT9",
60        ])
61    });
62    if str::from_utf8(bytes).is_ok_and(|s| MAGIC_FILENAMES.contains(&*s.to_ascii_uppercase())) {
63        let mut result: Vec<u8> = b"//./".into();
64        result.extend(bytes);
65        return interp_ok(Ok(bytes_to_os_str(&result)?.into()));
66    }
67    // Otherwise we try to do something kind of close to what Windows does, but this is probably not
68    // right in all cases.
69    let mut result: Vec<&[u8]> = vec![]; // will be a vector of components, joined by `/`.
70    let mut bytes = bytes; // the remaining bytes to process
71    let mut stop = false;
72    while !stop {
73        // Find next component, and advance `bytes`.
74        let mut component = match bytes.iter().position(|&b| b == b'/') {
75            Some(pos) => {
76                let (component, tail) = bytes.split_at(pos);
77                bytes = &tail[1..]; // remove the `/`.
78                component
79            }
80            None => {
81                // There's no more `/`.
82                stop = true;
83                let component = bytes;
84                bytes = &[];
85                component
86            }
87        };
88        // `NUL` and only `NUL` also gets changed to be relative to `//./` later in the path.
89        // (This changed with Windows 11; previously, all magic filenames behaved like this.)
90        // Also, this does not apply to UNC paths.
91        if !is_unc && component.eq_ignore_ascii_case(b"NUL") {
92            let mut result: Vec<u8> = b"//./".into();
93            result.extend(component);
94            return interp_ok(Ok(bytes_to_os_str(&result)?.into()));
95        }
96        // Deal with `..` -- Windows handles this entirely syntactically.
97        if component == b".." {
98            // Remove previous component, unless we are at the "root" already, then just ignore the `..`.
99            let is_root = {
100                // Paths like `/C:`.
101                result.len() == 2 && matches!(result[0], []) && matches!(result[1], [_, b':'])
102            } || {
103                // Paths like `//server/share`
104                result.len() == 4 && matches!(result[0], []) && matches!(result[1], [])
105            };
106            if !is_root {
107                result.pop();
108            }
109            continue;
110        }
111        // Preserve this component.
112        // Strip trailing `.`, but preserve trailing `..`. But not for UNC paths!
113        let len = component.len();
114        if !is_unc && len >= 2 && component[len - 1] == b'.' && component[len - 2] != b'.' {
115            component = &component[..len - 1];
116        }
117        // Add this component to output.
118        result.push(component);
119    }
120    // Drive letters must be followed by a `/`.
121    if result.len() == 2 && matches!(result[0], []) && matches!(result[1], [_, b':']) {
122        result.push(&[]);
123    }
124    // Let the host `absolute` function do working-dir handling.
125    let result = result.join(&b'/');
126    interp_ok(path::absolute(bytes_to_os_str(&result)?))
127}
128
129impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
130pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
131    fn emulate_foreign_item_inner(
132        &mut self,
133        link_name: Symbol,
134        abi: &FnAbi<'tcx, Ty<'tcx>>,
135        args: &[OpTy<'tcx>],
136        dest: &MPlaceTy<'tcx>,
137    ) -> InterpResult<'tcx, EmulateItemResult> {
138        let this = self.eval_context_mut();
139
140        // See `fn emulate_foreign_item_inner` in `shims/foreign_items.rs` for the general pattern.
141
142        // Windows API stubs.
143        // HANDLE = *mut c_void (formerly: isize)
144        // NTSTATUS = LONG = i32
145        // DWORD = ULONG = u32
146        // BOOL = i32
147        // BOOLEAN = u8
148        match link_name.as_str() {
149            // Environment related shims
150            "GetEnvironmentVariableW" => {
151                // FIXME: This does not have a direct test (#3179).
152                let [name, buf, size] = this.check_shim_sig(
153                    shim_sig!(extern "system" fn(*const _, *mut _, u32) -> u32),
154                    link_name,
155                    abi,
156                    args,
157                )?;
158                let result = this.GetEnvironmentVariableW(name, buf, size)?;
159                this.write_scalar(result, dest)?;
160            }
161            "SetEnvironmentVariableW" => {
162                // FIXME: This does not have a direct test (#3179).
163                let [name, value] = this.check_shim_sig(
164                    shim_sig!(extern "system" fn(*const _, *const _) -> winapi::BOOL),
165                    link_name,
166                    abi,
167                    args,
168                )?;
169                let result = this.SetEnvironmentVariableW(name, value)?;
170                this.write_scalar(result, dest)?;
171            }
172            "GetEnvironmentStringsW" => {
173                // FIXME: This does not have a direct test (#3179).
174                let [] = this.check_shim_sig(
175                    shim_sig!(extern "system" fn() -> *mut _),
176                    link_name,
177                    abi,
178                    args,
179                )?;
180                let result = this.GetEnvironmentStringsW()?;
181                this.write_pointer(result, dest)?;
182            }
183            "FreeEnvironmentStringsW" => {
184                // FIXME: This does not have a direct test (#3179).
185                let [env_block] = this.check_shim_sig(
186                    shim_sig!(extern "system" fn(*mut _) -> winapi::BOOL),
187                    link_name,
188                    abi,
189                    args,
190                )?;
191                let result = this.FreeEnvironmentStringsW(env_block)?;
192                this.write_scalar(result, dest)?;
193            }
194            "GetCurrentDirectoryW" => {
195                // FIXME: This does not have a direct test (#3179).
196                let [size, buf] = this.check_shim_sig(
197                    shim_sig!(extern "system" fn(u32, *mut _) -> u32),
198                    link_name,
199                    abi,
200                    args,
201                )?;
202                let result = this.GetCurrentDirectoryW(size, buf)?;
203                this.write_scalar(result, dest)?;
204            }
205            "SetCurrentDirectoryW" => {
206                // FIXME: This does not have a direct test (#3179).
207                let [path] = this.check_shim_sig(
208                    shim_sig!(extern "system" fn(*const _) -> winapi::BOOL),
209                    link_name,
210                    abi,
211                    args,
212                )?;
213                let result = this.SetCurrentDirectoryW(path)?;
214                this.write_scalar(result, dest)?;
215            }
216            "GetUserProfileDirectoryW" => {
217                // FIXME: This does not have a direct test (#3179).
218                let [token, buf, size] = this.check_shim_sig(
219                    shim_sig!(extern "system" fn(winapi::HANDLE, *mut _, *mut _) -> winapi::BOOL),
220                    link_name,
221                    abi,
222                    args,
223                )?;
224                let result = this.GetUserProfileDirectoryW(token, buf, size)?;
225                this.write_scalar(result, dest)?;
226            }
227            "GetCurrentProcessId" => {
228                // FIXME: This does not have a direct test (#3179).
229                let [] = this.check_shim_sig(
230                    shim_sig!(extern "system" fn() -> u32),
231                    link_name,
232                    abi,
233                    args,
234                )?;
235                let result = this.GetCurrentProcessId()?;
236                this.write_scalar(result, dest)?;
237            }
238            "GetTempPathW" => {
239                // FIXME: This does not have a direct test (#3179).
240                let [bufferlength, buffer] = this.check_shim_sig(
241                    shim_sig!(extern "system" fn(u32, *mut _) -> u32),
242                    link_name,
243                    abi,
244                    args,
245                )?;
246                let result = this.GetTempPathW(bufferlength, buffer)?;
247                this.write_scalar(result, dest)?;
248            }
249
250            // File related shims
251            "NtWriteFile" => {
252                let [
253                    handle,
254                    event,
255                    apc_routine,
256                    apc_context,
257                    io_status_block,
258                    buf,
259                    n,
260                    byte_offset,
261                    key,
262                ] = this.check_shim_sig(
263                    shim_sig!(
264                        extern "system" fn(
265                            winapi::HANDLE,
266                            winapi::HANDLE,
267                            *mut _,
268                            *mut _,
269                            *mut _,
270                            *mut _,
271                            u32,
272                            *mut _,
273                            *mut _,
274                        ) -> i32
275                    ),
276                    link_name,
277                    abi,
278                    args,
279                )?;
280                this.NtWriteFile(
281                    handle,
282                    event,
283                    apc_routine,
284                    apc_context,
285                    io_status_block,
286                    buf,
287                    n,
288                    byte_offset,
289                    key,
290                    dest,
291                )?;
292            }
293            "NtReadFile" => {
294                let [
295                    handle,
296                    event,
297                    apc_routine,
298                    apc_context,
299                    io_status_block,
300                    buf,
301                    n,
302                    byte_offset,
303                    key,
304                ] = this.check_shim_sig(
305                    shim_sig!(
306                        extern "system" fn(
307                            winapi::HANDLE,
308                            winapi::HANDLE,
309                            *mut _,
310                            *mut _,
311                            *mut _,
312                            *mut _,
313                            u32,
314                            *mut _,
315                            *mut _,
316                        ) -> i32
317                    ),
318                    link_name,
319                    abi,
320                    args,
321                )?;
322                this.NtReadFile(
323                    handle,
324                    event,
325                    apc_routine,
326                    apc_context,
327                    io_status_block,
328                    buf,
329                    n,
330                    byte_offset,
331                    key,
332                    dest,
333                )?;
334            }
335            "GetFullPathNameW" => {
336                // FIXME: This does not have a direct test (#3179).
337                let [filename, size, buffer, filepart] = this.check_shim_sig(
338                    shim_sig!(extern "system" fn(*const _, u32, *mut _, *mut _) -> u32),
339                    link_name,
340                    abi,
341                    args,
342                )?;
343                this.check_no_isolation("`GetFullPathNameW`")?;
344
345                let filename = this.read_pointer(filename)?;
346                let size = this.read_scalar(size)?.to_u32()?;
347                let buffer = this.read_pointer(buffer)?;
348                let filepart = this.read_pointer(filepart)?;
349
350                if !this.ptr_is_null(filepart)? {
351                    throw_unsup_format!("GetFullPathNameW: non-null `lpFilePart` is not supported");
352                }
353
354                let filename = this.read_path_from_wide_str(filename)?;
355                let result = match win_get_full_path_name(&filename)? {
356                    Err(err) => {
357                        this.set_last_error(err)?;
358                        Scalar::from_u32(0) // return zero upon failure
359                    }
360                    Ok(abs_filename) => {
361                        Scalar::from_u32(helpers::windows_check_buffer_size(
362                            this.write_path_to_wide_str(&abs_filename, buffer, size.into())?,
363                        ))
364                        // This can in fact return 0. It is up to the caller to set last_error to 0
365                        // beforehand and check it afterwards to exclude that case.
366                    }
367                };
368                this.write_scalar(result, dest)?;
369            }
370            "CreateFileW" => {
371                let [
372                    file_name,
373                    desired_access,
374                    share_mode,
375                    security_attributes,
376                    creation_disposition,
377                    flags_and_attributes,
378                    template_file,
379                ] = this.check_shim_sig(
380                    shim_sig!(
381                        extern "system" fn(
382                            *const _,
383                            u32,
384                            u32,
385                            *mut _,
386                            u32,
387                            u32,
388                            winapi::HANDLE,
389                        ) -> winapi::HANDLE
390                    ),
391                    link_name,
392                    abi,
393                    args,
394                )?;
395                let handle = this.CreateFileW(
396                    file_name,
397                    desired_access,
398                    share_mode,
399                    security_attributes,
400                    creation_disposition,
401                    flags_and_attributes,
402                    template_file,
403                )?;
404                this.write_scalar(handle.to_scalar(this), dest)?;
405            }
406            "GetFileInformationByHandle" => {
407                let [handle, info] = this.check_shim_sig(
408                    shim_sig!(extern "system" fn(winapi::HANDLE, *mut _) -> winapi::BOOL),
409                    link_name,
410                    abi,
411                    args,
412                )?;
413                let res = this.GetFileInformationByHandle(handle, info)?;
414                this.write_scalar(res, dest)?;
415            }
416            "SetFileInformationByHandle" => {
417                let [handle, class, info, size] = this.check_shim_sig(
418                    shim_sig!(
419                        extern "system" fn(
420                            winapi::HANDLE,
421                            winapi::FILE_INFO_BY_HANDLE_CLASS,
422                            *mut _,
423                            u32,
424                        ) -> winapi::BOOL
425                    ),
426                    link_name,
427                    abi,
428                    args,
429                )?;
430                let res = this.SetFileInformationByHandle(handle, class, info, size)?;
431                this.write_scalar(res, dest)?;
432            }
433            "FlushFileBuffers" => {
434                let [handle] = this.check_shim_sig(
435                    shim_sig!(extern "system" fn(winapi::HANDLE) -> winapi::BOOL),
436                    link_name,
437                    abi,
438                    args,
439                )?;
440                let res = this.FlushFileBuffers(handle)?;
441                this.write_scalar(res, dest)?;
442            }
443            "DeleteFileW" => {
444                let [file_name] = this.check_shim_sig(
445                    shim_sig!(extern "system" fn(*const _) -> winapi::BOOL),
446                    link_name,
447                    abi,
448                    args,
449                )?;
450                let res = this.DeleteFileW(file_name)?;
451                this.write_scalar(res, dest)?;
452            }
453            "SetFilePointerEx" => {
454                let [file, distance_to_move, new_file_pointer, move_method] = this.check_shim_sig(
455                    // i64 is actually a LARGE_INTEGER union of {u32, i32} and {i64}
456                    shim_sig!(extern "system" fn(winapi::HANDLE, i64, *mut _, u32) -> winapi::BOOL),
457                    link_name,
458                    abi,
459                    args,
460                )?;
461                let res =
462                    this.SetFilePointerEx(file, distance_to_move, new_file_pointer, move_method)?;
463                this.write_scalar(res, dest)?;
464            }
465            "MoveFileExW" => {
466                let [existing_name, new_name, flags] = this.check_shim_sig(
467                    shim_sig!(extern "system" fn(*const _, *const _, u32) -> winapi::BOOL),
468                    link_name,
469                    abi,
470                    args,
471                )?;
472                let res = this.MoveFileExW(existing_name, new_name, flags)?;
473                this.write_scalar(res, dest)?;
474            }
475
476            // Allocation
477            "HeapAlloc" => {
478                // FIXME: This does not have a direct test (#3179).
479                let [handle, flags, size] = this.check_shim_sig(
480                    shim_sig!(extern "system" fn(winapi::HANDLE, u32, usize) -> *mut _),
481                    link_name,
482                    abi,
483                    args,
484                )?;
485                this.read_target_isize(handle)?;
486                let flags = this.read_scalar(flags)?.to_u32()?;
487                let size = this.read_target_usize(size)?;
488                const HEAP_ZERO_MEMORY: u32 = 0x00000008;
489                let init = if (flags & HEAP_ZERO_MEMORY) == HEAP_ZERO_MEMORY {
490                    AllocInit::Zero
491                } else {
492                    AllocInit::Uninit
493                };
494                // Alignment is twice the pointer size.
495                // Source: <https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heapalloc>
496                let align = this.tcx.pointer_size().bytes().strict_mul(2);
497                let ptr = this.allocate_ptr(
498                    Size::from_bytes(size),
499                    Align::from_bytes(align).unwrap(),
500                    MiriMemoryKind::WinHeap.into(),
501                    init,
502                )?;
503                this.write_pointer(ptr, dest)?;
504            }
505            "HeapFree" => {
506                // FIXME: This does not have a direct test (#3179).
507                let [handle, flags, ptr] = this.check_shim_sig(
508                    shim_sig!(extern "system" fn(winapi::HANDLE, u32, *mut _) -> winapi::BOOL),
509                    link_name,
510                    abi,
511                    args,
512                )?;
513                this.read_target_isize(handle)?;
514                this.read_scalar(flags)?.to_u32()?;
515                let ptr = this.read_pointer(ptr)?;
516                // "This pointer can be NULL." It doesn't say what happens then, but presumably nothing.
517                // (https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heapfree)
518                if !this.ptr_is_null(ptr)? {
519                    this.deallocate_ptr(ptr, None, MiriMemoryKind::WinHeap.into())?;
520                }
521                this.write_scalar(Scalar::from_i32(1), dest)?;
522            }
523            "HeapReAlloc" => {
524                // FIXME: This does not have a direct test (#3179).
525                let [handle, flags, old_ptr, size] = this.check_shim_sig(
526                    shim_sig!(extern "system" fn(winapi::HANDLE, u32, *mut _, usize) -> *mut _),
527                    link_name,
528                    abi,
529                    args,
530                )?;
531                this.read_target_isize(handle)?;
532                this.read_scalar(flags)?.to_u32()?;
533                let old_ptr = this.read_pointer(old_ptr)?;
534                let size = this.read_target_usize(size)?;
535                let align = this.tcx.pointer_size().bytes().strict_mul(2); // same as above
536                // The docs say that `old_ptr` must come from an earlier HeapAlloc or HeapReAlloc,
537                // so unlike C `realloc` we do *not* allow a NULL here.
538                // (https://learn.microsoft.com/en-us/windows/win32/api/heapapi/nf-heapapi-heaprealloc)
539                let new_ptr = this.reallocate_ptr(
540                    old_ptr,
541                    None,
542                    Size::from_bytes(size),
543                    Align::from_bytes(align).unwrap(),
544                    MiriMemoryKind::WinHeap.into(),
545                    AllocInit::Uninit,
546                )?;
547                this.write_pointer(new_ptr, dest)?;
548            }
549            "LocalFree" => {
550                // FIXME: This does not have a direct test (#3179).
551                let [ptr] = this.check_shim_sig(
552                    shim_sig!(extern "system" fn(winapi::HLOCAL) -> winapi::HLOCAL),
553                    link_name,
554                    abi,
555                    args,
556                )?;
557                let ptr = this.read_pointer(ptr)?;
558                // "If the hMem parameter is NULL, LocalFree ignores the parameter and returns NULL."
559                // (https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-localfree)
560                if !this.ptr_is_null(ptr)? {
561                    this.deallocate_ptr(ptr, None, MiriMemoryKind::WinLocal.into())?;
562                }
563                this.write_null(dest)?;
564            }
565
566            // errno
567            "SetLastError" => {
568                let [error] = this.check_shim_sig(
569                    shim_sig!(extern "system" fn(u32) -> ()),
570                    link_name,
571                    abi,
572                    args,
573                )?;
574                let error = this.read_scalar(error)?;
575                this.set_last_error(error)?;
576            }
577            "GetLastError" => {
578                let [] = this.check_shim_sig(
579                    shim_sig!(extern "system" fn() -> u32),
580                    link_name,
581                    abi,
582                    args,
583                )?;
584                let last_error = this.get_last_error()?;
585                this.write_scalar(last_error, dest)?;
586            }
587            "RtlNtStatusToDosError" => {
588                let [status] = this.check_shim_sig(
589                    shim_sig!(extern "system" fn(i32) -> u32),
590                    link_name,
591                    abi,
592                    args,
593                )?;
594                let status = this.read_scalar(status)?.to_u32()?;
595                let err = match status {
596                    // STATUS_MEDIA_WRITE_PROTECTED => ERROR_WRITE_PROTECT
597                    0xC00000A2 => 19,
598                    // STATUS_FILE_INVALID => ERROR_FILE_INVALID
599                    0xC0000098 => 1006,
600                    // STATUS_DISK_FULL => ERROR_DISK_FULL
601                    0xC000007F => 112,
602                    // STATUS_IO_DEVICE_ERROR => ERROR_IO_DEVICE
603                    0xC0000185 => 1117,
604                    // STATUS_ACCESS_DENIED => ERROR_ACCESS_DENIED
605                    0xC0000022 => 5,
606                    // Anything without an error code => ERROR_MR_MID_NOT_FOUND
607                    _ => 317,
608                };
609                this.write_scalar(Scalar::from_i32(err), dest)?;
610            }
611
612            // Querying system information
613            "GetSystemInfo" => {
614                // FIXME: This does not have a direct test (#3179).
615                // Also called from `page_size` crate.
616                let [system_info] = this.check_shim_sig(
617                    shim_sig!(extern "system" fn(*mut _) -> ()),
618                    link_name,
619                    abi,
620                    args,
621                )?;
622                let system_info =
623                    this.deref_pointer_as(system_info, this.windows_ty_layout("SYSTEM_INFO"))?;
624                // Initialize with `0`.
625                this.write_bytes_ptr(
626                    system_info.ptr(),
627                    iter::repeat_n(0u8, system_info.layout.size.bytes_usize()),
628                )?;
629                // Set selected fields.
630                this.write_int_fields_named(
631                    &[
632                        ("dwPageSize", this.machine.page_size.into()),
633                        ("dwNumberOfProcessors", this.machine.num_cpus.into()),
634                    ],
635                    &system_info,
636                )?;
637            }
638
639            // Thread-local storage
640            "TlsAlloc" => {
641                // This just creates a key; Windows does not natively support TLS destructors.
642
643                // Create key and return it.
644                let [] = this.check_shim_sig(
645                    shim_sig!(extern "system" fn() -> u32),
646                    link_name,
647                    abi,
648                    args,
649                )?;
650                let key = this.machine.tls.create_tls_key(None, dest.layout.size)?;
651                this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?;
652            }
653            "TlsGetValue" => {
654                let [key] = this.check_shim_sig(
655                    shim_sig!(extern "system" fn(u32) -> *mut _),
656                    link_name,
657                    abi,
658                    args,
659                )?;
660                let key = u128::from(this.read_scalar(key)?.to_u32()?);
661                let active_thread = this.active_thread();
662                let ptr = this.machine.tls.load_tls(key, active_thread, this)?;
663                this.write_scalar(ptr, dest)?;
664            }
665            "TlsSetValue" => {
666                let [key, new_ptr] = this.check_shim_sig(
667                    shim_sig!(extern "system" fn(u32, *mut _) -> winapi::BOOL),
668                    link_name,
669                    abi,
670                    args,
671                )?;
672                let key = u128::from(this.read_scalar(key)?.to_u32()?);
673                let active_thread = this.active_thread();
674                let new_data = this.read_scalar(new_ptr)?;
675                this.machine.tls.store_tls(key, active_thread, new_data, &*this.tcx)?;
676
677                // Return success (`1`).
678                this.write_int(1, dest)?;
679            }
680            "TlsFree" => {
681                let [key] = this.check_shim_sig(
682                    shim_sig!(extern "system" fn(u32) -> winapi::BOOL),
683                    link_name,
684                    abi,
685                    args,
686                )?;
687                let key = u128::from(this.read_scalar(key)?.to_u32()?);
688                this.machine.tls.delete_tls_key(key)?;
689
690                // Return success (`1`).
691                this.write_int(1, dest)?;
692            }
693
694            // Fiber-local storage - similar to TLS but supports destructors.
695            "FlsAlloc" => {
696                // Create key and return it.
697                let [dtor] = this.check_shim_sig(
698                    shim_sig!(extern "system" fn(winapi::PFLS_CALLBACK_FUNCTION) -> u32),
699                    link_name,
700                    abi,
701                    args,
702                )?;
703                let dtor = this.read_pointer(dtor)?;
704
705                // Extract the function type out of the signature (that seems easier than constructing it ourselves).
706                let dtor = if !this.ptr_is_null(dtor)? {
707                    Some((
708                        this.get_ptr_fn(dtor)?.as_instance()?,
709                        this.machine.current_user_relevant_span(),
710                    ))
711                } else {
712                    None
713                };
714
715                let key = this.machine.tls.create_tls_key(dtor, dest.layout.size)?;
716                this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?;
717            }
718            "FlsGetValue" => {
719                let [key] = this.check_shim_sig(
720                    shim_sig!(extern "system" fn(u32) -> *mut _),
721                    link_name,
722                    abi,
723                    args,
724                )?;
725                let key = u128::from(this.read_scalar(key)?.to_u32()?);
726                let active_thread = this.active_thread();
727                let ptr = this.machine.tls.load_tls(key, active_thread, this)?;
728                this.write_scalar(ptr, dest)?;
729            }
730            "FlsSetValue" => {
731                let [key, new_ptr] = this.check_shim_sig(
732                    shim_sig!(extern "system" fn(u32, *mut _) -> winapi::BOOL),
733                    link_name,
734                    abi,
735                    args,
736                )?;
737                let key = u128::from(this.read_scalar(key)?.to_u32()?);
738                let active_thread = this.active_thread();
739                let new_data = this.read_scalar(new_ptr)?;
740                this.machine.tls.store_tls(key, active_thread, new_data, &*this.tcx)?;
741
742                // Return success (`1`).
743                this.write_int(1, dest)?;
744            }
745            "FlsFree" => {
746                let [key] = this.check_shim_sig(
747                    shim_sig!(extern "system" fn(u32) -> winapi::BOOL),
748                    link_name,
749                    abi,
750                    args,
751                )?;
752                let key = u128::from(this.read_scalar(key)?.to_u32()?);
753                let tls_entry = this.machine.tls.delete_tls_key(key)?;
754
755                // FIXME: We should run the destructor here *for all threads*. But that's non-trivial and std doesn't need it so we bail out with an "unsupported" error.
756                if !tls_entry.data.is_empty() && tls_entry.dtor.is_some() {
757                    throw_unsup_format!(
758                        "calling `FlsFree` on a key with an associated dtor is not supported"
759                    );
760                }
761
762                // Return success (`1`).
763                this.write_int(1, dest)?;
764            }
765            "IsThreadAFiber" => {
766                let [] = this.check_shim_sig(
767                    shim_sig!(extern "system" fn() -> winapi::BOOL),
768                    link_name,
769                    abi,
770                    args,
771                )?;
772
773                // Return FALSE, as Miri does not support fibers.
774                this.write_int(0, dest)?;
775            }
776
777            // Access to command-line arguments
778            "GetCommandLineW" => {
779                // FIXME: This does not have a direct test (#3179).
780                let [] = this.check_shim_sig(
781                    shim_sig!(extern "system" fn() -> *mut _),
782                    link_name,
783                    abi,
784                    args,
785                )?;
786                this.write_pointer(
787                    this.machine.cmd_line.expect("machine must be initialized"),
788                    dest,
789                )?;
790            }
791
792            // Time related shims
793            "GetSystemTimeAsFileTime" | "GetSystemTimePreciseAsFileTime" => {
794                // FIXME: This does not have a direct test (#3179).
795                let [filetime] = this.check_shim_sig(
796                    shim_sig!(extern "system" fn(*mut _) -> ()),
797                    link_name,
798                    abi,
799                    args,
800                )?;
801                this.GetSystemTimeAsFileTime(link_name.as_str(), filetime)?;
802            }
803            "QueryPerformanceCounter" => {
804                // FIXME: This does not have a direct test (#3179).
805                let [performance_count] = this.check_shim_sig(
806                    shim_sig!(extern "system" fn(*mut _) -> winapi::BOOL),
807                    link_name,
808                    abi,
809                    args,
810                )?;
811                let result = this.QueryPerformanceCounter(performance_count)?;
812                this.write_scalar(result, dest)?;
813            }
814            "QueryPerformanceFrequency" => {
815                // FIXME: This does not have a direct test (#3179).
816                let [frequency] = this.check_shim_sig(
817                    shim_sig!(extern "system" fn(*mut _) -> winapi::BOOL),
818                    link_name,
819                    abi,
820                    args,
821                )?;
822                let result = this.QueryPerformanceFrequency(frequency)?;
823                this.write_scalar(result, dest)?;
824            }
825            "Sleep" => {
826                // FIXME: This does not have a direct test (#3179).
827                let [timeout] = this.check_shim_sig(
828                    shim_sig!(extern "system" fn(u32) -> ()),
829                    link_name,
830                    abi,
831                    args,
832                )?;
833
834                this.Sleep(timeout)?;
835            }
836            "CreateWaitableTimerExW" => {
837                // FIXME: This does not have a direct test (#3179).
838                let [attributes, name, flags, access] = this.check_shim_sig(
839                    shim_sig!(extern "system" fn(*mut _, *const _, u32, u32) -> winapi::HANDLE),
840                    link_name,
841                    abi,
842                    args,
843                )?;
844                this.read_pointer(attributes)?;
845                this.read_pointer(name)?;
846                this.read_scalar(flags)?.to_u32()?;
847                this.read_scalar(access)?.to_u32()?;
848                // Unimplemented. Always return failure.
849                let not_supported = this.eval_windows("c", "ERROR_NOT_SUPPORTED");
850                this.set_last_error(not_supported)?;
851                this.write_null(dest)?;
852            }
853
854            // Synchronization primitives
855            "InitOnceBeginInitialize" => {
856                let [ptr, flags, pending, context] = this.check_shim_sig(
857                    shim_sig!(extern "system" fn(*mut _, u32, *mut _, *mut _) -> winapi::BOOL),
858                    link_name,
859                    abi,
860                    args,
861                )?;
862                this.InitOnceBeginInitialize(ptr, flags, pending, context, dest)?;
863            }
864            "InitOnceComplete" => {
865                let [ptr, flags, context] = this.check_shim_sig(
866                    shim_sig!(extern "system" fn(*mut _, u32, *mut _) -> winapi::BOOL),
867                    link_name,
868                    abi,
869                    args,
870                )?;
871                let result = this.InitOnceComplete(ptr, flags, context)?;
872                this.write_scalar(result, dest)?;
873            }
874            "WaitOnAddress" => {
875                // FIXME: This does not have a direct test (#3179).
876                let [ptr_op, compare_op, size_op, timeout_op] = this.check_shim_sig(
877                    // First pointer is volatile
878                    shim_sig!(extern "system" fn(*mut _, *mut _, usize, u32) -> winapi::BOOL),
879                    link_name,
880                    abi,
881                    args,
882                )?;
883
884                this.WaitOnAddress(ptr_op, compare_op, size_op, timeout_op, dest)?;
885            }
886            "WakeByAddressSingle" => {
887                // FIXME: This does not have a direct test (#3179).
888                let [ptr_op] = this.check_shim_sig(
889                    shim_sig!(extern "system" fn(*mut _) -> ()),
890                    link_name,
891                    abi,
892                    args,
893                )?;
894
895                this.WakeByAddressSingle(ptr_op)?;
896            }
897            "WakeByAddressAll" => {
898                // FIXME: This does not have a direct test (#3179).
899                let [ptr_op] = this.check_shim_sig(
900                    shim_sig!(extern "system" fn(*mut _) -> ()),
901                    link_name,
902                    abi,
903                    args,
904                )?;
905
906                this.WakeByAddressAll(ptr_op)?;
907            }
908
909            // Dynamic symbol loading
910            "GetProcAddress" => {
911                // FIXME: This does not have a direct test (#3179).
912                let [module, proc_name] = this.check_shim_sig(
913                    shim_sig!(extern "system" fn(winapi::HMODULE, *const _) -> winapi::FARPROC),
914                    link_name,
915                    abi,
916                    args,
917                )?;
918                this.read_target_isize(module)?;
919                let name = this.read_c_str(this.read_pointer(proc_name)?)?;
920                if let Ok(name) = str::from_utf8(name)
921                    && is_dyn_sym(name)
922                {
923                    let ptr = this.fn_ptr(FnVal::Other(DynSym::from_str(name)));
924                    this.write_pointer(ptr, dest)?;
925                } else {
926                    this.write_null(dest)?;
927                }
928            }
929
930            // Threading
931            "CreateThread" => {
932                let [security, stacksize, start, arg, flags, thread] = this.check_shim_sig(
933                    shim_sig!(
934                        extern "system" fn(
935                            *mut _,
936                            usize,
937                            *mut _,
938                            *mut _,
939                            u32,
940                            *mut _,
941                        ) -> winapi::HANDLE
942                    ),
943                    link_name,
944                    abi,
945                    args,
946                )?;
947
948                let thread_id =
949                    this.CreateThread(security, stacksize, start, arg, flags, thread)?;
950
951                this.write_scalar(Handle::Thread(thread_id).to_scalar(this), dest)?;
952            }
953            "WaitForSingleObject" => {
954                let [handle, timeout] = this.check_shim_sig(
955                    shim_sig!(extern "system" fn(winapi::HANDLE, u32) -> u32),
956                    link_name,
957                    abi,
958                    args,
959                )?;
960
961                this.WaitForSingleObject(handle, timeout, dest)?;
962            }
963            "GetCurrentProcess" => {
964                let [] = this.check_shim_sig(
965                    shim_sig!(extern "system" fn() -> winapi::HANDLE),
966                    link_name,
967                    abi,
968                    args,
969                )?;
970
971                this.write_scalar(
972                    Handle::Pseudo(PseudoHandle::CurrentProcess).to_scalar(this),
973                    dest,
974                )?;
975            }
976            "GetCurrentThread" => {
977                let [] = this.check_shim_sig(
978                    shim_sig!(extern "system" fn() -> winapi::HANDLE),
979                    link_name,
980                    abi,
981                    args,
982                )?;
983
984                this.write_scalar(
985                    Handle::Pseudo(PseudoHandle::CurrentThread).to_scalar(this),
986                    dest,
987                )?;
988            }
989            "SetThreadDescription" => {
990                let [handle, name] = this.check_shim_sig(
991                    shim_sig!(extern "system" fn(winapi::HANDLE, *const _) -> i32),
992                    link_name,
993                    abi,
994                    args,
995                )?;
996
997                let handle = this.read_handle(handle, "SetThreadDescription")?;
998                let name = this.read_wide_str(this.read_pointer(name)?)?;
999
1000                let thread = match handle {
1001                    Handle::Thread(thread) => thread,
1002                    Handle::Pseudo(PseudoHandle::CurrentThread) => this.active_thread(),
1003                    _ => this.invalid_handle("SetThreadDescription")?,
1004                };
1005                // FIXME: use non-lossy conversion
1006                this.set_thread_name(thread, String::from_utf16_lossy(&name).into_bytes());
1007                this.write_scalar(Scalar::from_u32(0), dest)?;
1008            }
1009            "GetThreadDescription" => {
1010                let [handle, name_ptr] = this.check_shim_sig(
1011                    shim_sig!(extern "system" fn(winapi::HANDLE, *mut _) -> i32),
1012                    link_name,
1013                    abi,
1014                    args,
1015                )?;
1016
1017                let handle = this.read_handle(handle, "GetThreadDescription")?;
1018                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
1019
1020                let thread = match handle {
1021                    Handle::Thread(thread) => thread,
1022                    Handle::Pseudo(PseudoHandle::CurrentThread) => this.active_thread(),
1023                    _ => this.invalid_handle("GetThreadDescription")?,
1024                };
1025                // Looks like the default thread name is empty.
1026                let name = this.get_thread_name(thread).unwrap_or(b"").to_owned();
1027                let name = this.alloc_os_str_as_wide_str(
1028                    bytes_to_os_str(&name)?,
1029                    MiriMemoryKind::WinLocal.into(),
1030                )?;
1031                let name = Scalar::from_maybe_pointer(name, this);
1032                let res = Scalar::from_u32(0);
1033
1034                this.write_scalar(name, &name_ptr)?;
1035                this.write_scalar(res, dest)?;
1036            }
1037            "GetThreadId" => {
1038                let [handle] = this.check_shim_sig(
1039                    shim_sig!(extern "system" fn(winapi::HANDLE) -> u32),
1040                    link_name,
1041                    abi,
1042                    args,
1043                )?;
1044                let handle = this.read_handle(handle, "GetThreadId")?;
1045                let thread = match handle {
1046                    Handle::Thread(thread) => thread,
1047                    Handle::Pseudo(PseudoHandle::CurrentThread) => this.active_thread(),
1048                    _ => this.invalid_handle("GetThreadDescription")?,
1049                };
1050                this.write_scalar(Scalar::from_u32(thread.to_u32()), dest)?;
1051            }
1052            "GetCurrentThreadId" => {
1053                let [] = this.check_shim_sig(
1054                    shim_sig!(extern "system" fn() -> u32),
1055                    link_name,
1056                    abi,
1057                    args,
1058                )?;
1059                this.write_scalar(Scalar::from_u32(this.active_thread().to_u32()), dest)?;
1060            }
1061
1062            // Miscellaneous
1063            "ExitProcess" => {
1064                // FIXME: This does not have a direct test (#3179).
1065                let [code] = this.check_shim_sig(
1066                    shim_sig!(extern "system" fn(u32) -> ()),
1067                    link_name,
1068                    abi,
1069                    args,
1070                )?;
1071                // Windows technically uses u32, but we unify everything to a Unix-style i32.
1072                let code = this.read_scalar(code)?.to_i32()?;
1073                throw_machine_stop!(TerminationInfo::Exit { code, leak_check: false });
1074            }
1075            "SystemFunction036" => {
1076                // used by getrandom 0.1
1077                // This is really 'RtlGenRandom'.
1078                let [ptr, len] = this.check_shim_sig(
1079                    // Returns winapi::BOOLEAN, which is a byte
1080                    shim_sig!(extern "system" fn(*mut _, u32) -> u8),
1081                    link_name,
1082                    abi,
1083                    args,
1084                )?;
1085                let ptr = this.read_pointer(ptr)?;
1086                let len = this.read_scalar(len)?.to_u32()?;
1087                this.gen_random(ptr, len.into())?;
1088                this.write_scalar(Scalar::from_bool(true), dest)?;
1089            }
1090            "ProcessPrng" => {
1091                // FIXME: This does not have a direct test (#3179).
1092                // used by `std`
1093                let [ptr, len] = this.check_shim_sig(
1094                    shim_sig!(extern "system" fn(*mut _, usize) -> winapi::BOOL),
1095                    link_name,
1096                    abi,
1097                    args,
1098                )?;
1099                let ptr = this.read_pointer(ptr)?;
1100                let len = this.read_target_usize(len)?;
1101                this.gen_random(ptr, len)?;
1102                this.write_int(1, dest)?;
1103            }
1104            "BCryptGenRandom" => {
1105                // used by getrandom 0.2
1106                let [algorithm, ptr, len, flags] = this.check_shim_sig(
1107                    shim_sig!(extern "system" fn(*mut _, *mut _, u32, u32) -> i32),
1108                    link_name,
1109                    abi,
1110                    args,
1111                )?;
1112                let algorithm = this.read_scalar(algorithm)?;
1113                let algorithm = algorithm.to_target_usize(this)?;
1114                let ptr = this.read_pointer(ptr)?;
1115                let len = this.read_scalar(len)?.to_u32()?;
1116                let flags = this.read_scalar(flags)?.to_u32()?;
1117                match flags {
1118                    0 => {
1119                        if algorithm != 0x81 {
1120                            // BCRYPT_RNG_ALG_HANDLE
1121                            throw_unsup_format!(
1122                                "BCryptGenRandom algorithm must be BCRYPT_RNG_ALG_HANDLE when the flag is 0"
1123                            );
1124                        }
1125                    }
1126                    2 => {
1127                        // BCRYPT_USE_SYSTEM_PREFERRED_RNG
1128                        if algorithm != 0 {
1129                            throw_unsup_format!(
1130                                "BCryptGenRandom algorithm must be NULL when the flag is BCRYPT_USE_SYSTEM_PREFERRED_RNG"
1131                            );
1132                        }
1133                    }
1134                    _ => {
1135                        throw_unsup_format!(
1136                            "BCryptGenRandom is only supported with BCRYPT_USE_SYSTEM_PREFERRED_RNG or BCRYPT_RNG_ALG_HANDLE"
1137                        );
1138                    }
1139                }
1140                this.gen_random(ptr, len.into())?;
1141                this.write_null(dest)?; // STATUS_SUCCESS
1142            }
1143            "GetConsoleScreenBufferInfo" => {
1144                // FIXME: This does not have a direct test (#3179).
1145                // `term` needs this, so we fake it.
1146                let [console, buffer_info] = this.check_shim_sig(
1147                    shim_sig!(extern "system" fn(winapi::HANDLE, *mut _) -> winapi::BOOL),
1148                    link_name,
1149                    abi,
1150                    args,
1151                )?;
1152                this.read_target_isize(console)?;
1153                // FIXME: this should use deref_pointer_as, but CONSOLE_SCREEN_BUFFER_INFO is not in std
1154                this.deref_pointer(buffer_info)?;
1155                // Indicate an error.
1156                // FIXME: we should set last_error, but to what?
1157                this.write_null(dest)?;
1158            }
1159            "GetStdHandle" => {
1160                // FIXME: This does not have a direct test (#3179).
1161                let [which] = this.check_shim_sig(
1162                    shim_sig!(extern "system" fn(u32) -> winapi::HANDLE),
1163                    link_name,
1164                    abi,
1165                    args,
1166                )?;
1167                let res = this.GetStdHandle(which)?;
1168                this.write_scalar(res, dest)?;
1169            }
1170            "DuplicateHandle" => {
1171                let [src_proc, src_handle, target_proc, target_handle, access, inherit, options] =
1172                    this.check_shim_sig(
1173                        shim_sig!(
1174                            extern "system" fn(
1175                                winapi::HANDLE,
1176                                winapi::HANDLE,
1177                                winapi::HANDLE,
1178                                *mut _,
1179                                u32,
1180                                winapi::BOOL,
1181                                u32,
1182                            ) -> winapi::BOOL
1183                        ),
1184                        link_name,
1185                        abi,
1186                        args,
1187                    )?;
1188                let res = this.DuplicateHandle(
1189                    src_proc,
1190                    src_handle,
1191                    target_proc,
1192                    target_handle,
1193                    access,
1194                    inherit,
1195                    options,
1196                )?;
1197                this.write_scalar(res, dest)?;
1198            }
1199            "CloseHandle" => {
1200                let [handle] = this.check_shim_sig(
1201                    shim_sig!(extern "system" fn(winapi::HANDLE) -> winapi::BOOL),
1202                    link_name,
1203                    abi,
1204                    args,
1205                )?;
1206
1207                let ret = this.CloseHandle(handle)?;
1208
1209                this.write_scalar(ret, dest)?;
1210            }
1211            "GetModuleFileNameW" => {
1212                // FIXME: This does not have a direct test (#3179).
1213                let [handle, filename, size] = this.check_shim_sig(
1214                    shim_sig!(extern "system" fn(winapi::HMODULE, *mut _, u32) -> u32),
1215                    link_name,
1216                    abi,
1217                    args,
1218                )?;
1219                this.check_no_isolation("`GetModuleFileNameW`")?;
1220
1221                let handle = this.read_handle(handle, "GetModuleFileNameW")?;
1222                let filename = this.read_pointer(filename)?;
1223                let size = this.read_scalar(size)?.to_u32()?;
1224
1225                if handle != Handle::Null {
1226                    throw_unsup_format!("`GetModuleFileNameW` only supports the NULL handle");
1227                }
1228
1229                // Using the host current_exe is a bit off, but consistent with Linux
1230                // (where stdlib reads /proc/self/exe).
1231                let path = std::env::current_exe().unwrap();
1232                let (all_written, size_needed) =
1233                    this.write_path_to_wide_str_truncated(&path, filename, size.into())?;
1234
1235                if all_written {
1236                    // If the function succeeds, the return value is the length of the string that
1237                    // is copied to the buffer, in characters, not including the terminating null
1238                    // character.
1239                    this.write_int(size_needed.strict_sub(1), dest)?;
1240                } else {
1241                    // If the buffer is too small to hold the module name, the string is truncated
1242                    // to nSize characters including the terminating null character, the function
1243                    // returns nSize, and the function sets the last error to
1244                    // ERROR_INSUFFICIENT_BUFFER.
1245                    this.write_int(size, dest)?;
1246                    let insufficient_buffer = this.eval_windows("c", "ERROR_INSUFFICIENT_BUFFER");
1247                    this.set_last_error(insufficient_buffer)?;
1248                }
1249            }
1250            "FormatMessageW" => {
1251                // FIXME: This does not have a direct test (#3179).
1252                let [flags, module, message_id, language_id, buffer, size, arguments] = this
1253                    .check_shim_sig(
1254                        shim_sig!(
1255                            extern "system" fn(u32, *const _, u32, u32, *mut _, u32, *mut _) -> u32
1256                        ),
1257                        link_name,
1258                        abi,
1259                        args,
1260                    )?;
1261
1262                let flags = this.read_scalar(flags)?.to_u32()?;
1263                let _module = this.read_pointer(module)?; // seems to contain a module name
1264                let message_id = this.read_scalar(message_id)?;
1265                let _language_id = this.read_scalar(language_id)?.to_u32()?;
1266                let buffer = this.read_pointer(buffer)?;
1267                let size = this.read_scalar(size)?.to_u32()?;
1268                let _arguments = this.read_pointer(arguments)?;
1269
1270                // We only support `FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS`
1271                // This also means `arguments` can be ignored.
1272                if flags != 4096u32 | 512u32 {
1273                    throw_unsup_format!("FormatMessageW: unsupported flags {flags:#x}");
1274                }
1275
1276                let error = this.try_errnum_to_io_error(message_id)?;
1277                let formatted = match error {
1278                    Some(err) => format!("{err}"),
1279                    None => format!("<unknown error in FormatMessageW: {message_id}>"),
1280                };
1281                let (complete, length) =
1282                    this.write_os_str_to_wide_str(OsStr::new(&formatted), buffer, size.into())?;
1283                if !complete {
1284                    // The API docs don't say what happens when the buffer is not big enough...
1285                    // Let's just bail.
1286                    throw_unsup_format!("FormatMessageW: buffer not big enough");
1287                }
1288                // The return value is the number of characters stored *excluding* the null terminator.
1289                this.write_int(length.strict_sub(1), dest)?;
1290            }
1291
1292            "_Unwind_RaiseException" => {
1293                // FIXME: This does not have a direct test (#3179).
1294                // This is not formally part of POSIX, but it is very wide-spread on POSIX systems.
1295                // It was originally specified as part of the Itanium C++ ABI:
1296                // https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html#base-throw.
1297                // MinGW implements _Unwind_RaiseException on top of SEH exceptions.
1298                if this.tcx.sess.target.env != Env::Gnu {
1299                    throw_unsup_format!(
1300                        "`_Unwind_RaiseException` is not supported on non-MinGW Windows",
1301                    );
1302                }
1303                // This function looks and behaves exactly like miri_start_unwind.
1304                let [payload] = this.check_shim_sig(
1305                    shim_sig!(extern "C" fn(*mut _) -> unwind::libunwind::_Unwind_Reason_Code),
1306                    link_name,
1307                    abi,
1308                    args,
1309                )?;
1310                this.handle_miri_start_unwind(payload)?;
1311                return interp_ok(EmulateItemResult::NeedsUnwind);
1312            }
1313
1314            // Incomplete shims that we "stub out" just to get pre-main initialization code to work.
1315            // These shims are enabled only when the caller is in the standard library.
1316            "GetProcessHeap" if this.frame_in_std() => {
1317                let [] = this.check_shim_sig(
1318                    shim_sig!(extern "system" fn() -> winapi::HANDLE),
1319                    link_name,
1320                    abi,
1321                    args,
1322                )?;
1323                // Just fake a HANDLE
1324                // It's fine to not use the Handle type here because its a stub
1325                this.write_int(1, dest)?;
1326            }
1327            "GetModuleHandleA" if this.frame_in_std() => {
1328                let [_module_name] = this.check_shim_sig(
1329                    shim_sig!(extern "system" fn(*const _) -> winapi::HMODULE),
1330                    link_name,
1331                    abi,
1332                    args,
1333                )?;
1334                // We need to return something non-null here to make `compat_fn!` work.
1335                this.write_int(1, dest)?;
1336            }
1337            "SetConsoleTextAttribute" if this.frame_in_std() => {
1338                let [_console_output, _attribute] = this.check_shim_sig(
1339                    shim_sig!(extern "system" fn(winapi::HANDLE, u16) -> winapi::BOOL),
1340                    link_name,
1341                    abi,
1342                    args,
1343                )?;
1344                // Pretend these does not exist / nothing happened, by returning zero.
1345                this.write_null(dest)?;
1346            }
1347            "GetConsoleMode" if this.frame_in_std() => {
1348                let [console, mode] = this.check_shim_sig(
1349                    shim_sig!(extern "system" fn(winapi::HANDLE, *mut _) -> winapi::BOOL),
1350                    link_name,
1351                    abi,
1352                    args,
1353                )?;
1354                this.read_target_isize(console)?;
1355                this.deref_pointer_as(mode, this.machine.layouts.u32)?;
1356                // Indicate an error.
1357                this.write_null(dest)?;
1358            }
1359            "GetFileType" if this.frame_in_std() => {
1360                let [_file] = this.check_shim_sig(
1361                    shim_sig!(extern "system" fn(winapi::HANDLE) -> u32),
1362                    link_name,
1363                    abi,
1364                    args,
1365                )?;
1366                // Return unknown file type.
1367                this.write_null(dest)?;
1368            }
1369            "AddVectoredExceptionHandler" if this.frame_in_std() => {
1370                let [_first, _handler] = this.check_shim_sig(
1371                    shim_sig!(extern "system" fn(u32, *mut _) -> *mut _),
1372                    link_name,
1373                    abi,
1374                    args,
1375                )?;
1376                // Any non zero value works for the stdlib. This is just used for stack overflows anyway.
1377                this.write_int(1, dest)?;
1378            }
1379            "SetThreadStackGuarantee" if this.frame_in_std() => {
1380                let [_stack_size_in_bytes] = this.check_shim_sig(
1381                    shim_sig!(extern "system" fn(*mut _) -> winapi::BOOL),
1382                    link_name,
1383                    abi,
1384                    args,
1385                )?;
1386                // Any non zero value works for the stdlib. This is just used for stack overflows anyway.
1387                this.write_int(1, dest)?;
1388            }
1389            // this is only callable from std because we know that std ignores the return value
1390            "SwitchToThread" if this.frame_in_std() => {
1391                let [] = this.check_shim_sig(
1392                    shim_sig!(extern "system" fn() -> winapi::BOOL),
1393                    link_name,
1394                    abi,
1395                    args,
1396                )?;
1397
1398                this.yield_active_thread();
1399
1400                // FIXME: this should return a nonzero value if this call does result in switching to another thread.
1401                this.write_null(dest)?;
1402            }
1403
1404            _ => return interp_ok(EmulateItemResult::NotSupported),
1405        }
1406
1407        interp_ok(EmulateItemResult::NeedsReturn)
1408    }
1409}