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