]> git.lizzy.rs Git - rust.git/blob - src/shims/windows/foreign_items.rs
f9cf755c5b4f5752c053327eaa6a1dd2eec37a87
[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
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, Tag>],
19         dest: &PlaceTy<'tcx, Tag>,
20         _ret: mir::BasicBlock,
21     ) -> InterpResult<'tcx, EmulateByNameResult<'mir, 'tcx>> {
22         let this = self.eval_context_mut();
23
24         // Windows API stubs.
25         // HANDLE = isize
26         // NTSTATUS = LONH = i32
27         // DWORD = ULONG = u32
28         // BOOL = i32
29         // BOOLEAN = u8
30         match &*link_name.as_str() {
31             // Environment related shims
32             "GetEnvironmentVariableW" => {
33                 let [name, buf, 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)?;
37             }
38             "SetEnvironmentVariableW" => {
39                 let [name, 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)?;
43             }
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)?;
48             }
49             "FreeEnvironmentStringsW" => {
50                 let [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)?;
54             }
55             "GetCurrentDirectoryW" => {
56                 let [size, 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)?;
60             }
61             "SetCurrentDirectoryW" => {
62                 let [path] =
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)?;
66             }
67
68             // Allocation
69             "HeapAlloc" => {
70                 let [handle, flags, 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)?;
78             }
79             "HeapFree" => {
80                 let [handle, flags, 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)?;
87             }
88             "HeapReAlloc" => {
89                 let [handle, flags, ptr, 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)?;
97             }
98
99             // errno
100             "SetLastError" => {
101                 let [error] =
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)?;
105             }
106             "GetLastError" => {
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)?;
110             }
111
112             // Querying system information
113             "GetSystemInfo" => {
114                 use crate::rustc_middle::ty::{layout::LayoutOf, TyKind, UintTy};
115
116                 let [system_info] =
117                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
118                 let system_info = this.deref_operand(system_info)?;
119                 // Initialize with `0`.
120                 this.write_bytes_ptr(
121                     system_info.ptr,
122                     iter::repeat(0u8).take(system_info.layout.size.bytes() as usize),
123                 )?;
124                 // Set selected fields.
125                 let dword_ty = this.tcx.mk_ty(TyKind::Uint(UintTy::U32));
126                 let dword_layout = this.layout_of(dword_ty)?;
127                 // Set page size.
128                 let page_size = system_info.offset(
129                     Size::from_bytes(4),
130                     MemPlaceMeta::None,
131                     dword_layout,
132                     &this.tcx,
133                 )?;
134                 this.write_scalar(
135                     Scalar::from_int(PAGE_SIZE, dword_layout.size),
136                     &page_size.into(),
137                 )?;
138                 // Set number of processors.
139                 let num_cpus = system_info.offset(
140                     Size::from_bytes(32),
141                     MemPlaceMeta::None,
142                     dword_layout,
143                     &this.tcx,
144                 )?;
145                 this.write_scalar(Scalar::from_int(NUM_CPUS, dword_layout.size), &num_cpus.into())?;
146             }
147
148             // Thread-local storage
149             "TlsAlloc" => {
150                 // This just creates a key; Windows does not natively support TLS destructors.
151
152                 // Create key and return it.
153                 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
154                 let key = this.machine.tls.create_tls_key(None, dest.layout.size)?;
155                 this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?;
156             }
157             "TlsGetValue" => {
158                 let [key] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
159                 let key = u128::from(this.read_scalar(key)?.to_u32()?);
160                 let active_thread = this.get_active_thread();
161                 let ptr = this.machine.tls.load_tls(key, active_thread, this)?;
162                 this.write_scalar(ptr, dest)?;
163             }
164             "TlsSetValue" => {
165                 let [key, new_ptr] =
166                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
167                 let key = u128::from(this.read_scalar(key)?.to_u32()?);
168                 let active_thread = this.get_active_thread();
169                 let new_data = this.read_scalar(new_ptr)?.check_init()?;
170                 this.machine.tls.store_tls(key, active_thread, new_data, &*this.tcx)?;
171
172                 // Return success (`1`).
173                 this.write_scalar(Scalar::from_i32(1), dest)?;
174             }
175
176             // Access to command-line arguments
177             "GetCommandLineW" => {
178                 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
179                 this.write_pointer(
180                     this.machine.cmd_line.expect("machine must be initialized").ptr,
181                     dest,
182                 )?;
183             }
184
185             // Time related shims
186             "GetSystemTimeAsFileTime" => {
187                 #[allow(non_snake_case)]
188                 let [LPFILETIME] =
189                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
190                 this.GetSystemTimeAsFileTime(LPFILETIME)?;
191             }
192             "QueryPerformanceCounter" => {
193                 #[allow(non_snake_case)]
194                 let [lpPerformanceCount] =
195                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
196                 let result = this.QueryPerformanceCounter(lpPerformanceCount)?;
197                 this.write_scalar(Scalar::from_i32(result), dest)?;
198             }
199             "QueryPerformanceFrequency" => {
200                 #[allow(non_snake_case)]
201                 let [lpFrequency] =
202                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
203                 let result = this.QueryPerformanceFrequency(lpFrequency)?;
204                 this.write_scalar(Scalar::from_i32(result), dest)?;
205             }
206
207             // Synchronization primitives
208             "AcquireSRWLockExclusive" => {
209                 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
210                 this.AcquireSRWLockExclusive(ptr)?;
211             }
212             "ReleaseSRWLockExclusive" => {
213                 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
214                 this.ReleaseSRWLockExclusive(ptr)?;
215             }
216             "TryAcquireSRWLockExclusive" => {
217                 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
218                 let ret = this.TryAcquireSRWLockExclusive(ptr)?;
219                 this.write_scalar(Scalar::from_u8(ret), dest)?;
220             }
221             "AcquireSRWLockShared" => {
222                 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
223                 this.AcquireSRWLockShared(ptr)?;
224             }
225             "ReleaseSRWLockShared" => {
226                 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
227                 this.ReleaseSRWLockShared(ptr)?;
228             }
229             "TryAcquireSRWLockShared" => {
230                 let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
231                 let ret = this.TryAcquireSRWLockShared(ptr)?;
232                 this.write_scalar(Scalar::from_u8(ret), dest)?;
233             }
234
235             // Dynamic symbol loading
236             "GetProcAddress" => {
237                 #[allow(non_snake_case)]
238                 let [hModule, lpProcName] =
239                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
240                 this.read_scalar(hModule)?.to_machine_isize(this)?;
241                 let name = this.read_c_str(this.read_pointer(lpProcName)?)?;
242                 if let Some(dlsym) = Dlsym::from_str(name, &this.tcx.sess.target.os)? {
243                     let ptr = this.create_fn_alloc_ptr(FnVal::Other(dlsym));
244                     this.write_pointer(ptr, dest)?;
245                 } else {
246                     this.write_null(dest)?;
247                 }
248             }
249
250             // Miscellaneous
251             "SystemFunction036" => {
252                 // This is really 'RtlGenRandom'.
253                 let [ptr, len] =
254                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
255                 let ptr = this.read_pointer(ptr)?;
256                 let len = this.read_scalar(len)?.to_u32()?;
257                 this.gen_random(ptr, len.into())?;
258                 this.write_scalar(Scalar::from_bool(true), dest)?;
259             }
260             "BCryptGenRandom" => {
261                 let [algorithm, ptr, len, flags] =
262                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
263                 let algorithm = this.read_scalar(algorithm)?;
264                 let ptr = this.read_pointer(ptr)?;
265                 let len = this.read_scalar(len)?.to_u32()?;
266                 let flags = this.read_scalar(flags)?.to_u32()?;
267                 if flags != 2 {
268                     //      ^ BCRYPT_USE_SYSTEM_PREFERRED_RNG
269                     throw_unsup_format!(
270                         "BCryptGenRandom is supported only with the BCRYPT_USE_SYSTEM_PREFERRED_RNG flag"
271                     );
272                 }
273                 if algorithm.to_machine_usize(this)? != 0 {
274                     throw_unsup_format!(
275                         "BCryptGenRandom algorithm must be NULL when the flag is BCRYPT_USE_SYSTEM_PREFERRED_RNG"
276                     );
277                 }
278                 this.gen_random(ptr, len.into())?;
279                 this.write_null(dest)?; // STATUS_SUCCESS
280             }
281             "GetConsoleScreenBufferInfo" => {
282                 // `term` needs this, so we fake it.
283                 let [console, buffer_info] =
284                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
285                 this.read_scalar(console)?.to_machine_isize(this)?;
286                 this.deref_operand(buffer_info)?;
287                 // Indicate an error.
288                 // FIXME: we should set last_error, but to what?
289                 this.write_null(dest)?;
290             }
291             "GetConsoleMode" => {
292                 // Windows "isatty" (in libtest) needs this, so we fake it.
293                 let [console, mode] =
294                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
295                 this.read_scalar(console)?.to_machine_isize(this)?;
296                 this.deref_operand(mode)?;
297                 // Indicate an error.
298                 // FIXME: we should set last_error, but to what?
299                 this.write_null(dest)?;
300             }
301             "SwitchToThread" => {
302                 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
303                 // Note that once Miri supports concurrency, this will need to return a nonzero
304                 // value if this call does result in switching to another thread.
305                 this.write_null(dest)?;
306             }
307             "GetStdHandle" => {
308                 let [which] =
309                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
310                 let which = this.read_scalar(which)?.to_i32()?;
311                 // We just make this the identity function, so we know later in `NtWriteFile` which
312                 // one it is. This is very fake, but libtest needs it so we cannot make it a
313                 // std-only shim.
314                 this.write_scalar(Scalar::from_machine_isize(which.into(), this), dest)?;
315             }
316
317             // Better error for attempts to create a thread
318             "CreateThread" => {
319                 let [_, _, _, _, _, _] =
320                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
321
322                 this.handle_unsupported("can't create threads on Windows")?;
323                 return Ok(EmulateByNameResult::AlreadyJumped);
324             }
325
326             // Incomplete shims that we "stub out" just to get pre-main initialization code to work.
327             // These shims are enabled only when the caller is in the standard library.
328             "GetProcessHeap" if this.frame_in_std() => {
329                 let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
330                 // Just fake a HANDLE
331                 this.write_scalar(Scalar::from_machine_isize(1, this), dest)?;
332             }
333             "GetModuleHandleA" if this.frame_in_std() => {
334                 #[allow(non_snake_case)]
335                 let [_lpModuleName] =
336                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
337                 // We need to return something non-null here to make `compat_fn!` work.
338                 this.write_scalar(Scalar::from_machine_isize(1, this), dest)?;
339             }
340             "SetConsoleTextAttribute" if this.frame_in_std() => {
341                 #[allow(non_snake_case)]
342                 let [_hConsoleOutput, _wAttribute] =
343                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
344                 // Pretend these does not exist / nothing happened, by returning zero.
345                 this.write_null(dest)?;
346             }
347             "AddVectoredExceptionHandler" if this.frame_in_std() => {
348                 #[allow(non_snake_case)]
349                 let [_First, _Handler] =
350                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
351                 // Any non zero value works for the stdlib. This is just used for stack overflows anyway.
352                 this.write_scalar(Scalar::from_machine_usize(1, this), dest)?;
353             }
354             "SetThreadStackGuarantee" if this.frame_in_std() => {
355                 #[allow(non_snake_case)]
356                 let [_StackSizeInBytes] =
357                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
358                 // Any non zero value works for the stdlib. This is just used for stack overflows anyway.
359                 this.write_scalar(Scalar::from_u32(1), dest)?;
360             }
361             | "InitializeCriticalSection"
362             | "EnterCriticalSection"
363             | "LeaveCriticalSection"
364             | "DeleteCriticalSection"
365                 if this.frame_in_std() =>
366             {
367                 #[allow(non_snake_case)]
368                 let [_lpCriticalSection] =
369                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
370                 assert_eq!(
371                     this.get_total_thread_count(),
372                     1,
373                     "concurrency on Windows is not supported"
374                 );
375                 // Nothing to do, not even a return value.
376                 // (Windows locks are reentrant, and we have only 1 thread,
377                 // so not doing any futher checks here is at least not incorrect.)
378             }
379             "TryEnterCriticalSection" if this.frame_in_std() => {
380                 #[allow(non_snake_case)]
381                 let [_lpCriticalSection] =
382                     this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
383                 assert_eq!(
384                     this.get_total_thread_count(),
385                     1,
386                     "concurrency on Windows is not supported"
387                 );
388                 // There is only one thread, so this always succeeds and returns TRUE.
389                 this.write_scalar(Scalar::from_i32(1), dest)?;
390             }
391
392             _ => return Ok(EmulateByNameResult::NotSupported),
393         }
394
395         Ok(EmulateByNameResult::NeedsJumping)
396     }
397 }