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