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::sync::EvalContextExt as _;
10 use smallvec::SmallVec;
12 impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
13 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
14 fn emulate_foreign_item_by_name(
18 args: &[OpTy<'tcx, Provenance>],
19 dest: &PlaceTy<'tcx, Provenance>,
20 ) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> {
21 let this = self.eval_context_mut();
25 // NTSTATUS = LONH = i32
26 // DWORD = ULONG = u32
29 match link_name.as_str() {
30 // Environment related shims
31 "GetEnvironmentVariableW" => {
32 let [name, buf, size] =
33 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
34 let result = this.GetEnvironmentVariableW(name, buf, size)?;
35 this.write_scalar(Scalar::from_u32(result), dest)?;
37 "SetEnvironmentVariableW" => {
39 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
40 let result = this.SetEnvironmentVariableW(name, value)?;
41 this.write_scalar(Scalar::from_i32(result), dest)?;
43 "GetEnvironmentStringsW" => {
44 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
45 let result = this.GetEnvironmentStringsW()?;
46 this.write_pointer(result, dest)?;
48 "FreeEnvironmentStringsW" => {
50 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
51 let result = this.FreeEnvironmentStringsW(env_block)?;
52 this.write_scalar(Scalar::from_i32(result), dest)?;
54 "GetCurrentDirectoryW" => {
56 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
57 let result = this.GetCurrentDirectoryW(size, buf)?;
58 this.write_scalar(Scalar::from_u32(result), dest)?;
60 "SetCurrentDirectoryW" => {
62 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
63 let result = this.SetCurrentDirectoryW(path)?;
64 this.write_scalar(Scalar::from_i32(result), dest)?;
69 let [handle, flags, size] =
70 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
71 this.read_scalar(handle)?.to_machine_isize(this)?;
72 let flags = this.read_scalar(flags)?.to_u32()?;
73 let size = this.read_scalar(size)?.to_machine_usize(this)?;
74 let zero_init = (flags & 0x00000008) != 0; // HEAP_ZERO_MEMORY
75 let res = this.malloc(size, zero_init, MiriMemoryKind::WinHeap)?;
76 this.write_pointer(res, dest)?;
79 let [handle, flags, ptr] =
80 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
81 this.read_scalar(handle)?.to_machine_isize(this)?;
82 this.read_scalar(flags)?.to_u32()?;
83 let ptr = this.read_pointer(ptr)?;
84 this.free(ptr, MiriMemoryKind::WinHeap)?;
85 this.write_scalar(Scalar::from_i32(1), dest)?;
88 let [handle, flags, ptr, size] =
89 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
90 this.read_scalar(handle)?.to_machine_isize(this)?;
91 this.read_scalar(flags)?.to_u32()?;
92 let ptr = this.read_pointer(ptr)?;
93 let size = this.read_scalar(size)?.to_machine_usize(this)?;
94 let res = this.realloc(ptr, size, MiriMemoryKind::WinHeap)?;
95 this.write_pointer(res, dest)?;
101 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
102 let error = this.read_scalar(error)?.check_init()?;
103 this.set_last_error(error)?;
106 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
107 let last_error = this.get_last_error()?;
108 this.write_scalar(last_error, dest)?;
111 // Querying system information
114 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
115 let system_info = this.deref_operand(system_info)?;
116 // Initialize with `0`.
117 this.write_bytes_ptr(
119 iter::repeat(0u8).take(system_info.layout.size.bytes_usize()),
121 // Set selected fields.
122 let word_layout = this.machine.layouts.u16;
123 let dword_layout = this.machine.layouts.u32;
124 let usize_layout = this.machine.layouts.usize;
126 // Using `mplace_field` is error-prone, see: https://github.com/rust-lang/miri/issues/2136.
127 // Pointer fields have different sizes on different targets.
128 // To avoid all these issue we calculate the offsets ourselves.
130 word_layout.size, // 0, wProcessorArchitecture : WORD
131 word_layout.size, // 1, wReserved : WORD
132 dword_layout.size, // 2, dwPageSize : DWORD
133 usize_layout.size, // 3, lpMinimumApplicationAddress : LPVOID
134 usize_layout.size, // 4, lpMaximumApplicationAddress : LPVOID
135 usize_layout.size, // 5, dwActiveProcessorMask : DWORD_PTR
136 dword_layout.size, // 6, dwNumberOfProcessors : DWORD
137 dword_layout.size, // 7, dwProcessorType : DWORD
138 dword_layout.size, // 8, dwAllocationGranularity : DWORD
139 word_layout.size, // 9, wProcessorLevel : WORD
140 word_layout.size, // 10, wProcessorRevision : WORD
142 let field_offsets: SmallVec<[Size; 11]> = field_sizes
145 .scan(Size::ZERO, |a, x| {
153 let page_size = system_info.offset(field_offsets[2], dword_layout, &this.tcx)?;
155 Scalar::from_int(PAGE_SIZE, dword_layout.size),
158 // Set number of processors.
159 let num_cpus = system_info.offset(field_offsets[6], dword_layout, &this.tcx)?;
160 this.write_scalar(Scalar::from_int(NUM_CPUS, dword_layout.size), &num_cpus.into())?;
163 // Thread-local storage
165 // This just creates a key; Windows does not natively support TLS destructors.
167 // Create key and return it.
168 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
169 let key = this.machine.tls.create_tls_key(None, dest.layout.size)?;
170 this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?;
173 let [key] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
174 let key = u128::from(this.read_scalar(key)?.to_u32()?);
175 let active_thread = this.get_active_thread();
176 let ptr = this.machine.tls.load_tls(key, active_thread, this)?;
177 this.write_scalar(ptr, dest)?;
181 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
182 let key = u128::from(this.read_scalar(key)?.to_u32()?);
183 let active_thread = this.get_active_thread();
184 let new_data = this.read_scalar(new_ptr)?.check_init()?;
185 this.machine.tls.store_tls(key, active_thread, new_data, &*this.tcx)?;
187 // Return success (`1`).
188 this.write_scalar(Scalar::from_i32(1), dest)?;
191 // Access to command-line arguments
192 "GetCommandLineW" => {
193 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
195 this.machine.cmd_line.expect("machine must be initialized").ptr,
200 // Time related shims
201 "GetSystemTimeAsFileTime" => {
202 #[allow(non_snake_case)]
204 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
205 this.GetSystemTimeAsFileTime(LPFILETIME)?;
207 "QueryPerformanceCounter" => {
208 #[allow(non_snake_case)]
209 let [lpPerformanceCount] =
210 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
211 let result = this.QueryPerformanceCounter(lpPerformanceCount)?;
212 this.write_scalar(Scalar::from_i32(result), dest)?;
214 "QueryPerformanceFrequency" => {
215 #[allow(non_snake_case)]
217 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
218 let result = this.QueryPerformanceFrequency(lpFrequency)?;
219 this.write_scalar(Scalar::from_i32(result), dest)?;
222 // Synchronization primitives
223 "AcquireSRWLockExclusive" => {
224 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
225 this.AcquireSRWLockExclusive(ptr)?;
227 "ReleaseSRWLockExclusive" => {
228 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
229 this.ReleaseSRWLockExclusive(ptr)?;
231 "TryAcquireSRWLockExclusive" => {
232 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
233 let ret = this.TryAcquireSRWLockExclusive(ptr)?;
234 this.write_scalar(Scalar::from_u8(ret), dest)?;
236 "AcquireSRWLockShared" => {
237 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
238 this.AcquireSRWLockShared(ptr)?;
240 "ReleaseSRWLockShared" => {
241 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
242 this.ReleaseSRWLockShared(ptr)?;
244 "TryAcquireSRWLockShared" => {
245 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
246 let ret = this.TryAcquireSRWLockShared(ptr)?;
247 this.write_scalar(Scalar::from_u8(ret), dest)?;
250 // Dynamic symbol loading
251 "GetProcAddress" => {
252 #[allow(non_snake_case)]
253 let [hModule, lpProcName] =
254 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
255 this.read_scalar(hModule)?.to_machine_isize(this)?;
256 let name = this.read_c_str(this.read_pointer(lpProcName)?)?;
257 if let Some(dlsym) = Dlsym::from_str(name, &this.tcx.sess.target.os)? {
258 let ptr = this.create_fn_alloc_ptr(FnVal::Other(dlsym));
259 this.write_pointer(ptr, dest)?;
261 this.write_null(dest)?;
266 "SystemFunction036" => {
267 // This is really 'RtlGenRandom'.
269 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
270 let ptr = this.read_pointer(ptr)?;
271 let len = this.read_scalar(len)?.to_u32()?;
272 this.gen_random(ptr, len.into())?;
273 this.write_scalar(Scalar::from_bool(true), dest)?;
275 "BCryptGenRandom" => {
276 let [algorithm, ptr, len, flags] =
277 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
278 let algorithm = this.read_scalar(algorithm)?;
279 let ptr = this.read_pointer(ptr)?;
280 let len = this.read_scalar(len)?.to_u32()?;
281 let flags = this.read_scalar(flags)?.to_u32()?;
283 // ^ BCRYPT_USE_SYSTEM_PREFERRED_RNG
285 "BCryptGenRandom is supported only with the BCRYPT_USE_SYSTEM_PREFERRED_RNG flag"
288 if algorithm.to_machine_usize(this)? != 0 {
290 "BCryptGenRandom algorithm must be NULL when the flag is BCRYPT_USE_SYSTEM_PREFERRED_RNG"
293 this.gen_random(ptr, len.into())?;
294 this.write_null(dest)?; // STATUS_SUCCESS
296 "GetConsoleScreenBufferInfo" => {
297 // `term` needs this, so we fake it.
298 let [console, buffer_info] =
299 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
300 this.read_scalar(console)?.to_machine_isize(this)?;
301 this.deref_operand(buffer_info)?;
302 // Indicate an error.
303 // FIXME: we should set last_error, but to what?
304 this.write_null(dest)?;
306 "GetConsoleMode" => {
307 // Windows "isatty" (in libtest) needs this, so we fake it.
308 let [console, mode] =
309 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
310 this.read_scalar(console)?.to_machine_isize(this)?;
311 this.deref_operand(mode)?;
312 // Indicate an error.
313 // FIXME: we should set last_error, but to what?
314 this.write_null(dest)?;
316 "SwitchToThread" => {
317 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
318 // Note that once Miri supports concurrency, this will need to return a nonzero
319 // value if this call does result in switching to another thread.
320 this.write_null(dest)?;
324 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
325 let which = this.read_scalar(which)?.to_i32()?;
326 // We just make this the identity function, so we know later in `NtWriteFile` which
327 // one it is. This is very fake, but libtest needs it so we cannot make it a
329 this.write_scalar(Scalar::from_machine_isize(which.into(), this), dest)?;
332 // Better error for attempts to create a thread
334 let [_, _, _, _, _, _] =
335 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
337 this.handle_unsupported("can't create threads on Windows")?;
338 return Ok(EmulateByNameResult::AlreadyJumped);
341 // Incomplete shims that we "stub out" just to get pre-main initialization code to work.
342 // These shims are enabled only when the caller is in the standard library.
343 "GetProcessHeap" if this.frame_in_std() => {
344 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
345 // Just fake a HANDLE
346 this.write_scalar(Scalar::from_machine_isize(1, this), dest)?;
348 "GetModuleHandleA" if this.frame_in_std() => {
349 #[allow(non_snake_case)]
350 let [_lpModuleName] =
351 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
352 // We need to return something non-null here to make `compat_fn!` work.
353 this.write_scalar(Scalar::from_machine_isize(1, this), dest)?;
355 "SetConsoleTextAttribute" if this.frame_in_std() => {
356 #[allow(non_snake_case)]
357 let [_hConsoleOutput, _wAttribute] =
358 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
359 // Pretend these does not exist / nothing happened, by returning zero.
360 this.write_null(dest)?;
362 "AddVectoredExceptionHandler" if this.frame_in_std() => {
363 #[allow(non_snake_case)]
364 let [_First, _Handler] =
365 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
366 // Any non zero value works for the stdlib. This is just used for stack overflows anyway.
367 this.write_scalar(Scalar::from_machine_usize(1, this), dest)?;
369 "SetThreadStackGuarantee" if this.frame_in_std() => {
370 #[allow(non_snake_case)]
371 let [_StackSizeInBytes] =
372 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
373 // Any non zero value works for the stdlib. This is just used for stack overflows anyway.
374 this.write_scalar(Scalar::from_u32(1), dest)?;
376 | "InitializeCriticalSection"
377 | "EnterCriticalSection"
378 | "LeaveCriticalSection"
379 | "DeleteCriticalSection"
380 if this.frame_in_std() =>
382 #[allow(non_snake_case)]
383 let [_lpCriticalSection] =
384 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
386 this.get_total_thread_count(),
388 "concurrency on Windows is not supported"
390 // Nothing to do, not even a return value.
391 // (Windows locks are reentrant, and we have only 1 thread,
392 // so not doing any futher checks here is at least not incorrect.)
394 "TryEnterCriticalSection" if this.frame_in_std() => {
395 #[allow(non_snake_case)]
396 let [_lpCriticalSection] =
397 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
399 this.get_total_thread_count(),
401 "concurrency on Windows is not supported"
403 // There is only one thread, so this always succeeds and returns TRUE.
404 this.write_scalar(Scalar::from_i32(1), dest)?;
406 "GetCurrentThread" if this.frame_in_std() => {
407 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
408 this.write_scalar(Scalar::from_machine_isize(1, this), dest)?;
410 "GetCurrentProcessId" if this.frame_in_std() => {
411 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
412 let result = this.GetCurrentProcessId()?;
413 this.write_scalar(Scalar::from_u32(result), dest)?;
416 _ => return Ok(EmulateByNameResult::NotSupported),
419 Ok(EmulateByNameResult::NeedsJumping)