]> git.lizzy.rs Git - rust.git/blob - src/shims/windows/foreign_items.rs
3f4b8b14002e78d183317995f47ea049159045b3
[rust.git] / src / shims / windows / foreign_items.rs
1 use std::iter;
2
3 use rustc_span::Symbol;
4 use rustc_target::abi::Size;
5 use rustc_target::spec::abi::Abi;
6
7 use crate::*;
8 use shims::foreign_items::EmulateByNameResult;
9 use shims::windows::sync::EvalContextExt as _;
10 use smallvec::SmallVec;
11
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(
15         &mut self,
16         link_name: Symbol,
17         abi: Abi,
18         args: &[OpTy<'tcx, Provenance>],
19         dest: &PlaceTy<'tcx, Provenance>,
20     ) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> {
21         let this = self.eval_context_mut();
22
23         // Windows API stubs.
24         // HANDLE = isize
25         // NTSTATUS = LONH = i32
26         // DWORD = ULONG = u32
27         // BOOL = i32
28         // BOOLEAN = u8
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)?;
36             }
37             "SetEnvironmentVariableW" => {
38                 let [name, value] =
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)?;
42             }
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)?;
47             }
48             "FreeEnvironmentStringsW" => {
49                 let [env_block] =
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)?;
53             }
54             "GetCurrentDirectoryW" => {
55                 let [size, buf] =
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)?;
59             }
60             "SetCurrentDirectoryW" => {
61                 let [path] =
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)?;
65             }
66
67             // Allocation
68             "HeapAlloc" => {
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)?;
77             }
78             "HeapFree" => {
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)?;
86             }
87             "HeapReAlloc" => {
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)?;
96             }
97
98             // errno
99             "SetLastError" => {
100                 let [error] =
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)?;
104             }
105             "GetLastError" => {
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)?;
109             }
110
111             // Querying system information
112             "GetSystemInfo" => {
113                 let [system_info] =
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(
118                     system_info.ptr,
119                     iter::repeat(0u8).take(system_info.layout.size.bytes() as usize),
120                 )?;
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;
125
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.
129                 let field_sizes = [
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
141                 ];
142                 let field_offsets: SmallVec<[Size; 11]> = field_sizes
143                     .iter()
144                     .copied()
145                     .scan(Size::ZERO, |a, x| {
146                         let res = Some(*a);
147                         *a += x;
148                         res
149                     })
150                     .collect();
151
152                 // Set page size.
153                 let page_size = system_info.offset(field_offsets[2], dword_layout, &this.tcx)?;
154                 this.write_scalar(
155                     Scalar::from_int(PAGE_SIZE, dword_layout.size),
156                     &page_size.into(),
157                 )?;
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())?;
161             }
162
163             // Thread-local storage
164             "TlsAlloc" => {
165                 // This just creates a key; Windows does not natively support TLS destructors.
166
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)?;
171             }
172             "TlsGetValue" => {
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)?;
178             }
179             "TlsSetValue" => {
180                 let [key, new_ptr] =
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)?;
186
187                 // Return success (`1`).
188                 this.write_scalar(Scalar::from_i32(1), dest)?;
189             }
190
191             // Access to command-line arguments
192             "GetCommandLineW" => {
193                 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
194                 this.write_pointer(
195                     this.machine.cmd_line.expect("machine must be initialized").ptr,
196                     dest,
197                 )?;
198             }
199
200             // Time related shims
201             "GetSystemTimeAsFileTime" => {
202                 #[allow(non_snake_case)]
203                 let [LPFILETIME] =
204                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
205                 this.GetSystemTimeAsFileTime(LPFILETIME)?;
206             }
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)?;
213             }
214             "QueryPerformanceFrequency" => {
215                 #[allow(non_snake_case)]
216                 let [lpFrequency] =
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)?;
220             }
221
222             // Synchronization primitives
223             "AcquireSRWLockExclusive" => {
224                 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
225                 this.AcquireSRWLockExclusive(ptr)?;
226             }
227             "ReleaseSRWLockExclusive" => {
228                 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
229                 this.ReleaseSRWLockExclusive(ptr)?;
230             }
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)?;
235             }
236             "AcquireSRWLockShared" => {
237                 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
238                 this.AcquireSRWLockShared(ptr)?;
239             }
240             "ReleaseSRWLockShared" => {
241                 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
242                 this.ReleaseSRWLockShared(ptr)?;
243             }
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)?;
248             }
249
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)?;
260                 } else {
261                     this.write_null(dest)?;
262                 }
263             }
264
265             // Miscellaneous
266             "SystemFunction036" => {
267                 // This is really 'RtlGenRandom'.
268                 let [ptr, len] =
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)?;
274             }
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()?;
282                 if flags != 2 {
283                     //      ^ BCRYPT_USE_SYSTEM_PREFERRED_RNG
284                     throw_unsup_format!(
285                         "BCryptGenRandom is supported only with the BCRYPT_USE_SYSTEM_PREFERRED_RNG flag"
286                     );
287                 }
288                 if algorithm.to_machine_usize(this)? != 0 {
289                     throw_unsup_format!(
290                         "BCryptGenRandom algorithm must be NULL when the flag is BCRYPT_USE_SYSTEM_PREFERRED_RNG"
291                     );
292                 }
293                 this.gen_random(ptr, len.into())?;
294                 this.write_null(dest)?; // STATUS_SUCCESS
295             }
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)?;
305             }
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)?;
315             }
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)?;
321             }
322             "GetStdHandle" => {
323                 let [which] =
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
328                 // std-only shim.
329                 this.write_scalar(Scalar::from_machine_isize(which.into(), this), dest)?;
330             }
331
332             // Better error for attempts to create a thread
333             "CreateThread" => {
334                 let [_, _, _, _, _, _] =
335                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
336
337                 this.handle_unsupported("can't create threads on Windows")?;
338                 return Ok(EmulateByNameResult::AlreadyJumped);
339             }
340
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)?;
347             }
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)?;
354             }
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)?;
361             }
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)?;
368             }
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)?;
375             }
376             | "InitializeCriticalSection"
377             | "EnterCriticalSection"
378             | "LeaveCriticalSection"
379             | "DeleteCriticalSection"
380                 if this.frame_in_std() =>
381             {
382                 #[allow(non_snake_case)]
383                 let [_lpCriticalSection] =
384                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
385                 assert_eq!(
386                     this.get_total_thread_count(),
387                     1,
388                     "concurrency on Windows is not supported"
389                 );
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.)
393             }
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)?;
398                 assert_eq!(
399                     this.get_total_thread_count(),
400                     1,
401                     "concurrency on Windows is not supported"
402                 );
403                 // There is only one thread, so this always succeeds and returns TRUE.
404                 this.write_scalar(Scalar::from_i32(1), dest)?;
405             }
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)?;
409             }
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)?;
414             }
415
416             _ => return Ok(EmulateByNameResult::NotSupported),
417         }
418
419         Ok(EmulateByNameResult::NeedsJumping)
420     }
421 }