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