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