]> git.lizzy.rs Git - rust.git/blob - src/shims/windows/foreign_items.rs
Check argument count for `CreateThread`
[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: &str,
17         link_name_sym: Symbol,
18         abi: Abi,
19         args: &[OpTy<'tcx, Tag>],
20         dest: &PlaceTy<'tcx, Tag>,
21         _ret: mir::BasicBlock,
22     ) -> InterpResult<'tcx, EmulateByNameResult> {
23         let this = self.eval_context_mut();
24
25         // Windows API stubs.
26         // HANDLE = isize
27         // DWORD = ULONG = u32
28         // BOOL = i32
29         // BOOLEAN = u8
30         match link_name {
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_sym, args)?;
35                 let result = this.GetEnvironmentVariableW(name, buf, size)?;
36                 this.write_scalar(Scalar::from_u32(result), dest)?;
37             }
38             "SetEnvironmentVariableW" => {
39                 let &[ref name, ref value] =
40                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
41                 let result = this.SetEnvironmentVariableW(name, value)?;
42                 this.write_scalar(Scalar::from_i32(result), dest)?;
43             }
44             "GetEnvironmentStringsW" => {
45                 let &[] =
46                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
47                 let result = this.GetEnvironmentStringsW()?;
48                 this.write_scalar(result, dest)?;
49             }
50             "FreeEnvironmentStringsW" => {
51                 let &[ref env_block] =
52                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
53                 let result = this.FreeEnvironmentStringsW(env_block)?;
54                 this.write_scalar(Scalar::from_i32(result), dest)?;
55             }
56             "GetCurrentDirectoryW" => {
57                 let &[ref size, ref buf] =
58                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
59                 let result = this.GetCurrentDirectoryW(size, buf)?;
60                 this.write_scalar(Scalar::from_u32(result), dest)?;
61             }
62             "SetCurrentDirectoryW" => {
63                 let &[ref path] =
64                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
65                 let result = this.SetCurrentDirectoryW(path)?;
66                 this.write_scalar(Scalar::from_i32(result), dest)?;
67             }
68
69             // File related shims
70             "GetStdHandle" => {
71                 let &[ref which] =
72                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
73                 let which = this.read_scalar(which)?.to_i32()?;
74                 // We just make this the identity function, so we know later in `WriteFile`
75                 // which one it is.
76                 this.write_scalar(Scalar::from_machine_isize(which.into(), this), dest)?;
77             }
78             "WriteFile" => {
79                 let &[ref handle, ref buf, ref n, ref written_ptr, ref overlapped] =
80                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
81                 this.read_scalar(overlapped)?.to_machine_usize(this)?; // this is a poiner, that we ignore
82                 let handle = this.read_scalar(handle)?.to_machine_isize(this)?;
83                 let buf = this.read_scalar(buf)?.check_init()?;
84                 let n = this.read_scalar(n)?.to_u32()?;
85                 let written_place = this.deref_operand(written_ptr)?;
86                 // Spec says to always write `0` first.
87                 this.write_null(&written_place.into())?;
88                 let written = if handle == -11 || handle == -12 {
89                     // stdout/stderr
90                     use std::io::{self, Write};
91
92                     let buf_cont = this.memory.read_bytes(buf, Size::from_bytes(u64::from(n)))?;
93                     let res = if handle == -11 {
94                         io::stdout().write(buf_cont)
95                     } else {
96                         io::stderr().write(buf_cont)
97                     };
98                     res.ok().map(|n| n as u32)
99                 } else {
100                     throw_unsup_format!(
101                         "on Windows, writing to anything except stdout/stderr is not supported"
102                     )
103                 };
104                 // If there was no error, write back how much was written.
105                 if let Some(n) = written {
106                     this.write_scalar(Scalar::from_u32(n), &written_place.into())?;
107                 }
108                 // Return whether this was a success.
109                 this.write_scalar(Scalar::from_i32(if written.is_some() { 1 } else { 0 }), dest)?;
110             }
111
112             // Allocation
113             "HeapAlloc" => {
114                 let &[ref handle, ref flags, ref size] =
115                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
116                 this.read_scalar(handle)?.to_machine_isize(this)?;
117                 let flags = this.read_scalar(flags)?.to_u32()?;
118                 let size = this.read_scalar(size)?.to_machine_usize(this)?;
119                 let zero_init = (flags & 0x00000008) != 0; // HEAP_ZERO_MEMORY
120                 let res = this.malloc(size, zero_init, MiriMemoryKind::WinHeap);
121                 this.write_scalar(res, dest)?;
122             }
123             "HeapFree" => {
124                 let &[ref handle, ref flags, ref ptr] =
125                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
126                 this.read_scalar(handle)?.to_machine_isize(this)?;
127                 this.read_scalar(flags)?.to_u32()?;
128                 let ptr = this.read_scalar(ptr)?.check_init()?;
129                 this.free(ptr, MiriMemoryKind::WinHeap)?;
130                 this.write_scalar(Scalar::from_i32(1), dest)?;
131             }
132             "HeapReAlloc" => {
133                 let &[ref handle, ref flags, ref ptr, ref size] =
134                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
135                 this.read_scalar(handle)?.to_machine_isize(this)?;
136                 this.read_scalar(flags)?.to_u32()?;
137                 let ptr = this.read_scalar(ptr)?.check_init()?;
138                 let size = this.read_scalar(size)?.to_machine_usize(this)?;
139                 let res = this.realloc(ptr, size, MiriMemoryKind::WinHeap)?;
140                 this.write_scalar(res, dest)?;
141             }
142
143             // errno
144             "SetLastError" => {
145                 let &[ref error] =
146                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
147                 let error = this.read_scalar(error)?.check_init()?;
148                 this.set_last_error(error)?;
149             }
150             "GetLastError" => {
151                 let &[] =
152                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
153                 let last_error = this.get_last_error()?;
154                 this.write_scalar(last_error, dest)?;
155             }
156
157             // Querying system information
158             "GetSystemInfo" => {
159                 let &[ref system_info] =
160                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
161                 let system_info = this.deref_operand(system_info)?;
162                 // Initialize with `0`.
163                 this.memory.write_bytes(
164                     system_info.ptr,
165                     iter::repeat(0u8).take(system_info.layout.size.bytes() as usize),
166                 )?;
167                 // Set number of processors.
168                 let dword_size = Size::from_bytes(4);
169                 let num_cpus = this.mplace_field(&system_info, 6)?;
170                 this.write_scalar(Scalar::from_int(NUM_CPUS, dword_size), &num_cpus.into())?;
171             }
172
173             // Thread-local storage
174             "TlsAlloc" => {
175                 // This just creates a key; Windows does not natively support TLS destructors.
176
177                 // Create key and return it.
178                 let &[] =
179                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
180                 let key = this.machine.tls.create_tls_key(None, dest.layout.size)?;
181                 this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?;
182             }
183             "TlsGetValue" => {
184                 let &[ref key] =
185                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, 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 &[ref key, ref new_ptr] =
193                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
194                 let key = u128::from(this.read_scalar(key)?.to_u32()?);
195                 let active_thread = this.get_active_thread();
196                 let new_ptr = this.read_scalar(new_ptr)?.check_init()?;
197                 this.machine.tls.store_tls(key, active_thread, this.test_null(new_ptr)?)?;
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 &[] =
206                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
207                 this.write_scalar(
208                     this.machine.cmd_line.expect("machine must be initialized"),
209                     dest,
210                 )?;
211             }
212
213             // Time related shims
214             "GetSystemTimeAsFileTime" => {
215                 #[allow(non_snake_case)]
216                 let &[ref LPFILETIME] =
217                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
218                 this.GetSystemTimeAsFileTime(LPFILETIME)?;
219             }
220             "QueryPerformanceCounter" => {
221                 #[allow(non_snake_case)]
222                 let &[ref lpPerformanceCount] =
223                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
224                 let result = this.QueryPerformanceCounter(lpPerformanceCount)?;
225                 this.write_scalar(Scalar::from_i32(result), dest)?;
226             }
227             "QueryPerformanceFrequency" => {
228                 #[allow(non_snake_case)]
229                 let &[ref lpFrequency] =
230                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
231                 let result = this.QueryPerformanceFrequency(lpFrequency)?;
232                 this.write_scalar(Scalar::from_i32(result), dest)?;
233             }
234
235             // Synchronization primitives
236             "AcquireSRWLockExclusive" => {
237                 let &[ref ptr] =
238                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
239                 this.AcquireSRWLockExclusive(ptr)?;
240             }
241             "ReleaseSRWLockExclusive" => {
242                 let &[ref ptr] =
243                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
244                 this.ReleaseSRWLockExclusive(ptr)?;
245             }
246             "TryAcquireSRWLockExclusive" => {
247                 let &[ref ptr] =
248                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
249                 let ret = this.TryAcquireSRWLockExclusive(ptr)?;
250                 this.write_scalar(Scalar::from_u8(ret), dest)?;
251             }
252             "AcquireSRWLockShared" => {
253                 let &[ref ptr] =
254                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
255                 this.AcquireSRWLockShared(ptr)?;
256             }
257             "ReleaseSRWLockShared" => {
258                 let &[ref ptr] =
259                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
260                 this.ReleaseSRWLockShared(ptr)?;
261             }
262             "TryAcquireSRWLockShared" => {
263                 let &[ref ptr] =
264                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
265                 let ret = this.TryAcquireSRWLockShared(ptr)?;
266                 this.write_scalar(Scalar::from_u8(ret), dest)?;
267             }
268
269             // Dynamic symbol loading
270             "GetProcAddress" => {
271                 #[allow(non_snake_case)]
272                 let &[ref hModule, ref lpProcName] =
273                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
274                 this.read_scalar(hModule)?.to_machine_isize(this)?;
275                 let name = this.read_c_str(this.read_scalar(lpProcName)?.check_init()?)?;
276                 if let Some(dlsym) = Dlsym::from_str(name, &this.tcx.sess.target.os)? {
277                     let ptr = this.memory.create_fn_alloc(FnVal::Other(dlsym));
278                     this.write_scalar(Scalar::from(ptr), dest)?;
279                 } else {
280                     this.write_null(dest)?;
281                 }
282             }
283
284             // Miscellaneous
285             "SystemFunction036" => {
286                 // This is really 'RtlGenRandom'.
287                 let &[ref ptr, ref len] =
288                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
289                 let ptr = this.read_scalar(ptr)?.check_init()?;
290                 let len = this.read_scalar(len)?.to_u32()?;
291                 this.gen_random(ptr, len.into())?;
292                 this.write_scalar(Scalar::from_bool(true), dest)?;
293             }
294             "BCryptGenRandom" => {
295                 let &[ref algorithm, ref ptr, ref len, ref flags] =
296                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
297                 let algorithm = this.read_scalar(algorithm)?;
298                 let ptr = this.read_scalar(ptr)?.check_init()?;
299                 let len = this.read_scalar(len)?.to_u32()?;
300                 let flags = this.read_scalar(flags)?.to_u32()?;
301                 if flags != 2 {
302                     //      ^ BCRYPT_USE_SYSTEM_PREFERRED_RNG
303                     throw_unsup_format!(
304                         "BCryptGenRandom is supported only with the BCRYPT_USE_SYSTEM_PREFERRED_RNG flag"
305                     );
306                 }
307                 if algorithm.to_machine_usize(this)? != 0 {
308                     throw_unsup_format!(
309                         "BCryptGenRandom algorithm must be NULL when the flag is BCRYPT_USE_SYSTEM_PREFERRED_RNG"
310                     );
311                 }
312                 this.gen_random(ptr, len.into())?;
313                 this.write_null(dest)?; // STATUS_SUCCESS
314             }
315             "GetConsoleScreenBufferInfo" => {
316                 // `term` needs this, so we fake it.
317                 let &[ref console, ref buffer_info] =
318                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
319                 this.read_scalar(console)?.to_machine_isize(this)?;
320                 this.deref_operand(buffer_info)?;
321                 // Indicate an error.
322                 // FIXME: we should set last_error, but to what?
323                 this.write_null(dest)?;
324             }
325             "GetConsoleMode" => {
326                 // Windows "isatty" (in libtest) needs this, so we fake it.
327                 let &[ref console, ref mode] =
328                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
329                 this.read_scalar(console)?.to_machine_isize(this)?;
330                 this.deref_operand(mode)?;
331                 // Indicate an error.
332                 // FIXME: we should set last_error, but to what?
333                 this.write_null(dest)?;
334             }
335             "SwitchToThread" => {
336                 let &[] =
337                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
338                 // Note that once Miri supports concurrency, this will need to return a nonzero
339                 // value if this call does result in switching to another thread.
340                 this.write_null(dest)?;
341             }
342
343             // Better error for attempts to create a thread
344             "CreateThread" => {
345                 let &[_, _, _, _, _, _] =
346                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
347
348                 this.handle_unsupported("can't create threads on Windows")?;
349                 return Ok(EmulateByNameResult::AlreadyJumped);
350             }
351
352             // Incomplete shims that we "stub out" just to get pre-main initialization code to work.
353             // These shims are enabled only when the caller is in the standard library.
354             "GetProcessHeap" if this.frame_in_std() => {
355                 let &[] =
356                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
357                 // Just fake a HANDLE
358                 this.write_scalar(Scalar::from_machine_isize(1, this), dest)?;
359             }
360             "SetConsoleTextAttribute" if this.frame_in_std() => {
361                 #[allow(non_snake_case)]
362                 let &[ref _hConsoleOutput, ref _wAttribute] =
363                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
364                 // Pretend these does not exist / nothing happened, by returning zero.
365                 this.write_null(dest)?;
366             }
367             "AddVectoredExceptionHandler" if this.frame_in_std() => {
368                 #[allow(non_snake_case)]
369                 let &[ref _First, ref _Handler] =
370                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
371                 // Any non zero value works for the stdlib. This is just used for stack overflows anyway.
372                 this.write_scalar(Scalar::from_machine_usize(1, this), dest)?;
373             }
374             "SetThreadStackGuarantee" if this.frame_in_std() => {
375                 #[allow(non_snake_case)]
376                 let &[_StackSizeInBytes] =
377                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
378                 // Any non zero value works for the stdlib. This is just used for stack overflows anyway.
379                 this.write_scalar(Scalar::from_u32(1), dest)?;
380             }
381             | "InitializeCriticalSection"
382             | "EnterCriticalSection"
383             | "LeaveCriticalSection"
384             | "DeleteCriticalSection"
385                 if this.frame_in_std() =>
386             {
387                 #[allow(non_snake_case)]
388                 let &[ref _lpCriticalSection] =
389                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
390                 assert_eq!(
391                     this.get_total_thread_count(),
392                     1,
393                     "concurrency on Windows is not supported"
394                 );
395                 // Nothing to do, not even a return value.
396                 // (Windows locks are reentrant, and we have only 1 thread,
397                 // so not doing any futher checks here is at least not incorrect.)
398             }
399             "TryEnterCriticalSection" if this.frame_in_std() => {
400                 #[allow(non_snake_case)]
401                 let &[ref _lpCriticalSection] =
402                     this.check_shim(abi, Abi::System { unwind: false }, link_name_sym, args)?;
403                 assert_eq!(
404                     this.get_total_thread_count(),
405                     1,
406                     "concurrency on Windows is not supported"
407                 );
408                 // There is only one thread, so this always succeeds and returns TRUE.
409                 this.write_scalar(Scalar::from_i32(1), dest)?;
410             }
411
412             _ => return Ok(EmulateByNameResult::NotSupported),
413         }
414
415         Ok(EmulateByNameResult::NeedsJumping)
416     }
417 }