4 use rustc_span::Symbol;
5 use rustc_target::abi::Size;
6 use rustc_target::spec::abi::Abi;
9 use shims::foreign_items::EmulateByNameResult;
10 use shims::windows::sync::EvalContextExt as _;
11 use smallvec::SmallVec;
13 impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
14 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
15 fn emulate_foreign_item_by_name(
19 args: &[OpTy<'tcx, Tag>],
20 dest: &PlaceTy<'tcx, Tag>,
21 _ret: mir::BasicBlock,
22 ) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> {
23 let this = self.eval_context_mut();
27 // NTSTATUS = LONH = i32
28 // DWORD = ULONG = u32
31 match &*link_name.as_str() {
32 // Environment related shims
33 "GetEnvironmentVariableW" => {
34 let [name, buf, size] =
35 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
36 let result = this.GetEnvironmentVariableW(name, buf, size)?;
37 this.write_scalar(Scalar::from_u32(result), dest)?;
39 "SetEnvironmentVariableW" => {
41 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
42 let result = this.SetEnvironmentVariableW(name, value)?;
43 this.write_scalar(Scalar::from_i32(result), dest)?;
45 "GetEnvironmentStringsW" => {
46 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
47 let result = this.GetEnvironmentStringsW()?;
48 this.write_pointer(result, dest)?;
50 "FreeEnvironmentStringsW" => {
52 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
53 let result = this.FreeEnvironmentStringsW(env_block)?;
54 this.write_scalar(Scalar::from_i32(result), dest)?;
56 "GetCurrentDirectoryW" => {
58 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
59 let result = this.GetCurrentDirectoryW(size, buf)?;
60 this.write_scalar(Scalar::from_u32(result), dest)?;
62 "SetCurrentDirectoryW" => {
64 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
65 let result = this.SetCurrentDirectoryW(path)?;
66 this.write_scalar(Scalar::from_i32(result), dest)?;
71 let [handle, flags, size] =
72 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
73 this.read_scalar(handle)?.to_machine_isize(this)?;
74 let flags = this.read_scalar(flags)?.to_u32()?;
75 let size = this.read_scalar(size)?.to_machine_usize(this)?;
76 let zero_init = (flags & 0x00000008) != 0; // HEAP_ZERO_MEMORY
77 let res = this.malloc(size, zero_init, MiriMemoryKind::WinHeap)?;
78 this.write_pointer(res, dest)?;
81 let [handle, flags, ptr] =
82 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
83 this.read_scalar(handle)?.to_machine_isize(this)?;
84 this.read_scalar(flags)?.to_u32()?;
85 let ptr = this.read_pointer(ptr)?;
86 this.free(ptr, MiriMemoryKind::WinHeap)?;
87 this.write_scalar(Scalar::from_i32(1), dest)?;
90 let [handle, flags, ptr, size] =
91 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
92 this.read_scalar(handle)?.to_machine_isize(this)?;
93 this.read_scalar(flags)?.to_u32()?;
94 let ptr = this.read_pointer(ptr)?;
95 let size = this.read_scalar(size)?.to_machine_usize(this)?;
96 let res = this.realloc(ptr, size, MiriMemoryKind::WinHeap)?;
97 this.write_pointer(res, dest)?;
103 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
104 let error = this.read_scalar(error)?.check_init()?;
105 this.set_last_error(error)?;
108 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
109 let last_error = this.get_last_error()?;
110 this.write_scalar(last_error, dest)?;
113 // Querying system information
115 use crate::rustc_middle::ty::{layout::LayoutOf, TyKind, UintTy};
118 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
119 let system_info = this.deref_operand(system_info)?;
120 // Initialize with `0`.
121 this.write_bytes_ptr(
123 iter::repeat(0u8).take(system_info.layout.size.bytes() as usize),
125 // Set selected fields.
126 let word_ty = this.tcx.mk_ty(TyKind::Uint(UintTy::U16));
127 let dword_ty = this.tcx.mk_ty(TyKind::Uint(UintTy::U32));
128 let usize_ty = this.tcx.mk_ty(TyKind::Uint(UintTy::Usize));
129 let word_layout = this.layout_of(word_ty)?;
130 let dword_layout = this.layout_of(dword_ty)?;
131 let usize_layout = this.layout_of(usize_ty)?;
133 // Using `mplace_field` is error-prone, see: https://github.com/rust-lang/miri/issues/2136.
134 // Pointer fields have different sizes on different targets.
135 // To avoid all these issue we calculate the offsets dynamically.
137 word_layout.size, // 0, wProcessorArchitecture : WORD
138 word_layout.size, // 1, wReserved : WORD
139 dword_layout.size, // 2, dwPageSize : DWORD
140 usize_layout.size, // 3, lpMinimumApplicationAddress : LPVOID
141 usize_layout.size, // 4, lpMaximumApplicationAddress : LPVOID
142 usize_layout.size, // 5, dwActiveProcessorMask : DWORD_PTR
143 dword_layout.size, // 6, dwNumberOfProcessors : DWORD
144 dword_layout.size, // 7, dwProcessorType : DWORD
145 dword_layout.size, // 8, dwAllocationGranularity : DWORD
146 word_layout.size, // 9, wProcessorLevel : WORD
147 word_layout.size, // 10, wProcessorRevision : WORD
149 let field_offsets: SmallVec<[Size; 11]> = field_sizes
152 .scan(Size::ZERO, |a, x| {
160 let page_size = system_info.offset(
167 Scalar::from_int(PAGE_SIZE, dword_layout.size),
170 // Set number of processors.
171 let num_cpus = system_info.offset(
177 this.write_scalar(Scalar::from_int(NUM_CPUS, dword_layout.size), &num_cpus.into())?;
180 // Thread-local storage
182 // This just creates a key; Windows does not natively support TLS destructors.
184 // Create key and return it.
185 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
186 let key = this.machine.tls.create_tls_key(None, dest.layout.size)?;
187 this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?;
190 let [key] = 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 ptr = this.machine.tls.load_tls(key, active_thread, this)?;
194 this.write_scalar(ptr, dest)?;
198 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
199 let key = u128::from(this.read_scalar(key)?.to_u32()?);
200 let active_thread = this.get_active_thread();
201 let new_data = this.read_scalar(new_ptr)?.check_init()?;
202 this.machine.tls.store_tls(key, active_thread, new_data, &*this.tcx)?;
204 // Return success (`1`).
205 this.write_scalar(Scalar::from_i32(1), dest)?;
208 // Access to command-line arguments
209 "GetCommandLineW" => {
210 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
212 this.machine.cmd_line.expect("machine must be initialized").ptr,
217 // Time related shims
218 "GetSystemTimeAsFileTime" => {
219 #[allow(non_snake_case)]
221 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
222 this.GetSystemTimeAsFileTime(LPFILETIME)?;
224 "QueryPerformanceCounter" => {
225 #[allow(non_snake_case)]
226 let [lpPerformanceCount] =
227 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
228 let result = this.QueryPerformanceCounter(lpPerformanceCount)?;
229 this.write_scalar(Scalar::from_i32(result), dest)?;
231 "QueryPerformanceFrequency" => {
232 #[allow(non_snake_case)]
234 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
235 let result = this.QueryPerformanceFrequency(lpFrequency)?;
236 this.write_scalar(Scalar::from_i32(result), dest)?;
239 // Synchronization primitives
240 "AcquireSRWLockExclusive" => {
241 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
242 this.AcquireSRWLockExclusive(ptr)?;
244 "ReleaseSRWLockExclusive" => {
245 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
246 this.ReleaseSRWLockExclusive(ptr)?;
248 "TryAcquireSRWLockExclusive" => {
249 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
250 let ret = this.TryAcquireSRWLockExclusive(ptr)?;
251 this.write_scalar(Scalar::from_u8(ret), dest)?;
253 "AcquireSRWLockShared" => {
254 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
255 this.AcquireSRWLockShared(ptr)?;
257 "ReleaseSRWLockShared" => {
258 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
259 this.ReleaseSRWLockShared(ptr)?;
261 "TryAcquireSRWLockShared" => {
262 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
263 let ret = this.TryAcquireSRWLockShared(ptr)?;
264 this.write_scalar(Scalar::from_u8(ret), dest)?;
267 // Dynamic symbol loading
268 "GetProcAddress" => {
269 #[allow(non_snake_case)]
270 let [hModule, lpProcName] =
271 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
272 this.read_scalar(hModule)?.to_machine_isize(this)?;
273 let name = this.read_c_str(this.read_pointer(lpProcName)?)?;
274 if let Some(dlsym) = Dlsym::from_str(name, &this.tcx.sess.target.os)? {
275 let ptr = this.create_fn_alloc_ptr(FnVal::Other(dlsym));
276 this.write_pointer(ptr, dest)?;
278 this.write_null(dest)?;
283 "SystemFunction036" => {
284 // This is really 'RtlGenRandom'.
286 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
287 let ptr = this.read_pointer(ptr)?;
288 let len = this.read_scalar(len)?.to_u32()?;
289 this.gen_random(ptr, len.into())?;
290 this.write_scalar(Scalar::from_bool(true), dest)?;
292 "BCryptGenRandom" => {
293 let [algorithm, ptr, len, flags] =
294 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
295 let algorithm = this.read_scalar(algorithm)?;
296 let ptr = this.read_pointer(ptr)?;
297 let len = this.read_scalar(len)?.to_u32()?;
298 let flags = this.read_scalar(flags)?.to_u32()?;
300 // ^ BCRYPT_USE_SYSTEM_PREFERRED_RNG
302 "BCryptGenRandom is supported only with the BCRYPT_USE_SYSTEM_PREFERRED_RNG flag"
305 if algorithm.to_machine_usize(this)? != 0 {
307 "BCryptGenRandom algorithm must be NULL when the flag is BCRYPT_USE_SYSTEM_PREFERRED_RNG"
310 this.gen_random(ptr, len.into())?;
311 this.write_null(dest)?; // STATUS_SUCCESS
313 "GetConsoleScreenBufferInfo" => {
314 // `term` needs this, so we fake it.
315 let [console, buffer_info] =
316 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
317 this.read_scalar(console)?.to_machine_isize(this)?;
318 this.deref_operand(buffer_info)?;
319 // Indicate an error.
320 // FIXME: we should set last_error, but to what?
321 this.write_null(dest)?;
323 "GetConsoleMode" => {
324 // Windows "isatty" (in libtest) needs this, so we fake it.
325 let [console, mode] =
326 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
327 this.read_scalar(console)?.to_machine_isize(this)?;
328 this.deref_operand(mode)?;
329 // Indicate an error.
330 // FIXME: we should set last_error, but to what?
331 this.write_null(dest)?;
333 "SwitchToThread" => {
334 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
335 // Note that once Miri supports concurrency, this will need to return a nonzero
336 // value if this call does result in switching to another thread.
337 this.write_null(dest)?;
341 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
342 let which = this.read_scalar(which)?.to_i32()?;
343 // We just make this the identity function, so we know later in `NtWriteFile` which
344 // one it is. This is very fake, but libtest needs it so we cannot make it a
346 this.write_scalar(Scalar::from_machine_isize(which.into(), this), dest)?;
349 // Better error for attempts to create a thread
351 let [_, _, _, _, _, _] =
352 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
354 this.handle_unsupported("can't create threads on Windows")?;
355 return Ok(EmulateByNameResult::AlreadyJumped);
358 // Incomplete shims that we "stub out" just to get pre-main initialization code to work.
359 // These shims are enabled only when the caller is in the standard library.
360 "GetProcessHeap" if this.frame_in_std() => {
361 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
362 // Just fake a HANDLE
363 this.write_scalar(Scalar::from_machine_isize(1, this), dest)?;
365 "GetModuleHandleA" if this.frame_in_std() => {
366 #[allow(non_snake_case)]
367 let [_lpModuleName] =
368 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
369 // We need to return something non-null here to make `compat_fn!` work.
370 this.write_scalar(Scalar::from_machine_isize(1, this), dest)?;
372 "SetConsoleTextAttribute" if this.frame_in_std() => {
373 #[allow(non_snake_case)]
374 let [_hConsoleOutput, _wAttribute] =
375 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
376 // Pretend these does not exist / nothing happened, by returning zero.
377 this.write_null(dest)?;
379 "AddVectoredExceptionHandler" if this.frame_in_std() => {
380 #[allow(non_snake_case)]
381 let [_First, _Handler] =
382 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
383 // Any non zero value works for the stdlib. This is just used for stack overflows anyway.
384 this.write_scalar(Scalar::from_machine_usize(1, this), dest)?;
386 "SetThreadStackGuarantee" if this.frame_in_std() => {
387 #[allow(non_snake_case)]
388 let [_StackSizeInBytes] =
389 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
390 // Any non zero value works for the stdlib. This is just used for stack overflows anyway.
391 this.write_scalar(Scalar::from_u32(1), dest)?;
393 | "InitializeCriticalSection"
394 | "EnterCriticalSection"
395 | "LeaveCriticalSection"
396 | "DeleteCriticalSection"
397 if this.frame_in_std() =>
399 #[allow(non_snake_case)]
400 let [_lpCriticalSection] =
401 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
403 this.get_total_thread_count(),
405 "concurrency on Windows is not supported"
407 // Nothing to do, not even a return value.
408 // (Windows locks are reentrant, and we have only 1 thread,
409 // so not doing any futher checks here is at least not incorrect.)
411 "TryEnterCriticalSection" if this.frame_in_std() => {
412 #[allow(non_snake_case)]
413 let [_lpCriticalSection] =
414 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
416 this.get_total_thread_count(),
418 "concurrency on Windows is not supported"
420 // There is only one thread, so this always succeeds and returns TRUE.
421 this.write_scalar(Scalar::from_i32(1), dest)?;
424 _ => return Ok(EmulateByNameResult::NotSupported),
427 Ok(EmulateByNameResult::NeedsJumping)