]> git.lizzy.rs Git - rust.git/blob - src/shims/windows/foreign_items.rs
some clippy-induced cleanup
[rust.git] / src / shims / windows / foreign_items.rs
1 use std::iter;
2
3 use rustc_middle::mir;
4 use rustc_span::Symbol;
5 use rustc_target::abi::Size;
6 use rustc_target::spec::abi::Abi;
7
8 use crate::*;
9 use shims::foreign_items::EmulateByNameResult;
10 use shims::windows::sync::EvalContextExt as _;
11 use smallvec::SmallVec;
12
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(
16         &mut self,
17         link_name: Symbol,
18         abi: Abi,
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();
24
25         // Windows API stubs.
26         // HANDLE = isize
27         // NTSTATUS = LONH = i32
28         // DWORD = ULONG = u32
29         // BOOL = i32
30         // BOOLEAN = u8
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)?;
38             }
39             "SetEnvironmentVariableW" => {
40                 let [name, value] =
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)?;
44             }
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)?;
49             }
50             "FreeEnvironmentStringsW" => {
51                 let [env_block] =
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)?;
55             }
56             "GetCurrentDirectoryW" => {
57                 let [size, buf] =
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)?;
61             }
62             "SetCurrentDirectoryW" => {
63                 let [path] =
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)?;
67             }
68
69             // Allocation
70             "HeapAlloc" => {
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)?;
79             }
80             "HeapFree" => {
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)?;
88             }
89             "HeapReAlloc" => {
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)?;
98             }
99
100             // errno
101             "SetLastError" => {
102                 let [error] =
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)?;
106             }
107             "GetLastError" => {
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)?;
111             }
112
113             // Querying system information
114             "GetSystemInfo" => {
115                 let [system_info] =
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(
120                     system_info.ptr,
121                     iter::repeat(0u8).take(system_info.layout.size.bytes() as usize),
122                 )?;
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;
127
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.
131                 let field_sizes = [
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
143                 ];
144                 let field_offsets: SmallVec<[Size; 11]> = field_sizes
145                     .iter()
146                     .copied()
147                     .scan(Size::ZERO, |a, x| {
148                         let res = Some(*a);
149                         *a += x;
150                         res
151                     })
152                     .collect();
153
154                 // Set page size.
155                 let page_size = system_info.offset(
156                     field_offsets[2],
157                     MemPlaceMeta::None,
158                     dword_layout,
159                     &this.tcx,
160                 )?;
161                 this.write_scalar(
162                     Scalar::from_int(PAGE_SIZE, dword_layout.size),
163                     &page_size.into(),
164                 )?;
165                 // Set number of processors.
166                 let num_cpus = system_info.offset(
167                     field_offsets[6],
168                     MemPlaceMeta::None,
169                     dword_layout,
170                     &this.tcx,
171                 )?;
172                 this.write_scalar(Scalar::from_int(NUM_CPUS, dword_layout.size), &num_cpus.into())?;
173             }
174
175             // Thread-local storage
176             "TlsAlloc" => {
177                 // This just creates a key; Windows does not natively support TLS destructors.
178
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)?;
183             }
184             "TlsGetValue" => {
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)?;
190             }
191             "TlsSetValue" => {
192                 let [key, new_ptr] =
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)?;
198
199                 // Return success (`1`).
200                 this.write_scalar(Scalar::from_i32(1), dest)?;
201             }
202
203             // Access to command-line arguments
204             "GetCommandLineW" => {
205                 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
206                 this.write_pointer(
207                     this.machine.cmd_line.expect("machine must be initialized").ptr,
208                     dest,
209                 )?;
210             }
211
212             // Time related shims
213             "GetSystemTimeAsFileTime" => {
214                 #[allow(non_snake_case)]
215                 let [LPFILETIME] =
216                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
217                 this.GetSystemTimeAsFileTime(LPFILETIME)?;
218             }
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)?;
225             }
226             "QueryPerformanceFrequency" => {
227                 #[allow(non_snake_case)]
228                 let [lpFrequency] =
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)?;
232             }
233
234             // Synchronization primitives
235             "AcquireSRWLockExclusive" => {
236                 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
237                 this.AcquireSRWLockExclusive(ptr)?;
238             }
239             "ReleaseSRWLockExclusive" => {
240                 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
241                 this.ReleaseSRWLockExclusive(ptr)?;
242             }
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)?;
247             }
248             "AcquireSRWLockShared" => {
249                 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
250                 this.AcquireSRWLockShared(ptr)?;
251             }
252             "ReleaseSRWLockShared" => {
253                 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
254                 this.ReleaseSRWLockShared(ptr)?;
255             }
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)?;
260             }
261
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)?;
272                 } else {
273                     this.write_null(dest)?;
274                 }
275             }
276
277             // Miscellaneous
278             "SystemFunction036" => {
279                 // This is really 'RtlGenRandom'.
280                 let [ptr, len] =
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)?;
286             }
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()?;
294                 if flags != 2 {
295                     //      ^ BCRYPT_USE_SYSTEM_PREFERRED_RNG
296                     throw_unsup_format!(
297                         "BCryptGenRandom is supported only with the BCRYPT_USE_SYSTEM_PREFERRED_RNG flag"
298                     );
299                 }
300                 if algorithm.to_machine_usize(this)? != 0 {
301                     throw_unsup_format!(
302                         "BCryptGenRandom algorithm must be NULL when the flag is BCRYPT_USE_SYSTEM_PREFERRED_RNG"
303                     );
304                 }
305                 this.gen_random(ptr, len.into())?;
306                 this.write_null(dest)?; // STATUS_SUCCESS
307             }
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)?;
317             }
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)?;
327             }
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)?;
333             }
334             "GetStdHandle" => {
335                 let [which] =
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
340                 // std-only shim.
341                 this.write_scalar(Scalar::from_machine_isize(which.into(), this), dest)?;
342             }
343
344             // Better error for attempts to create a thread
345             "CreateThread" => {
346                 let [_, _, _, _, _, _] =
347                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
348
349                 this.handle_unsupported("can't create threads on Windows")?;
350                 return Ok(EmulateByNameResult::AlreadyJumped);
351             }
352
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)?;
359             }
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)?;
366             }
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)?;
373             }
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)?;
380             }
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)?;
387             }
388             | "InitializeCriticalSection"
389             | "EnterCriticalSection"
390             | "LeaveCriticalSection"
391             | "DeleteCriticalSection"
392                 if this.frame_in_std() =>
393             {
394                 #[allow(non_snake_case)]
395                 let [_lpCriticalSection] =
396                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
397                 assert_eq!(
398                     this.get_total_thread_count(),
399                     1,
400                     "concurrency on Windows is not supported"
401                 );
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.)
405             }
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)?;
410                 assert_eq!(
411                     this.get_total_thread_count(),
412                     1,
413                     "concurrency on Windows is not supported"
414                 );
415                 // There is only one thread, so this always succeeds and returns TRUE.
416                 this.write_scalar(Scalar::from_i32(1), dest)?;
417             }
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)?;
421             }
422
423             _ => return Ok(EmulateByNameResult::NotSupported),
424         }
425
426         Ok(EmulateByNameResult::NeedsJumping)
427     }
428 }