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