]> git.lizzy.rs Git - rust.git/blob - src/fn_call.rs
Update to rustc nightly
[rust.git] / src / fn_call.rs
1 use rustc::ty;
2 use rustc::ty::layout::{Align, LayoutOf, Size};
3 use rustc::hir::def_id::DefId;
4 use rustc::mir;
5 use syntax::attr;
6
7 use crate::*;
8
9 impl<'a, 'mir, 'tcx> EvalContextExt<'a, 'mir, 'tcx> for crate::MiriEvalContext<'a, 'mir, 'tcx> {}
10 pub trait EvalContextExt<'a, 'mir, 'tcx: 'a + 'mir>: crate::MiriEvalContextExt<'a, 'mir, 'tcx> {
11     fn find_fn(
12         &mut self,
13         instance: ty::Instance<'tcx>,
14         args: &[OpTy<'tcx, Borrow>],
15         dest: Option<PlaceTy<'tcx, Borrow>>,
16         ret: Option<mir::BasicBlock>,
17     ) -> EvalResult<'tcx, Option<&'mir mir::Mir<'tcx>>> {
18         let this = self.eval_context_mut();
19         trace!("eval_fn_call: {:#?}, {:?}", instance, dest.map(|place| *place));
20
21         // First, run the common hooks also supported by CTFE.
22         if this.hook_fn(instance, args, dest)? {
23             this.goto_block(ret)?;
24             return Ok(None);
25         }
26         // There are some more lang items we want to hook that CTFE does not hook (yet).
27         if this.tcx.lang_items().align_offset_fn() == Some(instance.def.def_id()) {
28             // FIXME: return a real value in case the target allocation has an
29             // alignment bigger than the one requested.
30             let n = u128::max_value();
31             let dest = dest.unwrap();
32             let n = this.truncate(n, dest.layout);
33             this.write_scalar(Scalar::from_uint(n, dest.layout.size), dest)?;
34             this.goto_block(ret)?;
35             return Ok(None);
36         }
37
38         // Try to see if we can do something about foreign items.
39         if this.tcx.is_foreign_item(instance.def_id()) {
40             // An external function that we cannot find MIR for, but we can still run enough
41             // of them to make miri viable.
42             this.emulate_foreign_item(instance.def_id(), args, dest, ret)?;
43             // `goto_block` already handled.
44             return Ok(None);
45         }
46
47         // Otherwise, load the MIR.
48         Ok(Some(this.load_mir(instance.def)?))
49     }
50
51     /// Emulates calling a foreign item, failing if the item is not supported.
52     /// This function will handle `goto_block` if needed.
53     fn emulate_foreign_item(
54         &mut self,
55         def_id: DefId,
56         args: &[OpTy<'tcx, Borrow>],
57         dest: Option<PlaceTy<'tcx, Borrow>>,
58         ret: Option<mir::BasicBlock>,
59     ) -> EvalResult<'tcx> {
60         let this = self.eval_context_mut();
61         let attrs = this.tcx.get_attrs(def_id);
62         let link_name = match attr::first_attr_value_str_by_name(&attrs, "link_name") {
63             Some(name) => name.as_str().get(),
64             None => this.tcx.item_name(def_id).as_str().get(),
65         };
66         // Strip linker suffixes (seen on 32-bit macOS).
67         let link_name = link_name.trim_end_matches("$UNIX2003");
68         let tcx = &{this.tcx.tcx};
69
70         // First: functions that could diverge.
71         match link_name {
72             "__rust_start_panic" | "panic_impl" => {
73                 return err!(MachineError("the evaluated program panicked".to_string()));
74             }
75             _ => if dest.is_none() {
76                 return err!(Unimplemented(
77                     format!("can't call diverging foreign function: {}", link_name),
78                 ));
79             }
80         }
81
82         // Next: functions that assume a ret and dest.
83         let dest = dest.expect("we already checked for a dest");
84         let ret = ret.expect("dest is `Some` but ret is `None`");
85         match link_name {
86             "malloc" => {
87                 let size = this.read_scalar(args[0])?.to_usize(this)?;
88                 if size == 0 {
89                     this.write_null(dest)?;
90                 } else {
91                     let align = this.tcx.data_layout.pointer_align.abi;
92                     let ptr = this.memory_mut().allocate(Size::from_bytes(size), align, MiriMemoryKind::C.into());
93                     this.write_scalar(Scalar::Ptr(ptr.with_default_tag()), dest)?;
94                 }
95             }
96             "posix_memalign" => {
97                 let ret = this.deref_operand(args[0])?;
98                 let align = this.read_scalar(args[1])?.to_usize(this)?;
99                 let size = this.read_scalar(args[2])?.to_usize(this)?;
100                 // Align must be power of 2, and also at least ptr-sized (POSIX rules).
101                 if !align.is_power_of_two() {
102                     return err!(HeapAllocNonPowerOfTwoAlignment(align));
103                 }
104                 if align < this.pointer_size().bytes() {
105                     return err!(MachineError(format!(
106                         "posix_memalign: alignment must be at least the size of a pointer, but is {}",
107                         align,
108                     )));
109                 }
110                 if size == 0 {
111                     this.write_null(ret.into())?;
112                 } else {
113                     let ptr = this.memory_mut().allocate(
114                         Size::from_bytes(size),
115                         Align::from_bytes(align).unwrap(),
116                         MiriMemoryKind::C.into()
117                     );
118                     this.write_scalar(Scalar::Ptr(ptr.with_default_tag()), ret.into())?;
119                 }
120                 this.write_null(dest)?;
121             }
122
123             "free" => {
124                 let ptr = this.read_scalar(args[0])?.not_undef()?;
125                 if !ptr.is_null_ptr(this) {
126                     this.memory_mut().deallocate(
127                         ptr.to_ptr()?,
128                         None,
129                         MiriMemoryKind::C.into(),
130                     )?;
131                 }
132             }
133
134             "__rust_alloc" => {
135                 let size = this.read_scalar(args[0])?.to_usize(this)?;
136                 let align = this.read_scalar(args[1])?.to_usize(this)?;
137                 if size == 0 {
138                     return err!(HeapAllocZeroBytes);
139                 }
140                 if !align.is_power_of_two() {
141                     return err!(HeapAllocNonPowerOfTwoAlignment(align));
142                 }
143                 let ptr = this.memory_mut()
144                     .allocate(
145                         Size::from_bytes(size),
146                         Align::from_bytes(align).unwrap(),
147                         MiriMemoryKind::Rust.into()
148                     )
149                     .with_default_tag();
150                 this.write_scalar(Scalar::Ptr(ptr), dest)?;
151             }
152             "__rust_alloc_zeroed" => {
153                 let size = this.read_scalar(args[0])?.to_usize(this)?;
154                 let align = this.read_scalar(args[1])?.to_usize(this)?;
155                 if size == 0 {
156                     return err!(HeapAllocZeroBytes);
157                 }
158                 if !align.is_power_of_two() {
159                     return err!(HeapAllocNonPowerOfTwoAlignment(align));
160                 }
161                 let ptr = this.memory_mut()
162                     .allocate(
163                         Size::from_bytes(size),
164                         Align::from_bytes(align).unwrap(),
165                         MiriMemoryKind::Rust.into()
166                     )
167                     .with_default_tag();
168                 this.memory_mut()
169                     .get_mut(ptr.alloc_id)?
170                     .write_repeat(tcx, ptr, 0, Size::from_bytes(size))?;
171                 this.write_scalar(Scalar::Ptr(ptr), dest)?;
172             }
173             "__rust_dealloc" => {
174                 let ptr = this.read_scalar(args[0])?.to_ptr()?;
175                 let old_size = this.read_scalar(args[1])?.to_usize(this)?;
176                 let align = this.read_scalar(args[2])?.to_usize(this)?;
177                 if old_size == 0 {
178                     return err!(HeapAllocZeroBytes);
179                 }
180                 if !align.is_power_of_two() {
181                     return err!(HeapAllocNonPowerOfTwoAlignment(align));
182                 }
183                 this.memory_mut().deallocate(
184                     ptr,
185                     Some((Size::from_bytes(old_size), Align::from_bytes(align).unwrap())),
186                     MiriMemoryKind::Rust.into(),
187                 )?;
188             }
189             "__rust_realloc" => {
190                 let ptr = this.read_scalar(args[0])?.to_ptr()?;
191                 let old_size = this.read_scalar(args[1])?.to_usize(this)?;
192                 let align = this.read_scalar(args[2])?.to_usize(this)?;
193                 let new_size = this.read_scalar(args[3])?.to_usize(this)?;
194                 if old_size == 0 || new_size == 0 {
195                     return err!(HeapAllocZeroBytes);
196                 }
197                 if !align.is_power_of_two() {
198                     return err!(HeapAllocNonPowerOfTwoAlignment(align));
199                 }
200                 let new_ptr = this.memory_mut().reallocate(
201                     ptr,
202                     Size::from_bytes(old_size),
203                     Align::from_bytes(align).unwrap(),
204                     Size::from_bytes(new_size),
205                     Align::from_bytes(align).unwrap(),
206                     MiriMemoryKind::Rust.into(),
207                 )?;
208                 this.write_scalar(Scalar::Ptr(new_ptr.with_default_tag()), dest)?;
209             }
210
211             "syscall" => {
212                 // TODO: read `syscall` IDs like `sysconf` IDs and
213                 // figure out some way to actually process some of them.
214                 //
215                 // `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)`
216                 // is called if a `HashMap` is created the regular way.
217                 match this.read_scalar(args[0])?.to_usize(this)? {
218                     318 | 511 => {
219                         return err!(Unimplemented(
220                             "miri does not support random number generators".to_owned(),
221                         ))
222                     }
223                     id => {
224                         return err!(Unimplemented(
225                             format!("miri does not support syscall ID {}", id),
226                         ))
227                     }
228                 }
229             }
230
231             "dlsym" => {
232                 let _handle = this.read_scalar(args[0])?;
233                 let symbol = this.read_scalar(args[1])?.to_ptr()?;
234                 let symbol_name = this.memory().get(symbol.alloc_id)?.read_c_str(tcx, symbol)?;
235                 let err = format!("bad c unicode symbol: {:?}", symbol_name);
236                 let symbol_name = ::std::str::from_utf8(symbol_name).unwrap_or(&err);
237                 return err!(Unimplemented(format!(
238                     "miri does not support dynamically loading libraries (requested symbol: {})",
239                     symbol_name
240                 )));
241             }
242
243             "__rust_maybe_catch_panic" => {
244                 // fn __rust_maybe_catch_panic(
245                 //     f: fn(*mut u8),
246                 //     data: *mut u8,
247                 //     data_ptr: *mut usize,
248                 //     vtable_ptr: *mut usize,
249                 // ) -> u32
250                 // We abort on panic, so not much is going on here, but we still have to call the closure.
251                 let f = this.read_scalar(args[0])?.to_ptr()?;
252                 let data = this.read_scalar(args[1])?.not_undef()?;
253                 let f_instance = this.memory().get_fn(f)?;
254                 this.write_null(dest)?;
255                 trace!("__rust_maybe_catch_panic: {:?}", f_instance);
256
257                 // Now we make a function call.
258                 // TODO: consider making this reusable? `InterpretCx::step` does something similar
259                 // for the TLS destructors, and of course `eval_main`.
260                 let mir = this.load_mir(f_instance.def)?;
261                 let ret_place = MPlaceTy::dangling(this.layout_of(this.tcx.mk_unit())?, this).into();
262                 this.push_stack_frame(
263                     f_instance,
264                     mir.span,
265                     mir,
266                     Some(ret_place),
267                     // Directly return to caller.
268                     StackPopCleanup::Goto(Some(ret)),
269                 )?;
270                 let mut args = this.frame().mir.args_iter();
271
272                 let arg_local = args.next().ok_or_else(||
273                     InterpError::AbiViolation(
274                         "Argument to __rust_maybe_catch_panic does not take enough arguments."
275                             .to_owned(),
276                     ),
277                 )?;
278                 let arg_dest = this.eval_place(&mir::Place::Base(mir::PlaceBase::Local(arg_local)))?;
279                 this.write_scalar(data, arg_dest)?;
280
281                 assert!(args.next().is_none(), "__rust_maybe_catch_panic argument has more arguments than expected");
282
283                 // We ourselves will return `0`, eventually (because we will not return if we paniced).
284                 this.write_null(dest)?;
285
286                 // Don't fall through, we do *not* want to `goto_block`!
287                 return Ok(());
288             }
289
290             "memcmp" => {
291                 let left = this.read_scalar(args[0])?.not_undef()?;
292                 let right = this.read_scalar(args[1])?.not_undef()?;
293                 let n = Size::from_bytes(this.read_scalar(args[2])?.to_usize(this)?);
294
295                 let result = {
296                     let left_bytes = this.memory().read_bytes(left, n)?;
297                     let right_bytes = this.memory().read_bytes(right, n)?;
298
299                     use std::cmp::Ordering::*;
300                     match left_bytes.cmp(right_bytes) {
301                         Less => -1i32,
302                         Equal => 0,
303                         Greater => 1,
304                     }
305                 };
306
307                 this.write_scalar(
308                     Scalar::from_int(result, Size::from_bits(32)),
309                     dest,
310                 )?;
311             }
312
313             "memrchr" => {
314                 let ptr = this.read_scalar(args[0])?.not_undef()?;
315                 let val = this.read_scalar(args[1])?.to_i32()? as u8;
316                 let num = this.read_scalar(args[2])?.to_usize(this)?;
317                 if let Some(idx) = this.memory().read_bytes(ptr, Size::from_bytes(num))?
318                     .iter().rev().position(|&c| c == val)
319                 {
320                     let new_ptr = ptr.ptr_offset(Size::from_bytes(num - idx as u64 - 1), this)?;
321                     this.write_scalar(new_ptr, dest)?;
322                 } else {
323                     this.write_null(dest)?;
324                 }
325             }
326
327             "memchr" => {
328                 let ptr = this.read_scalar(args[0])?.not_undef()?;
329                 let val = this.read_scalar(args[1])?.to_i32()? as u8;
330                 let num = this.read_scalar(args[2])?.to_usize(this)?;
331                 let idx = this
332                     .memory()
333                     .read_bytes(ptr, Size::from_bytes(num))?
334                     .iter()
335                     .position(|&c| c == val);
336                 if let Some(idx) = idx {
337                     let new_ptr = ptr.ptr_offset(Size::from_bytes(idx as u64), this)?;
338                     this.write_scalar(new_ptr, dest)?;
339                 } else {
340                     this.write_null(dest)?;
341                 }
342             }
343
344             "getenv" => {
345                 let result = {
346                     let name_ptr = this.read_scalar(args[0])?.to_ptr()?;
347                     let name = this.memory().get(name_ptr.alloc_id)?.read_c_str(tcx, name_ptr)?;
348                     match this.machine.env_vars.get(name) {
349                         Some(&var) => Scalar::Ptr(var),
350                         None => Scalar::ptr_null(&*this.tcx),
351                     }
352                 };
353                 this.write_scalar(result, dest)?;
354             }
355
356             "unsetenv" => {
357                 let mut success = None;
358                 {
359                     let name_ptr = this.read_scalar(args[0])?.not_undef()?;
360                     if !name_ptr.is_null_ptr(this) {
361                         let name_ptr = name_ptr.to_ptr()?;
362                         let name = this
363                             .memory()
364                             .get(name_ptr.alloc_id)?
365                             .read_c_str(tcx, name_ptr)?
366                             .to_owned();
367                         if !name.is_empty() && !name.contains(&b'=') {
368                             success = Some(this.machine.env_vars.remove(&name));
369                         }
370                     }
371                 }
372                 if let Some(old) = success {
373                     if let Some(var) = old {
374                         this.memory_mut().deallocate(var, None, MiriMemoryKind::Env.into())?;
375                     }
376                     this.write_null(dest)?;
377                 } else {
378                     this.write_scalar(Scalar::from_int(-1, dest.layout.size), dest)?;
379                 }
380             }
381
382             "setenv" => {
383                 let mut new = None;
384                 {
385                     let name_ptr = this.read_scalar(args[0])?.not_undef()?;
386                     let value_ptr = this.read_scalar(args[1])?.to_ptr()?;
387                     let value = this.memory().get(value_ptr.alloc_id)?.read_c_str(tcx, value_ptr)?;
388                     if !name_ptr.is_null_ptr(this) {
389                         let name_ptr = name_ptr.to_ptr()?;
390                         let name = this.memory().get(name_ptr.alloc_id)?.read_c_str(tcx, name_ptr)?;
391                         if !name.is_empty() && !name.contains(&b'=') {
392                             new = Some((name.to_owned(), value.to_owned()));
393                         }
394                     }
395                 }
396                 if let Some((name, value)) = new {
397                     // `+1` for the null terminator.
398                     let value_copy = this.memory_mut().allocate(
399                         Size::from_bytes((value.len() + 1) as u64),
400                         Align::from_bytes(1).unwrap(),
401                         MiriMemoryKind::Env.into(),
402                     ).with_default_tag();
403                     {
404                         let alloc = this.memory_mut().get_mut(value_copy.alloc_id)?;
405                         alloc.write_bytes(tcx, value_copy, &value)?;
406                         let trailing_zero_ptr = value_copy.offset(
407                             Size::from_bytes(value.len() as u64),
408                             tcx,
409                         )?;
410                         alloc.write_bytes(tcx, trailing_zero_ptr, &[0])?;
411                     }
412                     if let Some(var) = this.machine.env_vars.insert(
413                         name.to_owned(),
414                         value_copy,
415                     )
416                     {
417                         this.memory_mut().deallocate(var, None, MiriMemoryKind::Env.into())?;
418                     }
419                     this.write_null(dest)?;
420                 } else {
421                     this.write_scalar(Scalar::from_int(-1, dest.layout.size), dest)?;
422                 }
423             }
424
425             "write" => {
426                 let fd = this.read_scalar(args[0])?.to_i32()?;
427                 let buf = this.read_scalar(args[1])?.not_undef()?;
428                 let n = this.read_scalar(args[2])?.to_usize(&*this.tcx)?;
429                 trace!("Called write({:?}, {:?}, {:?})", fd, buf, n);
430                 let result = if fd == 1 || fd == 2 {
431                     // stdout/stderr
432                     use std::io::{self, Write};
433
434                     let buf_cont = this.memory().read_bytes(buf, Size::from_bytes(n))?;
435                     // We need to flush to make sure this actually appears on the screen
436                     let res = if fd == 1 {
437                         // Stdout is buffered, flush to make sure it appears on the screen.
438                         // This is the write() syscall of the interpreted program, we want it
439                         // to correspond to a write() syscall on the host -- there is no good
440                         // in adding extra buffering here.
441                         let res = io::stdout().write(buf_cont);
442                         io::stdout().flush().unwrap();
443                         res
444                     } else {
445                         // No need to flush, stderr is not buffered.
446                         io::stderr().write(buf_cont)
447                     };
448                     match res {
449                         Ok(n) => n as i64,
450                         Err(_) => -1,
451                     }
452                 } else {
453                     eprintln!("Miri: Ignored output to FD {}", fd);
454                     // Pretend it all went well.
455                     n as i64
456                 };
457                 // Now, `result` is the value we return back to the program.
458                 this.write_scalar(
459                     Scalar::from_int(result, dest.layout.size),
460                     dest,
461                 )?;
462             }
463
464             "strlen" => {
465                 let ptr = this.read_scalar(args[0])?.to_ptr()?;
466                 let n = this.memory().get(ptr.alloc_id)?.read_c_str(tcx, ptr)?.len();
467                 this.write_scalar(Scalar::from_uint(n as u64, dest.layout.size), dest)?;
468             }
469
470             // Some things needed for `sys::thread` initialization to go through.
471             "signal" | "sigaction" | "sigaltstack" => {
472                 this.write_scalar(Scalar::from_int(0, dest.layout.size), dest)?;
473             }
474
475             "sysconf" => {
476                 let name = this.read_scalar(args[0])?.to_i32()?;
477
478                 trace!("sysconf() called with name {}", name);
479                 // Cache the sysconf integers via Miri's global cache.
480                 let paths = &[
481                     (&["libc", "_SC_PAGESIZE"], Scalar::from_int(4096, dest.layout.size)),
482                     (&["libc", "_SC_GETPW_R_SIZE_MAX"], Scalar::from_int(-1, dest.layout.size)),
483                     (&["libc", "_SC_NPROCESSORS_ONLN"], Scalar::from_int(1, dest.layout.size)),
484                 ];
485                 let mut result = None;
486                 for &(path, path_value) in paths {
487                     if let Ok(instance) = this.resolve_path(path) {
488                         let cid = GlobalId {
489                             instance,
490                             promoted: None,
491                         };
492                         let const_val = this.const_eval_raw(cid)?;
493                         let const_val = this.read_scalar(const_val.into())?;
494                         let value = const_val.to_i32()?;
495                         if value == name {
496                             result = Some(path_value);
497                             break;
498                         }
499                     }
500                 }
501                 if let Some(result) = result {
502                     this.write_scalar(result, dest)?;
503                 } else {
504                     return err!(Unimplemented(
505                         format!("Unimplemented sysconf name: {}", name),
506                     ));
507                 }
508             }
509
510             "isatty" => {
511                 this.write_null(dest)?;
512             }
513
514             // Hook pthread calls that go to the thread-local storage memory subsystem.
515             "pthread_key_create" => {
516                 let key_ptr = this.read_scalar(args[0])?.to_ptr()?;
517
518                 // Extract the function type out of the signature (that seems easier than constructing it ourselves).
519                 let dtor = match this.read_scalar(args[1])?.not_undef()? {
520                     Scalar::Ptr(dtor_ptr) => Some(this.memory().get_fn(dtor_ptr)?),
521                     Scalar::Bits { bits: 0, size } => {
522                         assert_eq!(size as u64, this.memory().pointer_size().bytes());
523                         None
524                     },
525                     Scalar::Bits { .. } => return err!(ReadBytesAsPointer),
526                 };
527
528                 // Figure out how large a pthread TLS key actually is.
529                 // This is `libc::pthread_key_t`.
530                 let key_type = args[0].layout.ty
531                     .builtin_deref(true)
532                     .ok_or_else(|| InterpError::AbiViolation("wrong signature used for `pthread_key_create`: first argument must be a raw pointer.".to_owned()))?
533                     .ty;
534                 let key_layout = this.layout_of(key_type)?;
535
536                 // Create key and write it into the memory where `key_ptr` wants it.
537                 let key = this.machine.tls.create_tls_key(dtor, tcx) as u128;
538                 if key_layout.size.bits() < 128 && key >= (1u128 << key_layout.size.bits() as u128) {
539                     return err!(OutOfTls);
540                 }
541
542                 this.memory().check_align(key_ptr.into(), key_layout.align.abi)?;
543                 this.memory_mut().get_mut(key_ptr.alloc_id)?.write_scalar(
544                     tcx,
545                     key_ptr,
546                     Scalar::from_uint(key, key_layout.size).into(),
547                     key_layout.size,
548                 )?;
549
550                 // Return success (`0`).
551                 this.write_null(dest)?;
552             }
553             "pthread_key_delete" => {
554                 let key = this.read_scalar(args[0])?.to_bits(args[0].layout.size)?;
555                 this.machine.tls.delete_tls_key(key)?;
556                 // Return success (0)
557                 this.write_null(dest)?;
558             }
559             "pthread_getspecific" => {
560                 let key = this.read_scalar(args[0])?.to_bits(args[0].layout.size)?;
561                 let ptr = this.machine.tls.load_tls(key)?;
562                 this.write_scalar(ptr, dest)?;
563             }
564             "pthread_setspecific" => {
565                 let key = this.read_scalar(args[0])?.to_bits(args[0].layout.size)?;
566                 let new_ptr = this.read_scalar(args[1])?.not_undef()?;
567                 this.machine.tls.store_tls(key, new_ptr)?;
568
569                 // Return success (`0`).
570                 this.write_null(dest)?;
571             }
572
573             // Determine stack base address.
574             "pthread_attr_init" | "pthread_attr_destroy" | "pthread_attr_get_np" |
575             "pthread_getattr_np" | "pthread_self" | "pthread_get_stacksize_np" => {
576                 this.write_null(dest)?;
577             }
578             "pthread_attr_getstack" => {
579                 // Second argument is where we are supposed to write the stack size.
580                 let ptr = this.deref_operand(args[1])?;
581                 // Just any address.
582                 let stack_addr = Scalar::from_int(0x80000, args[1].layout.size);
583                 this.write_scalar(stack_addr, ptr.into())?;
584                 // Return success (`0`).
585                 this.write_null(dest)?;
586             }
587             "pthread_get_stackaddr_np" => {
588                 // Just any address.
589                 let stack_addr = Scalar::from_int(0x80000, dest.layout.size);
590                 this.write_scalar(stack_addr, dest)?;
591             }
592
593             // Stub out calls for condvar, mutex and rwlock, to just return `0`.
594             "pthread_mutexattr_init" | "pthread_mutexattr_settype" | "pthread_mutex_init" |
595             "pthread_mutexattr_destroy" | "pthread_mutex_lock" | "pthread_mutex_unlock" |
596             "pthread_mutex_destroy" | "pthread_rwlock_rdlock" | "pthread_rwlock_unlock" |
597             "pthread_rwlock_wrlock" | "pthread_rwlock_destroy" | "pthread_condattr_init" |
598             "pthread_condattr_setclock" | "pthread_cond_init" | "pthread_condattr_destroy" |
599             "pthread_cond_destroy" => {
600                 this.write_null(dest)?;
601             }
602
603             "mmap" => {
604                 // This is a horrible hack, but since the guard page mechanism calls mmap and expects a particular return value, we just give it that value.
605                 let addr = this.read_scalar(args[0])?.not_undef()?;
606                 this.write_scalar(addr, dest)?;
607             }
608             "mprotect" => {
609                 this.write_null(dest)?;
610             }
611
612             // macOS API stubs.
613             "_tlv_atexit" => {
614                 // FIXME: register the destructor.
615             },
616             "_NSGetArgc" => {
617                 this.write_scalar(Scalar::Ptr(this.machine.argc.unwrap()), dest)?;
618             },
619             "_NSGetArgv" => {
620                 this.write_scalar(Scalar::Ptr(this.machine.argv.unwrap()), dest)?;
621             },
622
623             // Windows API stubs.
624             "SetLastError" => {
625                 let err = this.read_scalar(args[0])?.to_u32()?;
626                 this.machine.last_error = err;
627             }
628             "GetLastError" => {
629                 this.write_scalar(Scalar::from_uint(this.machine.last_error, Size::from_bits(32)), dest)?;
630             }
631
632             "AddVectoredExceptionHandler" => {
633                 // Any non zero value works for the stdlib. This is just used for stack overflows anyway.
634                 this.write_scalar(Scalar::from_int(1, dest.layout.size), dest)?;
635             },
636             "InitializeCriticalSection" |
637             "EnterCriticalSection" |
638             "LeaveCriticalSection" |
639             "DeleteCriticalSection" => {
640                 // Nothing to do, not even a return value.
641             },
642             "GetModuleHandleW" |
643             "GetProcAddress" |
644             "TryEnterCriticalSection" |
645             "GetConsoleScreenBufferInfo" |
646             "SetConsoleTextAttribute" => {
647                 // Pretend these do not exist / nothing happened, by returning zero.
648                 this.write_null(dest)?;
649             },
650             "GetSystemInfo" => {
651                 let system_info = this.deref_operand(args[0])?;
652                 let system_info_ptr = system_info.ptr.to_ptr()?;
653                 // Initialize with `0`.
654                 this.memory_mut().get_mut(system_info_ptr.alloc_id)?
655                     .write_repeat(tcx, system_info_ptr, 0, system_info.layout.size)?;
656                 // Set number of processors to `1`.
657                 let dword_size = Size::from_bytes(4);
658                 let offset = 2*dword_size + 3*tcx.pointer_size();
659                 this.memory_mut().get_mut(system_info_ptr.alloc_id)?
660                     .write_scalar(
661                         tcx,
662                         system_info_ptr.offset(offset, tcx)?,
663                         Scalar::from_int(1, dword_size).into(),
664                         dword_size,
665                     )?;
666             }
667
668             "TlsAlloc" => {
669                 // This just creates a key; Windows does not natively support TLS destructors.
670
671                 // Create key and return it.
672                 let key = this.machine.tls.create_tls_key(None, tcx) as u128;
673
674                 // Figure out how large a TLS key actually is. This is `c::DWORD`.
675                 if dest.layout.size.bits() < 128
676                         && key >= (1u128 << dest.layout.size.bits() as u128) {
677                     return err!(OutOfTls);
678                 }
679                 this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?;
680             }
681             "TlsGetValue" => {
682                 let key = this.read_scalar(args[0])?.to_u32()? as u128;
683                 let ptr = this.machine.tls.load_tls(key)?;
684                 this.write_scalar(ptr, dest)?;
685             }
686             "TlsSetValue" => {
687                 let key = this.read_scalar(args[0])?.to_u32()? as u128;
688                 let new_ptr = this.read_scalar(args[1])?.not_undef()?;
689                 this.machine.tls.store_tls(key, new_ptr)?;
690
691                 // Return success (`1`).
692                 this.write_scalar(Scalar::from_int(1, dest.layout.size), dest)?;
693             }
694             "GetStdHandle" => {
695                 let which = this.read_scalar(args[0])?.to_i32()?;
696                 // We just make this the identity function, so we know later in `WriteFile`
697                 // which one it is.
698                 this.write_scalar(Scalar::from_int(which, this.pointer_size()), dest)?;
699             }
700             "WriteFile" => {
701                 let handle = this.read_scalar(args[0])?.to_isize(this)?;
702                 let buf = this.read_scalar(args[1])?.not_undef()?;
703                 let n = this.read_scalar(args[2])?.to_u32()?;
704                 let written_place = this.deref_operand(args[3])?;
705                 // Spec says to always write `0` first.
706                 this.write_null(written_place.into())?;
707                 let written = if handle == -11 || handle == -12 {
708                     // stdout/stderr
709                     use std::io::{self, Write};
710
711                     let buf_cont = this.memory().read_bytes(buf, Size::from_bytes(u64::from(n)))?;
712                     let res = if handle == -11 {
713                         io::stdout().write(buf_cont)
714                     } else {
715                         io::stderr().write(buf_cont)
716                     };
717                     res.ok().map(|n| n as u32)
718                 } else {
719                     eprintln!("Miri: Ignored output to handle {}", handle);
720                     // Pretend it all went well.
721                     Some(n)
722                 };
723                 // If there was no error, write back how much was written.
724                 if let Some(n) = written {
725                     this.write_scalar(Scalar::from_uint(n, Size::from_bits(32)), written_place.into())?;
726                 }
727                 // Return whether this was a success.
728                 this.write_scalar(
729                     Scalar::from_int(if written.is_some() { 1 } else { 0 }, dest.layout.size),
730                     dest,
731                 )?;
732             }
733             "GetConsoleMode" => {
734                 // Everything is a pipe.
735                 this.write_null(dest)?;
736             }
737             "GetEnvironmentVariableW" => {
738                 // This is not the env var you are looking for.
739                 this.machine.last_error = 203; // ERROR_ENVVAR_NOT_FOUND
740                 this.write_null(dest)?;
741             }
742             "GetCommandLineW" => {
743                 this.write_scalar(Scalar::Ptr(this.machine.cmd_line.unwrap()), dest)?;
744             }
745
746             // We can't execute anything else.
747             _ => {
748                 return err!(Unimplemented(
749                     format!("can't call foreign function: {}", link_name),
750                 ));
751             }
752         }
753
754         this.goto_block(Some(ret))?;
755         this.dump_place(*dest);
756         Ok(())
757     }
758
759     fn write_null(&mut self, dest: PlaceTy<'tcx, Borrow>) -> EvalResult<'tcx> {
760         self.eval_context_mut().write_scalar(Scalar::from_int(0, dest.layout.size), dest)
761     }
762 }