miri/shims/windows/
foreign_items.rs

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