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