]> git.lizzy.rs Git - rust.git/blob - src/shims/windows/foreign_items.rs
Dynamic offset calculation in GetSystemInfo.
[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                 use crate::rustc_middle::ty::{layout::LayoutOf, TyKind, UintTy};
116
117                 let [system_info] =
118                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
119                 let system_info = this.deref_operand(system_info)?;
120                 // Initialize with `0`.
121                 this.write_bytes_ptr(
122                     system_info.ptr,
123                     iter::repeat(0u8).take(system_info.layout.size.bytes() as usize),
124                 )?;
125                 // Set selected fields.
126                 let word_ty = this.tcx.mk_ty(TyKind::Uint(UintTy::U16));
127                 let dword_ty = this.tcx.mk_ty(TyKind::Uint(UintTy::U32));
128                 let usize_ty = this.tcx.mk_ty(TyKind::Uint(UintTy::Usize));
129                 let word_layout = this.layout_of(word_ty)?;
130                 let dword_layout = this.layout_of(dword_ty)?;
131                 let usize_layout = this.layout_of(usize_ty)?;
132
133                 // Using `mplace_field` is error-prone, see: https://github.com/rust-lang/miri/issues/2136.
134                 // Pointer fields have different sizes on different targets.
135                 // To avoid all these issue we calculate the offsets dynamically.
136                 let field_sizes = [
137                     word_layout.size,  // 0,  wProcessorArchitecture      : WORD
138                     word_layout.size,  // 1,  wReserved                   : WORD
139                     dword_layout.size, // 2,  dwPageSize                  : DWORD
140                     usize_layout.size, // 3,  lpMinimumApplicationAddress : LPVOID
141                     usize_layout.size, // 4,  lpMaximumApplicationAddress : LPVOID
142                     usize_layout.size, // 5,  dwActiveProcessorMask       : DWORD_PTR
143                     dword_layout.size, // 6,  dwNumberOfProcessors        : DWORD
144                     dword_layout.size, // 7,  dwProcessorType             : DWORD
145                     dword_layout.size, // 8,  dwAllocationGranularity     : DWORD
146                     word_layout.size,  // 9,  wProcessorLevel             : WORD
147                     word_layout.size,  // 10, wProcessorRevision          : WORD
148                 ];
149                 let field_offsets: SmallVec<[Size; 11]> = field_sizes
150                     .iter()
151                     .copied()
152                     .scan(Size::ZERO, |a, x| {
153                         let res = Some(*a);
154                         *a += x;
155                         res
156                     })
157                     .collect();
158
159                 // Set page size.
160                 let page_size = system_info.offset(
161                     field_offsets[2],
162                     MemPlaceMeta::None,
163                     dword_layout,
164                     &this.tcx,
165                 )?;
166                 this.write_scalar(
167                     Scalar::from_int(PAGE_SIZE, dword_layout.size),
168                     &page_size.into(),
169                 )?;
170                 // Set number of processors.
171                 let num_cpus = system_info.offset(
172                     field_offsets[6],
173                     MemPlaceMeta::None,
174                     dword_layout,
175                     &this.tcx,
176                 )?;
177                 this.write_scalar(Scalar::from_int(NUM_CPUS, dword_layout.size), &num_cpus.into())?;
178             }
179
180             // Thread-local storage
181             "TlsAlloc" => {
182                 // This just creates a key; Windows does not natively support TLS destructors.
183
184                 // Create key and return it.
185                 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
186                 let key = this.machine.tls.create_tls_key(None, dest.layout.size)?;
187                 this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?;
188             }
189             "TlsGetValue" => {
190                 let [key] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
191                 let key = u128::from(this.read_scalar(key)?.to_u32()?);
192                 let active_thread = this.get_active_thread();
193                 let ptr = this.machine.tls.load_tls(key, active_thread, this)?;
194                 this.write_scalar(ptr, dest)?;
195             }
196             "TlsSetValue" => {
197                 let [key, new_ptr] =
198                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
199                 let key = u128::from(this.read_scalar(key)?.to_u32()?);
200                 let active_thread = this.get_active_thread();
201                 let new_data = this.read_scalar(new_ptr)?.check_init()?;
202                 this.machine.tls.store_tls(key, active_thread, new_data, &*this.tcx)?;
203
204                 // Return success (`1`).
205                 this.write_scalar(Scalar::from_i32(1), dest)?;
206             }
207
208             // Access to command-line arguments
209             "GetCommandLineW" => {
210                 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
211                 this.write_pointer(
212                     this.machine.cmd_line.expect("machine must be initialized").ptr,
213                     dest,
214                 )?;
215             }
216
217             // Time related shims
218             "GetSystemTimeAsFileTime" => {
219                 #[allow(non_snake_case)]
220                 let [LPFILETIME] =
221                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
222                 this.GetSystemTimeAsFileTime(LPFILETIME)?;
223             }
224             "QueryPerformanceCounter" => {
225                 #[allow(non_snake_case)]
226                 let [lpPerformanceCount] =
227                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
228                 let result = this.QueryPerformanceCounter(lpPerformanceCount)?;
229                 this.write_scalar(Scalar::from_i32(result), dest)?;
230             }
231             "QueryPerformanceFrequency" => {
232                 #[allow(non_snake_case)]
233                 let [lpFrequency] =
234                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
235                 let result = this.QueryPerformanceFrequency(lpFrequency)?;
236                 this.write_scalar(Scalar::from_i32(result), dest)?;
237             }
238
239             // Synchronization primitives
240             "AcquireSRWLockExclusive" => {
241                 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
242                 this.AcquireSRWLockExclusive(ptr)?;
243             }
244             "ReleaseSRWLockExclusive" => {
245                 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
246                 this.ReleaseSRWLockExclusive(ptr)?;
247             }
248             "TryAcquireSRWLockExclusive" => {
249                 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
250                 let ret = this.TryAcquireSRWLockExclusive(ptr)?;
251                 this.write_scalar(Scalar::from_u8(ret), dest)?;
252             }
253             "AcquireSRWLockShared" => {
254                 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
255                 this.AcquireSRWLockShared(ptr)?;
256             }
257             "ReleaseSRWLockShared" => {
258                 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
259                 this.ReleaseSRWLockShared(ptr)?;
260             }
261             "TryAcquireSRWLockShared" => {
262                 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
263                 let ret = this.TryAcquireSRWLockShared(ptr)?;
264                 this.write_scalar(Scalar::from_u8(ret), dest)?;
265             }
266
267             // Dynamic symbol loading
268             "GetProcAddress" => {
269                 #[allow(non_snake_case)]
270                 let [hModule, lpProcName] =
271                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
272                 this.read_scalar(hModule)?.to_machine_isize(this)?;
273                 let name = this.read_c_str(this.read_pointer(lpProcName)?)?;
274                 if let Some(dlsym) = Dlsym::from_str(name, &this.tcx.sess.target.os)? {
275                     let ptr = this.create_fn_alloc_ptr(FnVal::Other(dlsym));
276                     this.write_pointer(ptr, dest)?;
277                 } else {
278                     this.write_null(dest)?;
279                 }
280             }
281
282             // Miscellaneous
283             "SystemFunction036" => {
284                 // This is really 'RtlGenRandom'.
285                 let [ptr, len] =
286                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
287                 let ptr = this.read_pointer(ptr)?;
288                 let len = this.read_scalar(len)?.to_u32()?;
289                 this.gen_random(ptr, len.into())?;
290                 this.write_scalar(Scalar::from_bool(true), dest)?;
291             }
292             "BCryptGenRandom" => {
293                 let [algorithm, ptr, len, flags] =
294                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
295                 let algorithm = this.read_scalar(algorithm)?;
296                 let ptr = this.read_pointer(ptr)?;
297                 let len = this.read_scalar(len)?.to_u32()?;
298                 let flags = this.read_scalar(flags)?.to_u32()?;
299                 if flags != 2 {
300                     //      ^ BCRYPT_USE_SYSTEM_PREFERRED_RNG
301                     throw_unsup_format!(
302                         "BCryptGenRandom is supported only with the BCRYPT_USE_SYSTEM_PREFERRED_RNG flag"
303                     );
304                 }
305                 if algorithm.to_machine_usize(this)? != 0 {
306                     throw_unsup_format!(
307                         "BCryptGenRandom algorithm must be NULL when the flag is BCRYPT_USE_SYSTEM_PREFERRED_RNG"
308                     );
309                 }
310                 this.gen_random(ptr, len.into())?;
311                 this.write_null(dest)?; // STATUS_SUCCESS
312             }
313             "GetConsoleScreenBufferInfo" => {
314                 // `term` needs this, so we fake it.
315                 let [console, buffer_info] =
316                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
317                 this.read_scalar(console)?.to_machine_isize(this)?;
318                 this.deref_operand(buffer_info)?;
319                 // Indicate an error.
320                 // FIXME: we should set last_error, but to what?
321                 this.write_null(dest)?;
322             }
323             "GetConsoleMode" => {
324                 // Windows "isatty" (in libtest) needs this, so we fake it.
325                 let [console, mode] =
326                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
327                 this.read_scalar(console)?.to_machine_isize(this)?;
328                 this.deref_operand(mode)?;
329                 // Indicate an error.
330                 // FIXME: we should set last_error, but to what?
331                 this.write_null(dest)?;
332             }
333             "SwitchToThread" => {
334                 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
335                 // Note that once Miri supports concurrency, this will need to return a nonzero
336                 // value if this call does result in switching to another thread.
337                 this.write_null(dest)?;
338             }
339             "GetStdHandle" => {
340                 let [which] =
341                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
342                 let which = this.read_scalar(which)?.to_i32()?;
343                 // We just make this the identity function, so we know later in `NtWriteFile` which
344                 // one it is. This is very fake, but libtest needs it so we cannot make it a
345                 // std-only shim.
346                 this.write_scalar(Scalar::from_machine_isize(which.into(), this), dest)?;
347             }
348
349             // Better error for attempts to create a thread
350             "CreateThread" => {
351                 let [_, _, _, _, _, _] =
352                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
353
354                 this.handle_unsupported("can't create threads on Windows")?;
355                 return Ok(EmulateByNameResult::AlreadyJumped);
356             }
357
358             // Incomplete shims that we "stub out" just to get pre-main initialization code to work.
359             // These shims are enabled only when the caller is in the standard library.
360             "GetProcessHeap" if this.frame_in_std() => {
361                 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
362                 // Just fake a HANDLE
363                 this.write_scalar(Scalar::from_machine_isize(1, this), dest)?;
364             }
365             "GetModuleHandleA" if this.frame_in_std() => {
366                 #[allow(non_snake_case)]
367                 let [_lpModuleName] =
368                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
369                 // We need to return something non-null here to make `compat_fn!` work.
370                 this.write_scalar(Scalar::from_machine_isize(1, this), dest)?;
371             }
372             "SetConsoleTextAttribute" if this.frame_in_std() => {
373                 #[allow(non_snake_case)]
374                 let [_hConsoleOutput, _wAttribute] =
375                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
376                 // Pretend these does not exist / nothing happened, by returning zero.
377                 this.write_null(dest)?;
378             }
379             "AddVectoredExceptionHandler" if this.frame_in_std() => {
380                 #[allow(non_snake_case)]
381                 let [_First, _Handler] =
382                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
383                 // Any non zero value works for the stdlib. This is just used for stack overflows anyway.
384                 this.write_scalar(Scalar::from_machine_usize(1, this), dest)?;
385             }
386             "SetThreadStackGuarantee" if this.frame_in_std() => {
387                 #[allow(non_snake_case)]
388                 let [_StackSizeInBytes] =
389                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
390                 // Any non zero value works for the stdlib. This is just used for stack overflows anyway.
391                 this.write_scalar(Scalar::from_u32(1), dest)?;
392             }
393             | "InitializeCriticalSection"
394             | "EnterCriticalSection"
395             | "LeaveCriticalSection"
396             | "DeleteCriticalSection"
397                 if this.frame_in_std() =>
398             {
399                 #[allow(non_snake_case)]
400                 let [_lpCriticalSection] =
401                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
402                 assert_eq!(
403                     this.get_total_thread_count(),
404                     1,
405                     "concurrency on Windows is not supported"
406                 );
407                 // Nothing to do, not even a return value.
408                 // (Windows locks are reentrant, and we have only 1 thread,
409                 // so not doing any futher checks here is at least not incorrect.)
410             }
411             "TryEnterCriticalSection" if this.frame_in_std() => {
412                 #[allow(non_snake_case)]
413                 let [_lpCriticalSection] =
414                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
415                 assert_eq!(
416                     this.get_total_thread_count(),
417                     1,
418                     "concurrency on Windows is not supported"
419                 );
420                 // There is only one thread, so this always succeeds and returns TRUE.
421                 this.write_scalar(Scalar::from_i32(1), dest)?;
422             }
423
424             _ => return Ok(EmulateByNameResult::NotSupported),
425         }
426
427         Ok(EmulateByNameResult::NeedsJumping)
428     }
429 }