Skip to main content

miri/shims/windows/
foreign_items.rs

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