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