3 use rustc_span::Symbol;
4 use rustc_target::abi::Size;
5 use rustc_target::spec::abi::Abi;
8 use shims::foreign_items::EmulateByNameResult;
9 use shims::windows::handle::{EvalContextExt as _, Handle, PseudoHandle};
10 use shims::windows::sync::EvalContextExt as _;
11 use shims::windows::thread::EvalContextExt as _;
13 use smallvec::SmallVec;
15 impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
16 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
17 fn emulate_foreign_item_by_name(
21 args: &[OpTy<'tcx, Provenance>],
22 dest: &PlaceTy<'tcx, Provenance>,
23 ) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> {
24 let this = self.eval_context_mut();
26 // See `fn emulate_foreign_item_by_name` in `shims/foreign_items.rs` for the general pattern.
30 // NTSTATUS = LONH = i32
31 // DWORD = ULONG = u32
34 match link_name.as_str() {
35 // Environment related shims
36 "GetEnvironmentVariableW" => {
37 let [name, buf, size] =
38 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
39 let result = this.GetEnvironmentVariableW(name, buf, size)?;
40 this.write_scalar(result, dest)?;
42 "SetEnvironmentVariableW" => {
44 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
45 let result = this.SetEnvironmentVariableW(name, value)?;
46 this.write_scalar(result, dest)?;
48 "GetEnvironmentStringsW" => {
49 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
50 let result = this.GetEnvironmentStringsW()?;
51 this.write_pointer(result, dest)?;
53 "FreeEnvironmentStringsW" => {
55 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
56 let result = this.FreeEnvironmentStringsW(env_block)?;
57 this.write_scalar(result, dest)?;
59 "GetCurrentDirectoryW" => {
61 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
62 let result = this.GetCurrentDirectoryW(size, buf)?;
63 this.write_scalar(result, dest)?;
65 "SetCurrentDirectoryW" => {
67 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
68 let result = this.SetCurrentDirectoryW(path)?;
69 this.write_scalar(result, dest)?;
74 let [handle, flags, size] =
75 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
76 this.read_machine_isize(handle)?;
77 let flags = this.read_scalar(flags)?.to_u32()?;
78 let size = this.read_machine_usize(size)?;
79 let zero_init = (flags & 0x00000008) != 0; // HEAP_ZERO_MEMORY
80 let res = this.malloc(size, zero_init, MiriMemoryKind::WinHeap)?;
81 this.write_pointer(res, dest)?;
84 let [handle, flags, ptr] =
85 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
86 this.read_machine_isize(handle)?;
87 this.read_scalar(flags)?.to_u32()?;
88 let ptr = this.read_pointer(ptr)?;
89 this.free(ptr, MiriMemoryKind::WinHeap)?;
90 this.write_scalar(Scalar::from_i32(1), dest)?;
93 let [handle, flags, ptr, size] =
94 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
95 this.read_machine_isize(handle)?;
96 this.read_scalar(flags)?.to_u32()?;
97 let ptr = this.read_pointer(ptr)?;
98 let size = this.read_machine_usize(size)?;
99 let res = this.realloc(ptr, size, MiriMemoryKind::WinHeap)?;
100 this.write_pointer(res, dest)?;
106 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
107 let error = this.read_scalar(error)?;
108 this.set_last_error(error)?;
111 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
112 let last_error = this.get_last_error()?;
113 this.write_scalar(last_error, dest)?;
116 // Querying system information
118 // Also called from `page_size` crate.
120 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
121 let system_info = this.deref_operand(system_info)?;
122 // Initialize with `0`.
123 this.write_bytes_ptr(
125 iter::repeat(0u8).take(system_info.layout.size.bytes_usize()),
127 // Set selected fields.
128 let word_layout = this.machine.layouts.u16;
129 let dword_layout = this.machine.layouts.u32;
130 let usize_layout = this.machine.layouts.usize;
132 // Using `mplace_field` is error-prone, see: https://github.com/rust-lang/miri/issues/2136.
133 // Pointer fields have different sizes on different targets.
134 // To avoid all these issue we calculate the offsets ourselves.
136 word_layout.size, // 0, wProcessorArchitecture : WORD
137 word_layout.size, // 1, wReserved : WORD
138 dword_layout.size, // 2, dwPageSize : DWORD
139 usize_layout.size, // 3, lpMinimumApplicationAddress : LPVOID
140 usize_layout.size, // 4, lpMaximumApplicationAddress : LPVOID
141 usize_layout.size, // 5, dwActiveProcessorMask : DWORD_PTR
142 dword_layout.size, // 6, dwNumberOfProcessors : DWORD
143 dword_layout.size, // 7, dwProcessorType : DWORD
144 dword_layout.size, // 8, dwAllocationGranularity : DWORD
145 word_layout.size, // 9, wProcessorLevel : WORD
146 word_layout.size, // 10, wProcessorRevision : WORD
148 let field_offsets: SmallVec<[Size; 11]> = field_sizes
151 .scan(Size::ZERO, |a, x| {
159 let page_size = system_info.offset(field_offsets[2], dword_layout, &this.tcx)?;
161 Scalar::from_int(PAGE_SIZE, dword_layout.size),
164 // Set number of processors.
165 let num_cpus = system_info.offset(field_offsets[6], dword_layout, &this.tcx)?;
167 Scalar::from_int(this.machine.num_cpus, dword_layout.size),
172 // Thread-local storage
174 // This just creates a key; Windows does not natively support TLS destructors.
176 // Create key and return it.
177 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
178 let key = this.machine.tls.create_tls_key(None, dest.layout.size)?;
179 this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?;
182 let [key] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
183 let key = u128::from(this.read_scalar(key)?.to_u32()?);
184 let active_thread = this.get_active_thread();
185 let ptr = this.machine.tls.load_tls(key, active_thread, this)?;
186 this.write_scalar(ptr, dest)?;
190 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
191 let key = u128::from(this.read_scalar(key)?.to_u32()?);
192 let active_thread = this.get_active_thread();
193 let new_data = this.read_scalar(new_ptr)?;
194 this.machine.tls.store_tls(key, active_thread, new_data, &*this.tcx)?;
196 // Return success (`1`).
197 this.write_scalar(Scalar::from_i32(1), dest)?;
200 // Access to command-line arguments
201 "GetCommandLineW" => {
202 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
204 this.machine.cmd_line.expect("machine must be initialized").ptr,
209 // Time related shims
210 "GetSystemTimeAsFileTime" => {
211 #[allow(non_snake_case)]
213 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
214 this.GetSystemTimeAsFileTime(LPFILETIME)?;
216 "QueryPerformanceCounter" => {
217 #[allow(non_snake_case)]
218 let [lpPerformanceCount] =
219 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
220 let result = this.QueryPerformanceCounter(lpPerformanceCount)?;
221 this.write_scalar(result, dest)?;
223 "QueryPerformanceFrequency" => {
224 #[allow(non_snake_case)]
226 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
227 let result = this.QueryPerformanceFrequency(lpFrequency)?;
228 this.write_scalar(result, dest)?;
232 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
234 this.Sleep(timeout)?;
237 // Synchronization primitives
238 "AcquireSRWLockExclusive" => {
239 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
240 this.AcquireSRWLockExclusive(ptr)?;
242 "ReleaseSRWLockExclusive" => {
243 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
244 this.ReleaseSRWLockExclusive(ptr)?;
246 "TryAcquireSRWLockExclusive" => {
247 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
248 let ret = this.TryAcquireSRWLockExclusive(ptr)?;
249 this.write_scalar(ret, dest)?;
251 "AcquireSRWLockShared" => {
252 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
253 this.AcquireSRWLockShared(ptr)?;
255 "ReleaseSRWLockShared" => {
256 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
257 this.ReleaseSRWLockShared(ptr)?;
259 "TryAcquireSRWLockShared" => {
260 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
261 let ret = this.TryAcquireSRWLockShared(ptr)?;
262 this.write_scalar(ret, dest)?;
264 "InitOnceBeginInitialize" => {
265 let [ptr, flags, pending, context] =
266 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
267 let result = this.InitOnceBeginInitialize(ptr, flags, pending, context)?;
268 this.write_scalar(result, dest)?;
270 "InitOnceComplete" => {
271 let [ptr, flags, context] =
272 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
273 let result = this.InitOnceComplete(ptr, flags, context)?;
274 this.write_scalar(result, dest)?;
276 "SleepConditionVariableSRW" => {
277 let [condvar, lock, timeout, flags] =
278 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
280 let result = this.SleepConditionVariableSRW(condvar, lock, timeout, flags, dest)?;
281 this.write_scalar(result, dest)?;
283 "WakeConditionVariable" => {
285 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
287 this.WakeConditionVariable(condvar)?;
289 "WakeAllConditionVariable" => {
291 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
293 this.WakeAllConditionVariable(condvar)?;
296 // Dynamic symbol loading
297 "GetProcAddress" => {
298 #[allow(non_snake_case)]
299 let [hModule, lpProcName] =
300 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
301 this.read_machine_isize(hModule)?;
302 let name = this.read_c_str(this.read_pointer(lpProcName)?)?;
303 if let Some(dlsym) = Dlsym::from_str(name, &this.tcx.sess.target.os)? {
304 let ptr = this.create_fn_alloc_ptr(FnVal::Other(dlsym));
305 this.write_pointer(ptr, dest)?;
307 this.write_null(dest)?;
312 "SystemFunction036" => {
313 // This is really 'RtlGenRandom'.
315 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
316 let ptr = this.read_pointer(ptr)?;
317 let len = this.read_scalar(len)?.to_u32()?;
318 this.gen_random(ptr, len.into())?;
319 this.write_scalar(Scalar::from_bool(true), dest)?;
321 "BCryptGenRandom" => {
322 let [algorithm, ptr, len, flags] =
323 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
324 let algorithm = this.read_scalar(algorithm)?;
325 let algorithm = algorithm.to_machine_usize(this)?;
326 let ptr = this.read_pointer(ptr)?;
327 let len = this.read_scalar(len)?.to_u32()?;
328 let flags = this.read_scalar(flags)?.to_u32()?;
331 if algorithm != 0x81 {
332 // BCRYPT_RNG_ALG_HANDLE
334 "BCryptGenRandom algorithm must be BCRYPT_RNG_ALG_HANDLE when the flag is 0"
339 // BCRYPT_USE_SYSTEM_PREFERRED_RNG
342 "BCryptGenRandom algorithm must be NULL when the flag is BCRYPT_USE_SYSTEM_PREFERRED_RNG"
348 "BCryptGenRandom is only supported with BCRYPT_USE_SYSTEM_PREFERRED_RNG or BCRYPT_RNG_ALG_HANDLE"
352 this.gen_random(ptr, len.into())?;
353 this.write_null(dest)?; // STATUS_SUCCESS
355 "GetConsoleScreenBufferInfo" => {
356 // `term` needs this, so we fake it.
357 let [console, buffer_info] =
358 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
359 this.read_machine_isize(console)?;
360 this.deref_operand(buffer_info)?;
361 // Indicate an error.
362 // FIXME: we should set last_error, but to what?
363 this.write_null(dest)?;
367 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
368 let which = this.read_scalar(which)?.to_i32()?;
369 // We just make this the identity function, so we know later in `NtWriteFile` which
370 // one it is. This is very fake, but libtest needs it so we cannot make it a
372 // FIXME: this should return real HANDLEs when io support is added
373 this.write_scalar(Scalar::from_machine_isize(which.into(), this), dest)?;
377 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
379 this.CloseHandle(handle)?;
381 this.write_scalar(Scalar::from_u32(1), dest)?;
386 let [security, stacksize, start, arg, flags, thread] =
387 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
390 this.CreateThread(security, stacksize, start, arg, flags, thread)?;
392 this.write_scalar(Handle::Thread(thread_id).to_scalar(this), dest)?;
394 "WaitForSingleObject" => {
395 let [handle, timeout] =
396 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
398 let ret = this.WaitForSingleObject(handle, timeout)?;
399 this.write_scalar(Scalar::from_u32(ret), dest)?;
401 "GetCurrentThread" => {
402 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
405 Handle::Pseudo(PseudoHandle::CurrentThread).to_scalar(this),
410 // Incomplete shims that we "stub out" just to get pre-main initialization code to work.
411 // These shims are enabled only when the caller is in the standard library.
412 "GetProcessHeap" if this.frame_in_std() => {
413 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
414 // Just fake a HANDLE
415 // It's fine to not use the Handle type here because its a stub
416 this.write_int(1, dest)?;
418 "GetModuleHandleA" if this.frame_in_std() => {
419 #[allow(non_snake_case)]
420 let [_lpModuleName] =
421 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
422 // We need to return something non-null here to make `compat_fn!` work.
423 this.write_int(1, dest)?;
425 "SetConsoleTextAttribute" if this.frame_in_std() => {
426 #[allow(non_snake_case)]
427 let [_hConsoleOutput, _wAttribute] =
428 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
429 // Pretend these does not exist / nothing happened, by returning zero.
430 this.write_null(dest)?;
432 "GetConsoleMode" if this.frame_in_std() => {
433 let [console, mode] =
434 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
435 this.read_machine_isize(console)?;
436 this.deref_operand(mode)?;
437 // Indicate an error.
438 this.write_null(dest)?;
440 "GetFileType" if this.frame_in_std() => {
441 #[allow(non_snake_case)]
443 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
444 // Return unknown file type.
445 this.write_null(dest)?;
447 "AddVectoredExceptionHandler" if this.frame_in_std() => {
448 #[allow(non_snake_case)]
449 let [_First, _Handler] =
450 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
451 // Any non zero value works for the stdlib. This is just used for stack overflows anyway.
452 this.write_int(1, dest)?;
454 "SetThreadStackGuarantee" if this.frame_in_std() => {
455 #[allow(non_snake_case)]
456 let [_StackSizeInBytes] =
457 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
458 // Any non zero value works for the stdlib. This is just used for stack overflows anyway.
459 this.write_int(1, dest)?;
461 "GetCurrentProcessId" if this.frame_in_std() => {
462 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
463 let result = this.GetCurrentProcessId()?;
464 this.write_int(result, dest)?;
466 // this is only callable from std because we know that std ignores the return value
467 "SwitchToThread" if this.frame_in_std() => {
468 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
470 this.yield_active_thread();
472 // FIXME: this should return a nonzero value if this call does result in switching to another thread.
473 this.write_null(dest)?;
476 _ => return Ok(EmulateByNameResult::NotSupported),
479 Ok(EmulateByNameResult::NeedsJumping)