]> git.lizzy.rs Git - rust.git/blob - src/shims/windows/foreign_items.rs
Auto merge of #1739 - RalfJung:rustup, r=RalfJung
[rust.git] / src / shims / windows / foreign_items.rs
1 use std::iter;
2
3 use rustc_middle::mir;
4 use rustc_target::abi::Size;
5 use rustc_target::spec::abi::Abi;
6
7 use crate::*;
8 use helpers::{check_abi, check_arg_count};
9 use shims::windows::sync::EvalContextExt as _;
10
11 impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
12 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
13     fn emulate_foreign_item_by_name(
14         &mut self,
15         link_name: &str,
16         abi: Abi,
17         args: &[OpTy<'tcx, Tag>],
18         dest: &PlaceTy<'tcx, Tag>,
19         _ret: mir::BasicBlock,
20     ) -> InterpResult<'tcx, bool> {
21         let this = self.eval_context_mut();
22
23         check_abi(abi, Abi::System { unwind: false })?;
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] = check_arg_count(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] = check_arg_count(args)?;
39                 let result = this.SetEnvironmentVariableW(name, value)?;
40                 this.write_scalar(Scalar::from_i32(result), dest)?;
41             }
42             "GetEnvironmentStringsW" => {
43                 let &[] = check_arg_count(args)?;
44                 let result = this.GetEnvironmentStringsW()?;
45                 this.write_scalar(result, dest)?;
46             }
47             "FreeEnvironmentStringsW" => {
48                 let &[ref env_block] = check_arg_count(args)?;
49                 let result = this.FreeEnvironmentStringsW(env_block)?;
50                 this.write_scalar(Scalar::from_i32(result), dest)?;
51             }
52             "GetCurrentDirectoryW" => {
53                 let &[ref size, ref buf] = check_arg_count(args)?;
54                 let result = this.GetCurrentDirectoryW(size, buf)?;
55                 this.write_scalar(Scalar::from_u32(result), dest)?;
56             }
57             "SetCurrentDirectoryW" => {
58                 let &[ref path] = check_arg_count(args)?;
59                 let result = this.SetCurrentDirectoryW(path)?;
60                 this.write_scalar(Scalar::from_i32(result), dest)?;
61             }
62
63             // File related shims
64             "GetStdHandle" => {
65                 let &[ref which] = check_arg_count(args)?;
66                 let which = this.read_scalar(which)?.to_i32()?;
67                 // We just make this the identity function, so we know later in `WriteFile`
68                 // which one it is.
69                 this.write_scalar(Scalar::from_machine_isize(which.into(), this), dest)?;
70             }
71             "WriteFile" => {
72                 let &[ref handle, ref buf, ref n, ref written_ptr, ref overlapped] = check_arg_count(args)?;
73                 this.read_scalar(overlapped)?.to_machine_usize(this)?; // this is a poiner, that we ignore
74                 let handle = this.read_scalar(handle)?.to_machine_isize(this)?;
75                 let buf = this.read_scalar(buf)?.check_init()?;
76                 let n = this.read_scalar(n)?.to_u32()?;
77                 let written_place = this.deref_operand(written_ptr)?;
78                 // Spec says to always write `0` first.
79                 this.write_null(&written_place.into())?;
80                 let written = if handle == -11 || handle == -12 {
81                     // stdout/stderr
82                     use std::io::{self, Write};
83
84                     let buf_cont = this.memory.read_bytes(buf, Size::from_bytes(u64::from(n)))?;
85                     let res = if handle == -11 {
86                         io::stdout().write(buf_cont)
87                     } else {
88                         io::stderr().write(buf_cont)
89                     };
90                     res.ok().map(|n| n as u32)
91                 } else {
92                     throw_unsup_format!("on Windows, writing to anything except stdout/stderr is not supported")
93                 };
94                 // If there was no error, write back how much was written.
95                 if let Some(n) = written {
96                     this.write_scalar(Scalar::from_u32(n), &written_place.into())?;
97                 }
98                 // Return whether this was a success.
99                 this.write_scalar(
100                     Scalar::from_i32(if written.is_some() { 1 } else { 0 }),
101                     dest,
102                 )?;
103             }
104
105             // Allocation
106             "HeapAlloc" => {
107                 let &[ref handle, ref flags, ref size] = check_arg_count(args)?;
108                 this.read_scalar(handle)?.to_machine_isize(this)?;
109                 let flags = this.read_scalar(flags)?.to_u32()?;
110                 let size = this.read_scalar(size)?.to_machine_usize(this)?;
111                 let zero_init = (flags & 0x00000008) != 0; // HEAP_ZERO_MEMORY
112                 let res = this.malloc(size, zero_init, MiriMemoryKind::WinHeap);
113                 this.write_scalar(res, dest)?;
114             }
115             "HeapFree" => {
116                 let &[ref handle, ref flags, ref ptr] = check_arg_count(args)?;
117                 this.read_scalar(handle)?.to_machine_isize(this)?;
118                 this.read_scalar(flags)?.to_u32()?;
119                 let ptr = this.read_scalar(ptr)?.check_init()?;
120                 this.free(ptr, MiriMemoryKind::WinHeap)?;
121                 this.write_scalar(Scalar::from_i32(1), dest)?;
122             }
123             "HeapReAlloc" => {
124                 let &[ref handle, ref flags, ref ptr, ref size] = check_arg_count(args)?;
125                 this.read_scalar(handle)?.to_machine_isize(this)?;
126                 this.read_scalar(flags)?.to_u32()?;
127                 let ptr = this.read_scalar(ptr)?.check_init()?;
128                 let size = this.read_scalar(size)?.to_machine_usize(this)?;
129                 let res = this.realloc(ptr, size, MiriMemoryKind::WinHeap)?;
130                 this.write_scalar(res, dest)?;
131             }
132
133             // errno
134             "SetLastError" => {
135                 let &[ref error] = check_arg_count(args)?;
136                 let error = this.read_scalar(error)?.check_init()?;
137                 this.set_last_error(error)?;
138             }
139             "GetLastError" => {
140                 let &[] = check_arg_count(args)?;
141                 let last_error = this.get_last_error()?;
142                 this.write_scalar(last_error, dest)?;
143             }
144
145             // Querying system information
146             "GetSystemInfo" => {
147                 let &[ref system_info] = check_arg_count(args)?;
148                 let system_info = this.deref_operand(system_info)?;
149                 // Initialize with `0`.
150                 this.memory.write_bytes(
151                     system_info.ptr,
152                     iter::repeat(0u8).take(system_info.layout.size.bytes() as usize),
153                 )?;
154                 // Set number of processors.
155                 let dword_size = Size::from_bytes(4);
156                 let num_cpus = this.mplace_field(&system_info, 6)?;
157                 this.write_scalar(Scalar::from_int(NUM_CPUS, dword_size), &num_cpus.into())?;
158             }
159
160             // Thread-local storage
161             "TlsAlloc" => {
162                 // This just creates a key; Windows does not natively support TLS destructors.
163
164                 // Create key and return it.
165                 let &[] = check_arg_count(args)?;
166                 let key = this.machine.tls.create_tls_key(None, dest.layout.size)?;
167                 this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?;
168             }
169             "TlsGetValue" => {
170                 let &[ref key] = check_arg_count(args)?;
171                 let key = u128::from(this.read_scalar(key)?.to_u32()?);
172                 let active_thread = this.get_active_thread();
173                 let ptr = this.machine.tls.load_tls(key, active_thread, this)?;
174                 this.write_scalar(ptr, dest)?;
175             }
176             "TlsSetValue" => {
177                 let &[ref key, ref new_ptr] = check_arg_count(args)?;
178                 let key = u128::from(this.read_scalar(key)?.to_u32()?);
179                 let active_thread = this.get_active_thread();
180                 let new_ptr = this.read_scalar(new_ptr)?.check_init()?;
181                 this.machine.tls.store_tls(key, active_thread, this.test_null(new_ptr)?)?;
182
183                 // Return success (`1`).
184                 this.write_scalar(Scalar::from_i32(1), dest)?;
185             }
186
187             // Access to command-line arguments
188             "GetCommandLineW" => {
189                 let &[] = check_arg_count(args)?;
190                 this.write_scalar(
191                     this.machine.cmd_line.expect("machine must be initialized"),
192                     dest,
193                 )?;
194             }
195
196             // Time related shims
197             "GetSystemTimeAsFileTime" => {
198                 #[allow(non_snake_case)]
199                 let &[ref LPFILETIME] = check_arg_count(args)?;
200                 this.GetSystemTimeAsFileTime(LPFILETIME)?;
201             }
202             "QueryPerformanceCounter" => {
203                 #[allow(non_snake_case)]
204                 let &[ref lpPerformanceCount] = check_arg_count(args)?;
205                 let result = this.QueryPerformanceCounter(lpPerformanceCount)?;
206                 this.write_scalar(Scalar::from_i32(result), dest)?;
207             }
208             "QueryPerformanceFrequency" => {
209                 #[allow(non_snake_case)]
210                 let &[ref lpFrequency] = check_arg_count(args)?;
211                 let result = this.QueryPerformanceFrequency(lpFrequency)?;
212                 this.write_scalar(Scalar::from_i32(result), dest)?;
213             }
214
215             // Synchronization primitives
216             "AcquireSRWLockExclusive" => {
217                 let &[ref ptr] = check_arg_count(args)?;
218                 this.AcquireSRWLockExclusive(ptr)?;
219             }
220             "ReleaseSRWLockExclusive" => {
221                 let &[ref ptr] = check_arg_count(args)?;
222                 this.ReleaseSRWLockExclusive(ptr)?;
223             }
224             "TryAcquireSRWLockExclusive" => {
225                 let &[ref ptr] = check_arg_count(args)?;
226                 let ret = this.TryAcquireSRWLockExclusive(ptr)?;
227                 this.write_scalar(Scalar::from_u8(ret), dest)?;
228             }
229             "AcquireSRWLockShared" => {
230                 let &[ref ptr] = check_arg_count(args)?;
231                 this.AcquireSRWLockShared(ptr)?;
232             }
233             "ReleaseSRWLockShared" => {
234                 let &[ref ptr] = check_arg_count(args)?;
235                 this.ReleaseSRWLockShared(ptr)?;
236             }
237             "TryAcquireSRWLockShared" => {
238                 let &[ref ptr] = check_arg_count(args)?;
239                 let ret = this.TryAcquireSRWLockShared(ptr)?;
240                 this.write_scalar(Scalar::from_u8(ret), dest)?;
241             }
242
243             // Dynamic symbol loading
244             "GetProcAddress" => {
245                 #[allow(non_snake_case)]
246                 let &[ref hModule, ref lpProcName] = check_arg_count(args)?;
247                 this.read_scalar(hModule)?.to_machine_isize(this)?;
248                 let name = this.memory.read_c_str(this.read_scalar(lpProcName)?.check_init()?)?;
249                 if let Some(dlsym) = Dlsym::from_str(name, &this.tcx.sess.target.os)? {
250                     let ptr = this.memory.create_fn_alloc(FnVal::Other(dlsym));
251                     this.write_scalar(Scalar::from(ptr), dest)?;
252                 } else {
253                     this.write_null(dest)?;
254                 }
255             }
256
257             // Miscellaneous
258             "SystemFunction036" => {
259                 // The actual name of 'RtlGenRandom'
260                 let &[ref ptr, ref len] = check_arg_count(args)?;
261                 let ptr = this.read_scalar(ptr)?.check_init()?;
262                 let len = this.read_scalar(len)?.to_u32()?;
263                 this.gen_random(ptr, len.into())?;
264                 this.write_scalar(Scalar::from_bool(true), dest)?;
265             }
266             "GetConsoleScreenBufferInfo" => {
267                 // `term` needs this, so we fake it.
268                 let &[ref console, ref buffer_info] = check_arg_count(args)?;
269                 this.read_scalar(console)?.to_machine_isize(this)?;
270                 this.deref_operand(buffer_info)?;
271                 // Indicate an error.
272                 // FIXME: we should set last_error, but to what?
273                 this.write_null(dest)?;
274             }
275             "GetConsoleMode" => {
276                 // Windows "isatty" (in libtest) needs this, so we fake it.
277                 let &[ref console, ref mode] = check_arg_count(args)?;
278                 this.read_scalar(console)?.to_machine_isize(this)?;
279                 this.deref_operand(mode)?;
280                 // Indicate an error.
281                 // FIXME: we should set last_error, but to what?
282                 this.write_null(dest)?;
283             }
284             "SwitchToThread" => {
285                 let &[] = check_arg_count(args)?;
286                 // Note that once Miri supports concurrency, this will need to return a nonzero
287                 // value if this call does result in switching to another thread.
288                 this.write_null(dest)?;
289             }
290
291             // Better error for attempts to create a thread
292             "CreateThread" => {
293                 throw_unsup_format!("Miri does not support concurrency on Windows");
294             }
295
296             // Incomplete shims that we "stub out" just to get pre-main initialization code to work.
297             // These shims are enabled only when the caller is in the standard library.
298             "GetProcessHeap" if this.frame().instance.to_string().starts_with("std::sys::windows::") => {
299                 let &[] = check_arg_count(args)?;
300                 // Just fake a HANDLE
301                 this.write_scalar(Scalar::from_machine_isize(1, this), dest)?;
302             }
303             "SetConsoleTextAttribute" if this.frame().instance.to_string().starts_with("std::sys::windows::") => {
304                 #[allow(non_snake_case)]
305                 let &[ref _hConsoleOutput, ref _wAttribute] = check_arg_count(args)?;
306                 // Pretend these does not exist / nothing happened, by returning zero.
307                 this.write_null(dest)?;
308             }
309             "AddVectoredExceptionHandler" if this.frame().instance.to_string().starts_with("std::sys::windows::") => {
310                 #[allow(non_snake_case)]
311                 let &[ref _First, ref _Handler] = check_arg_count(args)?;
312                 // Any non zero value works for the stdlib. This is just used for stack overflows anyway.
313                 this.write_scalar(Scalar::from_machine_usize(1, this), dest)?;
314             }
315             "SetThreadStackGuarantee" if this.frame().instance.to_string().starts_with("std::sys::windows::") => {
316                 #[allow(non_snake_case)]
317                 let &[_StackSizeInBytes] = check_arg_count(args)?;
318                 // Any non zero value works for the stdlib. This is just used for stack overflows anyway.
319                 this.write_scalar(Scalar::from_u32(1), dest)?;
320             }
321             | "InitializeCriticalSection"
322             | "EnterCriticalSection"
323             | "LeaveCriticalSection"
324             | "DeleteCriticalSection"
325             if this.frame().instance.to_string().starts_with("std::sys::windows::") => {
326                 #[allow(non_snake_case)]
327                 let &[ref _lpCriticalSection] = check_arg_count(args)?;
328                 assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows is not supported");
329                 // Nothing to do, not even a return value.
330                 // (Windows locks are reentrant, and we have only 1 thread,
331                 // so not doing any futher checks here is at least not incorrect.)
332             }
333             "TryEnterCriticalSection"
334             if this.frame().instance.to_string().starts_with("std::sys::windows::") => {
335                 #[allow(non_snake_case)]
336                 let &[ref _lpCriticalSection] = check_arg_count(args)?;
337                 assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows is not supported");
338                 // There is only one thread, so this always succeeds and returns TRUE.
339                 this.write_scalar(Scalar::from_i32(1), dest)?;
340             }
341
342             _ => throw_unsup_format!("can't call foreign function: {}", link_name),
343         }
344
345         Ok(true)
346     }
347 }
348