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
116 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
117 let system_info = this.deref_operand(system_info)?;
118 // Initialize with `0`.
119 this.write_bytes_ptr(
121 iter::repeat(0u8).take(system_info.layout.size.bytes() as usize),
123 // Set selected fields.
124 let word_layout = this.machine.layouts.u16;
125 let dword_layout = this.machine.layouts.u32;
126 let usize_layout = this.machine.layouts.usize;
128 // Using `mplace_field` is error-prone, see: https://github.com/rust-lang/miri/issues/2136.
129 // Pointer fields have different sizes on different targets.
130 // To avoid all these issue we calculate the offsets ourselves.
132 word_layout.size, // 0, wProcessorArchitecture : WORD
133 word_layout.size, // 1, wReserved : WORD
134 dword_layout.size, // 2, dwPageSize : DWORD
135 usize_layout.size, // 3, lpMinimumApplicationAddress : LPVOID
136 usize_layout.size, // 4, lpMaximumApplicationAddress : LPVOID
137 usize_layout.size, // 5, dwActiveProcessorMask : DWORD_PTR
138 dword_layout.size, // 6, dwNumberOfProcessors : DWORD
139 dword_layout.size, // 7, dwProcessorType : DWORD
140 dword_layout.size, // 8, dwAllocationGranularity : DWORD
141 word_layout.size, // 9, wProcessorLevel : WORD
142 word_layout.size, // 10, wProcessorRevision : WORD
144 let field_offsets: SmallVec<[Size; 11]> = field_sizes
147 .scan(Size::ZERO, |a, x| {
155 let page_size = system_info.offset(
162 Scalar::from_int(PAGE_SIZE, dword_layout.size),
165 // Set number of processors.
166 let num_cpus = system_info.offset(
172 this.write_scalar(Scalar::from_int(NUM_CPUS, dword_layout.size), &num_cpus.into())?;
175 // Thread-local storage
177 // This just creates a key; Windows does not natively support TLS destructors.
179 // Create key and return it.
180 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
181 let key = this.machine.tls.create_tls_key(None, dest.layout.size)?;
182 this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?;
185 let [key] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
186 let key = u128::from(this.read_scalar(key)?.to_u32()?);
187 let active_thread = this.get_active_thread();
188 let ptr = this.machine.tls.load_tls(key, active_thread, this)?;
189 this.write_scalar(ptr, dest)?;
193 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
194 let key = u128::from(this.read_scalar(key)?.to_u32()?);
195 let active_thread = this.get_active_thread();
196 let new_data = this.read_scalar(new_ptr)?.check_init()?;
197 this.machine.tls.store_tls(key, active_thread, new_data, &*this.tcx)?;
199 // Return success (`1`).
200 this.write_scalar(Scalar::from_i32(1), dest)?;
203 // Access to command-line arguments
204 "GetCommandLineW" => {
205 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
207 this.machine.cmd_line.expect("machine must be initialized").ptr,
212 // Time related shims
213 "GetSystemTimeAsFileTime" => {
214 #[allow(non_snake_case)]
216 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
217 this.GetSystemTimeAsFileTime(LPFILETIME)?;
219 "QueryPerformanceCounter" => {
220 #[allow(non_snake_case)]
221 let [lpPerformanceCount] =
222 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
223 let result = this.QueryPerformanceCounter(lpPerformanceCount)?;
224 this.write_scalar(Scalar::from_i32(result), dest)?;
226 "QueryPerformanceFrequency" => {
227 #[allow(non_snake_case)]
229 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
230 let result = this.QueryPerformanceFrequency(lpFrequency)?;
231 this.write_scalar(Scalar::from_i32(result), dest)?;
234 // Synchronization primitives
235 "AcquireSRWLockExclusive" => {
236 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
237 this.AcquireSRWLockExclusive(ptr)?;
239 "ReleaseSRWLockExclusive" => {
240 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
241 this.ReleaseSRWLockExclusive(ptr)?;
243 "TryAcquireSRWLockExclusive" => {
244 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
245 let ret = this.TryAcquireSRWLockExclusive(ptr)?;
246 this.write_scalar(Scalar::from_u8(ret), dest)?;
248 "AcquireSRWLockShared" => {
249 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
250 this.AcquireSRWLockShared(ptr)?;
252 "ReleaseSRWLockShared" => {
253 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
254 this.ReleaseSRWLockShared(ptr)?;
256 "TryAcquireSRWLockShared" => {
257 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
258 let ret = this.TryAcquireSRWLockShared(ptr)?;
259 this.write_scalar(Scalar::from_u8(ret), dest)?;
262 // Dynamic symbol loading
263 "GetProcAddress" => {
264 #[allow(non_snake_case)]
265 let [hModule, lpProcName] =
266 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
267 this.read_scalar(hModule)?.to_machine_isize(this)?;
268 let name = this.read_c_str(this.read_pointer(lpProcName)?)?;
269 if let Some(dlsym) = Dlsym::from_str(name, &this.tcx.sess.target.os)? {
270 let ptr = this.create_fn_alloc_ptr(FnVal::Other(dlsym));
271 this.write_pointer(ptr, dest)?;
273 this.write_null(dest)?;
278 "SystemFunction036" => {
279 // This is really 'RtlGenRandom'.
281 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
282 let ptr = this.read_pointer(ptr)?;
283 let len = this.read_scalar(len)?.to_u32()?;
284 this.gen_random(ptr, len.into())?;
285 this.write_scalar(Scalar::from_bool(true), dest)?;
287 "BCryptGenRandom" => {
288 let [algorithm, ptr, len, flags] =
289 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
290 let algorithm = this.read_scalar(algorithm)?;
291 let ptr = this.read_pointer(ptr)?;
292 let len = this.read_scalar(len)?.to_u32()?;
293 let flags = this.read_scalar(flags)?.to_u32()?;
295 // ^ BCRYPT_USE_SYSTEM_PREFERRED_RNG
297 "BCryptGenRandom is supported only with the BCRYPT_USE_SYSTEM_PREFERRED_RNG flag"
300 if algorithm.to_machine_usize(this)? != 0 {
302 "BCryptGenRandom algorithm must be NULL when the flag is BCRYPT_USE_SYSTEM_PREFERRED_RNG"
305 this.gen_random(ptr, len.into())?;
306 this.write_null(dest)?; // STATUS_SUCCESS
308 "GetConsoleScreenBufferInfo" => {
309 // `term` needs this, so we fake it.
310 let [console, buffer_info] =
311 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
312 this.read_scalar(console)?.to_machine_isize(this)?;
313 this.deref_operand(buffer_info)?;
314 // Indicate an error.
315 // FIXME: we should set last_error, but to what?
316 this.write_null(dest)?;
318 "GetConsoleMode" => {
319 // Windows "isatty" (in libtest) needs this, so we fake it.
320 let [console, mode] =
321 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
322 this.read_scalar(console)?.to_machine_isize(this)?;
323 this.deref_operand(mode)?;
324 // Indicate an error.
325 // FIXME: we should set last_error, but to what?
326 this.write_null(dest)?;
328 "SwitchToThread" => {
329 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
330 // Note that once Miri supports concurrency, this will need to return a nonzero
331 // value if this call does result in switching to another thread.
332 this.write_null(dest)?;
336 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
337 let which = this.read_scalar(which)?.to_i32()?;
338 // We just make this the identity function, so we know later in `NtWriteFile` which
339 // one it is. This is very fake, but libtest needs it so we cannot make it a
341 this.write_scalar(Scalar::from_machine_isize(which.into(), this), dest)?;
344 // Better error for attempts to create a thread
346 let [_, _, _, _, _, _] =
347 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
349 this.handle_unsupported("can't create threads on Windows")?;
350 return Ok(EmulateByNameResult::AlreadyJumped);
353 // Incomplete shims that we "stub out" just to get pre-main initialization code to work.
354 // These shims are enabled only when the caller is in the standard library.
355 "GetProcessHeap" if this.frame_in_std() => {
356 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
357 // Just fake a HANDLE
358 this.write_scalar(Scalar::from_machine_isize(1, this), dest)?;
360 "GetModuleHandleA" if this.frame_in_std() => {
361 #[allow(non_snake_case)]
362 let [_lpModuleName] =
363 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
364 // We need to return something non-null here to make `compat_fn!` work.
365 this.write_scalar(Scalar::from_machine_isize(1, this), dest)?;
367 "SetConsoleTextAttribute" if this.frame_in_std() => {
368 #[allow(non_snake_case)]
369 let [_hConsoleOutput, _wAttribute] =
370 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
371 // Pretend these does not exist / nothing happened, by returning zero.
372 this.write_null(dest)?;
374 "AddVectoredExceptionHandler" if this.frame_in_std() => {
375 #[allow(non_snake_case)]
376 let [_First, _Handler] =
377 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
378 // Any non zero value works for the stdlib. This is just used for stack overflows anyway.
379 this.write_scalar(Scalar::from_machine_usize(1, this), dest)?;
381 "SetThreadStackGuarantee" if this.frame_in_std() => {
382 #[allow(non_snake_case)]
383 let [_StackSizeInBytes] =
384 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
385 // Any non zero value works for the stdlib. This is just used for stack overflows anyway.
386 this.write_scalar(Scalar::from_u32(1), dest)?;
388 | "InitializeCriticalSection"
389 | "EnterCriticalSection"
390 | "LeaveCriticalSection"
391 | "DeleteCriticalSection"
392 if this.frame_in_std() =>
394 #[allow(non_snake_case)]
395 let [_lpCriticalSection] =
396 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
398 this.get_total_thread_count(),
400 "concurrency on Windows is not supported"
402 // Nothing to do, not even a return value.
403 // (Windows locks are reentrant, and we have only 1 thread,
404 // so not doing any futher checks here is at least not incorrect.)
406 "TryEnterCriticalSection" if this.frame_in_std() => {
407 #[allow(non_snake_case)]
408 let [_lpCriticalSection] =
409 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
411 this.get_total_thread_count(),
413 "concurrency on Windows is not supported"
415 // There is only one thread, so this always succeeds and returns TRUE.
416 this.write_scalar(Scalar::from_i32(1), dest)?;
418 "GetCurrentThread" if this.frame_in_std() => {
419 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
420 this.write_scalar(Scalar::from_machine_isize(1, this), dest)?;
423 _ => return Ok(EmulateByNameResult::NotSupported),
426 Ok(EmulateByNameResult::NeedsJumping)