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 _;
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, Tag>],
19 dest: &PlaceTy<'tcx, Tag>,
20 _ret: mir::BasicBlock,
21 ) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> {
22 let this = self.eval_context_mut();
26 // NTSTATUS = LONH = i32
27 // DWORD = ULONG = u32
30 match &*link_name.as_str() {
31 // Environment related shims
32 "GetEnvironmentVariableW" => {
33 let &[ref name, ref buf, ref size] =
34 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
35 let result = this.GetEnvironmentVariableW(name, buf, size)?;
36 this.write_scalar(Scalar::from_u32(result), dest)?;
38 "SetEnvironmentVariableW" => {
39 let &[ref name, ref value] =
40 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
41 let result = this.SetEnvironmentVariableW(name, value)?;
42 this.write_scalar(Scalar::from_i32(result), dest)?;
44 "GetEnvironmentStringsW" => {
45 let &[] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
46 let result = this.GetEnvironmentStringsW()?;
47 this.write_pointer(result, dest)?;
49 "FreeEnvironmentStringsW" => {
50 let &[ref env_block] =
51 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
52 let result = this.FreeEnvironmentStringsW(env_block)?;
53 this.write_scalar(Scalar::from_i32(result), dest)?;
55 "GetCurrentDirectoryW" => {
56 let &[ref size, ref buf] =
57 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
58 let result = this.GetCurrentDirectoryW(size, buf)?;
59 this.write_scalar(Scalar::from_u32(result), dest)?;
61 "SetCurrentDirectoryW" => {
63 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
64 let result = this.SetCurrentDirectoryW(path)?;
65 this.write_scalar(Scalar::from_i32(result), dest)?;
70 let &[ref handle, ref flags, ref size] =
71 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
72 this.read_scalar(handle)?.to_machine_isize(this)?;
73 let flags = this.read_scalar(flags)?.to_u32()?;
74 let size = this.read_scalar(size)?.to_machine_usize(this)?;
75 let zero_init = (flags & 0x00000008) != 0; // HEAP_ZERO_MEMORY
76 let res = this.malloc(size, zero_init, MiriMemoryKind::WinHeap)?;
77 this.write_pointer(res, dest)?;
80 let &[ref handle, ref flags, ref ptr] =
81 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
82 this.read_scalar(handle)?.to_machine_isize(this)?;
83 this.read_scalar(flags)?.to_u32()?;
84 let ptr = this.read_pointer(ptr)?;
85 this.free(ptr, MiriMemoryKind::WinHeap)?;
86 this.write_scalar(Scalar::from_i32(1), dest)?;
89 let &[ref handle, ref flags, ref ptr, ref size] =
90 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
91 this.read_scalar(handle)?.to_machine_isize(this)?;
92 this.read_scalar(flags)?.to_u32()?;
93 let ptr = this.read_pointer(ptr)?;
94 let size = this.read_scalar(size)?.to_machine_usize(this)?;
95 let res = this.realloc(ptr, size, MiriMemoryKind::WinHeap)?;
96 this.write_pointer(res, dest)?;
102 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
103 let error = this.read_scalar(error)?.check_init()?;
104 this.set_last_error(error)?;
107 let &[] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
108 let last_error = this.get_last_error()?;
109 this.write_scalar(last_error, dest)?;
112 // Querying system information
114 let &[ref system_info] =
115 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
116 let system_info = this.deref_operand(system_info)?;
117 // Initialize with `0`.
118 this.write_bytes_ptr(
120 iter::repeat(0u8).take(system_info.layout.size.bytes() as usize),
122 // Set number of processors.
123 let dword_size = Size::from_bytes(4);
124 let num_cpus = this.mplace_field(&system_info, 6)?;
125 this.write_scalar(Scalar::from_int(NUM_CPUS, dword_size), &num_cpus.into())?;
128 // Thread-local storage
130 // This just creates a key; Windows does not natively support TLS destructors.
132 // Create key and return it.
133 let &[] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
134 let key = this.machine.tls.create_tls_key(None, dest.layout.size)?;
135 this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?;
139 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
140 let key = u128::from(this.read_scalar(key)?.to_u32()?);
141 let active_thread = this.get_active_thread();
142 let ptr = this.machine.tls.load_tls(key, active_thread, this)?;
143 this.write_scalar(ptr, dest)?;
146 let &[ref key, ref new_ptr] =
147 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
148 let key = u128::from(this.read_scalar(key)?.to_u32()?);
149 let active_thread = this.get_active_thread();
150 let new_data = this.read_scalar(new_ptr)?.check_init()?;
151 this.machine.tls.store_tls(key, active_thread, new_data, &*this.tcx)?;
153 // Return success (`1`).
154 this.write_scalar(Scalar::from_i32(1), dest)?;
157 // Access to command-line arguments
158 "GetCommandLineW" => {
159 let &[] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
161 this.machine.cmd_line.expect("machine must be initialized").ptr,
166 // Time related shims
167 "GetSystemTimeAsFileTime" => {
168 #[allow(non_snake_case)]
169 let &[ref LPFILETIME] =
170 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
171 this.GetSystemTimeAsFileTime(LPFILETIME)?;
173 "QueryPerformanceCounter" => {
174 #[allow(non_snake_case)]
175 let &[ref lpPerformanceCount] =
176 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
177 let result = this.QueryPerformanceCounter(lpPerformanceCount)?;
178 this.write_scalar(Scalar::from_i32(result), dest)?;
180 "QueryPerformanceFrequency" => {
181 #[allow(non_snake_case)]
182 let &[ref lpFrequency] =
183 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
184 let result = this.QueryPerformanceFrequency(lpFrequency)?;
185 this.write_scalar(Scalar::from_i32(result), dest)?;
188 // Synchronization primitives
189 "AcquireSRWLockExclusive" => {
191 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
192 this.AcquireSRWLockExclusive(ptr)?;
194 "ReleaseSRWLockExclusive" => {
196 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
197 this.ReleaseSRWLockExclusive(ptr)?;
199 "TryAcquireSRWLockExclusive" => {
201 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
202 let ret = this.TryAcquireSRWLockExclusive(ptr)?;
203 this.write_scalar(Scalar::from_u8(ret), dest)?;
205 "AcquireSRWLockShared" => {
207 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
208 this.AcquireSRWLockShared(ptr)?;
210 "ReleaseSRWLockShared" => {
212 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
213 this.ReleaseSRWLockShared(ptr)?;
215 "TryAcquireSRWLockShared" => {
217 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
218 let ret = this.TryAcquireSRWLockShared(ptr)?;
219 this.write_scalar(Scalar::from_u8(ret), dest)?;
222 // Dynamic symbol loading
223 "GetProcAddress" => {
224 #[allow(non_snake_case)]
225 let &[ref hModule, ref lpProcName] =
226 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
227 this.read_scalar(hModule)?.to_machine_isize(this)?;
228 let name = this.read_c_str(this.read_pointer(lpProcName)?)?;
229 if let Some(dlsym) = Dlsym::from_str(name, &this.tcx.sess.target.os)? {
230 let ptr = this.create_fn_alloc_ptr(FnVal::Other(dlsym));
231 this.write_pointer(ptr, dest)?;
233 this.write_null(dest)?;
238 "SystemFunction036" => {
239 // This is really 'RtlGenRandom'.
240 let &[ref ptr, ref len] =
241 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
242 let ptr = this.read_pointer(ptr)?;
243 let len = this.read_scalar(len)?.to_u32()?;
244 this.gen_random(ptr, len.into())?;
245 this.write_scalar(Scalar::from_bool(true), dest)?;
247 "BCryptGenRandom" => {
248 let &[ref algorithm, ref ptr, ref len, ref flags] =
249 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
250 let algorithm = this.read_scalar(algorithm)?;
251 let ptr = this.read_pointer(ptr)?;
252 let len = this.read_scalar(len)?.to_u32()?;
253 let flags = this.read_scalar(flags)?.to_u32()?;
255 // ^ BCRYPT_USE_SYSTEM_PREFERRED_RNG
257 "BCryptGenRandom is supported only with the BCRYPT_USE_SYSTEM_PREFERRED_RNG flag"
260 if algorithm.to_machine_usize(this)? != 0 {
262 "BCryptGenRandom algorithm must be NULL when the flag is BCRYPT_USE_SYSTEM_PREFERRED_RNG"
265 this.gen_random(ptr, len.into())?;
266 this.write_null(dest)?; // STATUS_SUCCESS
268 "GetConsoleScreenBufferInfo" => {
269 // `term` needs this, so we fake it.
270 let &[ref console, ref buffer_info] =
271 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
272 this.read_scalar(console)?.to_machine_isize(this)?;
273 this.deref_operand(buffer_info)?;
274 // Indicate an error.
275 // FIXME: we should set last_error, but to what?
276 this.write_null(dest)?;
278 "GetConsoleMode" => {
279 // Windows "isatty" (in libtest) needs this, so we fake it.
280 let &[ref console, ref mode] =
281 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
282 this.read_scalar(console)?.to_machine_isize(this)?;
283 this.deref_operand(mode)?;
284 // Indicate an error.
285 // FIXME: we should set last_error, but to what?
286 this.write_null(dest)?;
288 "SwitchToThread" => {
289 let &[] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
290 // Note that once Miri supports concurrency, this will need to return a nonzero
291 // value if this call does result in switching to another thread.
292 this.write_null(dest)?;
296 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
297 let which = this.read_scalar(which)?.to_i32()?;
298 // We just make this the identity function, so we know later in `NtWriteFile` which
299 // one it is. This is very fake, but libtest needs it so we cannot make it a
301 this.write_scalar(Scalar::from_machine_isize(which.into(), this), dest)?;
304 // Better error for attempts to create a thread
306 let &[_, _, _, _, _, _] =
307 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
309 this.handle_unsupported("can't create threads on Windows")?;
310 return Ok(EmulateByNameResult::AlreadyJumped);
313 // Incomplete shims that we "stub out" just to get pre-main initialization code to work.
314 // These shims are enabled only when the caller is in the standard library.
315 "GetProcessHeap" if this.frame_in_std() => {
316 let &[] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
317 // Just fake a HANDLE
318 this.write_scalar(Scalar::from_machine_isize(1, this), dest)?;
320 "GetModuleHandleA" if this.frame_in_std() => {
321 #[allow(non_snake_case)]
322 let &[_lpModuleName] =
323 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
324 // We need to return something non-null here to make `compat_fn!` work.
325 this.write_scalar(Scalar::from_machine_isize(1, this), dest)?;
327 "SetConsoleTextAttribute" if this.frame_in_std() => {
328 #[allow(non_snake_case)]
329 let &[ref _hConsoleOutput, ref _wAttribute] =
330 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
331 // Pretend these does not exist / nothing happened, by returning zero.
332 this.write_null(dest)?;
334 "AddVectoredExceptionHandler" if this.frame_in_std() => {
335 #[allow(non_snake_case)]
336 let &[ref _First, ref _Handler] =
337 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
338 // Any non zero value works for the stdlib. This is just used for stack overflows anyway.
339 this.write_scalar(Scalar::from_machine_usize(1, this), dest)?;
341 "SetThreadStackGuarantee" if this.frame_in_std() => {
342 #[allow(non_snake_case)]
343 let &[_StackSizeInBytes] =
344 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
345 // Any non zero value works for the stdlib. This is just used for stack overflows anyway.
346 this.write_scalar(Scalar::from_u32(1), dest)?;
348 | "InitializeCriticalSection"
349 | "EnterCriticalSection"
350 | "LeaveCriticalSection"
351 | "DeleteCriticalSection"
352 if this.frame_in_std() =>
354 #[allow(non_snake_case)]
355 let &[ref _lpCriticalSection] =
356 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
358 this.get_total_thread_count(),
360 "concurrency on Windows is not supported"
362 // Nothing to do, not even a return value.
363 // (Windows locks are reentrant, and we have only 1 thread,
364 // so not doing any futher checks here is at least not incorrect.)
366 "TryEnterCriticalSection" if this.frame_in_std() => {
367 #[allow(non_snake_case)]
368 let &[ref _lpCriticalSection] =
369 this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
371 this.get_total_thread_count(),
373 "concurrency on Windows is not supported"
375 // There is only one thread, so this always succeeds and returns TRUE.
376 this.write_scalar(Scalar::from_i32(1), dest)?;
379 _ => return Ok(EmulateByNameResult::NotSupported),
382 Ok(EmulateByNameResult::NeedsJumping)