]> git.proxmox.com Git - rustc.git/blob - src/tools/miri/miri/fn_call.rs
New upstream version 1.22.1+dfsg1
[rustc.git] / src / tools / miri / miri / fn_call.rs
1 use rustc::ty::{self, Ty};
2 use rustc::hir::def_id::{DefId, CRATE_DEF_INDEX};
3 use rustc::mir;
4 use syntax::attr;
5 use syntax::abi::Abi;
6 use syntax::codemap::Span;
7
8 use std::mem;
9
10 use rustc_miri::interpret::*;
11
12 use super::{TlsKey, EvalContext};
13
14 use tls::MemoryExt;
15
16 use super::memory::MemoryKind;
17
18 pub trait EvalContextExt<'tcx> {
19 fn call_c_abi(
20 &mut self,
21 def_id: DefId,
22 args: &[ValTy<'tcx>],
23 dest: Lvalue,
24 dest_ty: Ty<'tcx>,
25 dest_block: mir::BasicBlock,
26 ) -> EvalResult<'tcx>;
27
28 fn resolve_path(&self, path: &[&str]) -> EvalResult<'tcx, ty::Instance<'tcx>>;
29
30 fn call_missing_fn(
31 &mut self,
32 instance: ty::Instance<'tcx>,
33 destination: Option<(Lvalue, mir::BasicBlock)>,
34 args: &[ValTy<'tcx>],
35 sig: ty::FnSig<'tcx>,
36 path: String,
37 ) -> EvalResult<'tcx>;
38
39 fn eval_fn_call(
40 &mut self,
41 instance: ty::Instance<'tcx>,
42 destination: Option<(Lvalue, mir::BasicBlock)>,
43 args: &[ValTy<'tcx>],
44 span: Span,
45 sig: ty::FnSig<'tcx>,
46 ) -> EvalResult<'tcx, bool>;
47 }
48
49 impl<'a, 'tcx> EvalContextExt<'tcx> for EvalContext<'a, 'tcx, super::Evaluator> {
50 fn eval_fn_call(
51 &mut self,
52 instance: ty::Instance<'tcx>,
53 destination: Option<(Lvalue, mir::BasicBlock)>,
54 args: &[ValTy<'tcx>],
55 span: Span,
56 sig: ty::FnSig<'tcx>,
57 ) -> EvalResult<'tcx, bool> {
58 trace!("eval_fn_call: {:#?}, {:#?}", instance, destination);
59
60 let mir = match self.load_mir(instance.def) {
61 Ok(mir) => mir,
62 Err(EvalError { kind: EvalErrorKind::NoMirFor(path), .. }) => {
63 self.call_missing_fn(
64 instance,
65 destination,
66 args,
67 sig,
68 path,
69 )?;
70 return Ok(true);
71 }
72 Err(other) => return Err(other),
73 };
74
75 let (return_lvalue, return_to_block) = match destination {
76 Some((lvalue, block)) => (lvalue, StackPopCleanup::Goto(block)),
77 None => (Lvalue::undef(), StackPopCleanup::None),
78 };
79
80 self.push_stack_frame(
81 instance,
82 span,
83 mir,
84 return_lvalue,
85 return_to_block,
86 )?;
87
88 Ok(false)
89 }
90
91 fn call_c_abi(
92 &mut self,
93 def_id: DefId,
94 args: &[ValTy<'tcx>],
95 dest: Lvalue,
96 dest_ty: Ty<'tcx>,
97 dest_block: mir::BasicBlock,
98 ) -> EvalResult<'tcx> {
99 let attrs = self.tcx.get_attrs(def_id);
100 let link_name = match attr::first_attr_value_str_by_name(&attrs, "link_name") {
101 Some(name) => name.as_str(),
102 None => self.tcx.item_name(def_id),
103 };
104
105 match &link_name[..] {
106 "malloc" => {
107 let size = self.value_to_primval(args[0])?.to_u64()?;
108 if size == 0 {
109 self.write_null(dest, dest_ty)?;
110 } else {
111 let align = self.memory.pointer_size();
112 let ptr = self.memory.allocate(size, align, MemoryKind::C.into())?;
113 self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?;
114 }
115 }
116
117 "free" => {
118 let ptr = args[0].into_ptr(&mut self.memory)?;
119 if !ptr.is_null()? {
120 self.memory.deallocate(
121 ptr.to_ptr()?,
122 None,
123 MemoryKind::C.into(),
124 )?;
125 }
126 }
127
128 "syscall" => {
129 // TODO: read `syscall` ids like `sysconf` ids and
130 // figure out some way to actually process some of them
131 //
132 // libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)
133 // is called if a `HashMap` is created the regular way.
134 match self.value_to_primval(args[0])?.to_u64()? {
135 318 | 511 => {
136 return err!(Unimplemented(
137 "miri does not support random number generators".to_owned(),
138 ))
139 }
140 id => {
141 return err!(Unimplemented(
142 format!("miri does not support syscall id {}", id),
143 ))
144 }
145 }
146 }
147
148 "dlsym" => {
149 let _handle = args[0].into_ptr(&mut self.memory)?;
150 let symbol = args[1].into_ptr(&mut self.memory)?.to_ptr()?;
151 let symbol_name = self.memory.read_c_str(symbol)?;
152 let err = format!("bad c unicode symbol: {:?}", symbol_name);
153 let symbol_name = ::std::str::from_utf8(symbol_name).unwrap_or(&err);
154 return err!(Unimplemented(format!(
155 "miri does not support dynamically loading libraries (requested symbol: {})",
156 symbol_name
157 )));
158 }
159
160 "__rust_maybe_catch_panic" => {
161 // fn __rust_maybe_catch_panic(f: fn(*mut u8), data: *mut u8, data_ptr: *mut usize, vtable_ptr: *mut usize) -> u32
162 // We abort on panic, so not much is going on here, but we still have to call the closure
163 let u8_ptr_ty = self.tcx.mk_mut_ptr(self.tcx.types.u8);
164 let f = args[0].into_ptr(&mut self.memory)?.to_ptr()?;
165 let data = args[1].into_ptr(&mut self.memory)?;
166 let f_instance = self.memory.get_fn(f)?;
167 self.write_null(dest, dest_ty)?;
168
169 // Now we make a function call. TODO: Consider making this re-usable? EvalContext::step does sth. similar for the TLS dtors,
170 // and of course eval_main.
171 let mir = self.load_mir(f_instance.def)?;
172 self.push_stack_frame(
173 f_instance,
174 mir.span,
175 mir,
176 Lvalue::undef(),
177 StackPopCleanup::Goto(dest_block),
178 )?;
179
180 let arg_local = self.frame().mir.args_iter().next().ok_or(
181 EvalErrorKind::AbiViolation(
182 "Argument to __rust_maybe_catch_panic does not take enough arguments."
183 .to_owned(),
184 ),
185 )?;
186 let arg_dest = self.eval_lvalue(&mir::Lvalue::Local(arg_local))?;
187 self.write_ptr(arg_dest, data, u8_ptr_ty)?;
188
189 // We ourselves return 0
190 self.write_null(dest, dest_ty)?;
191
192 // Don't fall through
193 return Ok(());
194 }
195
196 "__rust_start_panic" => {
197 return err!(Panic);
198 }
199
200 "memcmp" => {
201 let left = args[0].into_ptr(&mut self.memory)?;
202 let right = args[1].into_ptr(&mut self.memory)?;
203 let n = self.value_to_primval(args[2])?.to_u64()?;
204
205 let result = {
206 let left_bytes = self.memory.read_bytes(left, n)?;
207 let right_bytes = self.memory.read_bytes(right, n)?;
208
209 use std::cmp::Ordering::*;
210 match left_bytes.cmp(right_bytes) {
211 Less => -1i8,
212 Equal => 0,
213 Greater => 1,
214 }
215 };
216
217 self.write_primval(
218 dest,
219 PrimVal::Bytes(result as u128),
220 dest_ty,
221 )?;
222 }
223
224 "memrchr" => {
225 let ptr = args[0].into_ptr(&mut self.memory)?;
226 let val = self.value_to_primval(args[1])?.to_u64()? as u8;
227 let num = self.value_to_primval(args[2])?.to_u64()?;
228 if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().rev().position(
229 |&c| c == val,
230 )
231 {
232 let new_ptr = ptr.offset(num - idx as u64 - 1, &self)?;
233 self.write_ptr(dest, new_ptr, dest_ty)?;
234 } else {
235 self.write_null(dest, dest_ty)?;
236 }
237 }
238
239 "memchr" => {
240 let ptr = args[0].into_ptr(&mut self.memory)?;
241 let val = self.value_to_primval(args[1])?.to_u64()? as u8;
242 let num = self.value_to_primval(args[2])?.to_u64()?;
243 if let Some(idx) = self.memory.read_bytes(ptr, num)?.iter().position(
244 |&c| c == val,
245 )
246 {
247 let new_ptr = ptr.offset(idx as u64, &self)?;
248 self.write_ptr(dest, new_ptr, dest_ty)?;
249 } else {
250 self.write_null(dest, dest_ty)?;
251 }
252 }
253
254 "getenv" => {
255 let result = {
256 let name_ptr = args[0].into_ptr(&mut self.memory)?.to_ptr()?;
257 let name = self.memory.read_c_str(name_ptr)?;
258 match self.machine_data.env_vars.get(name) {
259 Some(&var) => PrimVal::Ptr(var),
260 None => PrimVal::Bytes(0),
261 }
262 };
263 self.write_primval(dest, result, dest_ty)?;
264 }
265
266 "unsetenv" => {
267 let mut success = None;
268 {
269 let name_ptr = args[0].into_ptr(&mut self.memory)?;
270 if !name_ptr.is_null()? {
271 let name = self.memory.read_c_str(name_ptr.to_ptr()?)?;
272 if !name.is_empty() && !name.contains(&b'=') {
273 success = Some(self.machine_data.env_vars.remove(name));
274 }
275 }
276 }
277 if let Some(old) = success {
278 if let Some(var) = old {
279 self.memory.deallocate(var, None, MemoryKind::Env.into())?;
280 }
281 self.write_null(dest, dest_ty)?;
282 } else {
283 self.write_primval(dest, PrimVal::from_i128(-1), dest_ty)?;
284 }
285 }
286
287 "setenv" => {
288 let mut new = None;
289 {
290 let name_ptr = args[0].into_ptr(&mut self.memory)?;
291 let value_ptr = args[1].into_ptr(&mut self.memory)?.to_ptr()?;
292 let value = self.memory.read_c_str(value_ptr)?;
293 if !name_ptr.is_null()? {
294 let name = self.memory.read_c_str(name_ptr.to_ptr()?)?;
295 if !name.is_empty() && !name.contains(&b'=') {
296 new = Some((name.to_owned(), value.to_owned()));
297 }
298 }
299 }
300 if let Some((name, value)) = new {
301 // +1 for the null terminator
302 let value_copy = self.memory.allocate(
303 (value.len() + 1) as u64,
304 1,
305 MemoryKind::Env.into(),
306 )?;
307 self.memory.write_bytes(value_copy.into(), &value)?;
308 let trailing_zero_ptr = value_copy.offset(value.len() as u64, &self)?.into();
309 self.memory.write_bytes(trailing_zero_ptr, &[0])?;
310 if let Some(var) = self.machine_data.env_vars.insert(
311 name.to_owned(),
312 value_copy,
313 )
314 {
315 self.memory.deallocate(var, None, MemoryKind::Env.into())?;
316 }
317 self.write_null(dest, dest_ty)?;
318 } else {
319 self.write_primval(dest, PrimVal::from_i128(-1), dest_ty)?;
320 }
321 }
322
323 "write" => {
324 let fd = self.value_to_primval(args[0])?.to_u64()?;
325 let buf = args[1].into_ptr(&mut self.memory)?;
326 let n = self.value_to_primval(args[2])?.to_u64()?;
327 trace!("Called write({:?}, {:?}, {:?})", fd, buf, n);
328 let result = if fd == 1 || fd == 2 {
329 // stdout/stderr
330 use std::io::{self, Write};
331
332 let buf_cont = self.memory.read_bytes(buf, n)?;
333 let res = if fd == 1 {
334 io::stdout().write(buf_cont)
335 } else {
336 io::stderr().write(buf_cont)
337 };
338 match res {
339 Ok(n) => n as isize,
340 Err(_) => -1,
341 }
342 } else {
343 warn!("Ignored output to FD {}", fd);
344 n as isize // pretend it all went well
345 }; // now result is the value we return back to the program
346 self.write_primval(
347 dest,
348 PrimVal::Bytes(result as u128),
349 dest_ty,
350 )?;
351 }
352
353 "strlen" => {
354 let ptr = args[0].into_ptr(&mut self.memory)?.to_ptr()?;
355 let n = self.memory.read_c_str(ptr)?.len();
356 self.write_primval(dest, PrimVal::Bytes(n as u128), dest_ty)?;
357 }
358
359 // Some things needed for sys::thread initialization to go through
360 "signal" | "sigaction" | "sigaltstack" => {
361 self.write_primval(dest, PrimVal::Bytes(0), dest_ty)?;
362 }
363
364 "sysconf" => {
365 let name = self.value_to_primval(args[0])?.to_u64()?;
366 trace!("sysconf() called with name {}", name);
367 // cache the sysconf integers via miri's global cache
368 let paths = &[
369 (&["libc", "_SC_PAGESIZE"], PrimVal::Bytes(4096)),
370 (&["libc", "_SC_GETPW_R_SIZE_MAX"], PrimVal::from_i128(-1)),
371 ];
372 let mut result = None;
373 for &(path, path_value) in paths {
374 if let Ok(instance) = self.resolve_path(path) {
375 let cid = GlobalId {
376 instance,
377 promoted: None,
378 };
379 // compute global if not cached
380 let val = match self.globals.get(&cid).cloned() {
381 Some(ptr) => self.value_to_primval(ValTy { value: Value::ByRef(ptr), ty: args[0].ty })?.to_u64()?,
382 None => eval_body_as_primval(self.tcx, instance)?.0.to_u64()?,
383 };
384 if val == name {
385 result = Some(path_value);
386 break;
387 }
388 }
389 }
390 if let Some(result) = result {
391 self.write_primval(dest, result, dest_ty)?;
392 } else {
393 return err!(Unimplemented(
394 format!("Unimplemented sysconf name: {}", name),
395 ));
396 }
397 }
398
399 // Hook pthread calls that go to the thread-local storage memory subsystem
400 "pthread_key_create" => {
401 let key_ptr = args[0].into_ptr(&mut self.memory)?;
402
403 // Extract the function type out of the signature (that seems easier than constructing it ourselves...)
404 let dtor = match args[1].into_ptr(&mut self.memory)?.into_inner_primval() {
405 PrimVal::Ptr(dtor_ptr) => Some(self.memory.get_fn(dtor_ptr)?),
406 PrimVal::Bytes(0) => None,
407 PrimVal::Bytes(_) => return err!(ReadBytesAsPointer),
408 PrimVal::Undef => return err!(ReadUndefBytes),
409 };
410
411 // Figure out how large a pthread TLS key actually is. This is libc::pthread_key_t.
412 let key_type = args[0].ty.builtin_deref(true, ty::LvaluePreference::NoPreference)
413 .ok_or(EvalErrorKind::AbiViolation("Wrong signature used for pthread_key_create: First argument must be a raw pointer.".to_owned()))?.ty;
414 let key_size = {
415 let layout = self.type_layout(key_type)?;
416 layout.size(&self.tcx.data_layout)
417 };
418
419 // Create key and write it into the memory where key_ptr wants it
420 let key = self.memory.create_tls_key(dtor) as u128;
421 if key_size.bits() < 128 && key >= (1u128 << key_size.bits() as u128) {
422 return err!(OutOfTls);
423 }
424 self.memory.write_primval(
425 key_ptr.to_ptr()?,
426 PrimVal::Bytes(key),
427 key_size.bytes(),
428 false,
429 )?;
430
431 // Return success (0)
432 self.write_null(dest, dest_ty)?;
433 }
434 "pthread_key_delete" => {
435 // The conversion into TlsKey here is a little fishy, but should work as long as usize >= libc::pthread_key_t
436 let key = self.value_to_primval(args[0])?.to_u64()? as TlsKey;
437 self.memory.delete_tls_key(key)?;
438 // Return success (0)
439 self.write_null(dest, dest_ty)?;
440 }
441 "pthread_getspecific" => {
442 // The conversion into TlsKey here is a little fishy, but should work as long as usize >= libc::pthread_key_t
443 let key = self.value_to_primval(args[0])?.to_u64()? as TlsKey;
444 let ptr = self.memory.load_tls(key)?;
445 self.write_ptr(dest, ptr, dest_ty)?;
446 }
447 "pthread_setspecific" => {
448 // The conversion into TlsKey here is a little fishy, but should work as long as usize >= libc::pthread_key_t
449 let key = self.value_to_primval(args[0])?.to_u64()? as TlsKey;
450 let new_ptr = args[1].into_ptr(&mut self.memory)?;
451 self.memory.store_tls(key, new_ptr)?;
452
453 // Return success (0)
454 self.write_null(dest, dest_ty)?;
455 }
456
457 // Stub out all the other pthread calls to just return 0
458 link_name if link_name.starts_with("pthread_") => {
459 info!("ignoring C ABI call: {}", link_name);
460 self.write_null(dest, dest_ty)?;
461 }
462
463 _ => {
464 return err!(Unimplemented(
465 format!("can't call C ABI function: {}", link_name),
466 ));
467 }
468 }
469
470 // Since we pushed no stack frame, the main loop will act
471 // as if the call just completed and it's returning to the
472 // current frame.
473 self.dump_local(dest);
474 self.goto_block(dest_block);
475 Ok(())
476 }
477
478 /// Get an instance for a path.
479 fn resolve_path(&self, path: &[&str]) -> EvalResult<'tcx, ty::Instance<'tcx>> {
480 self.tcx
481 .crates()
482 .iter()
483 .find(|&&krate| self.tcx.original_crate_name(krate) == path[0])
484 .and_then(|krate| {
485 let krate = DefId {
486 krate: *krate,
487 index: CRATE_DEF_INDEX,
488 };
489 let mut items = self.tcx.item_children(krate);
490 let mut path_it = path.iter().skip(1).peekable();
491
492 while let Some(segment) = path_it.next() {
493 for item in mem::replace(&mut items, Default::default()).iter() {
494 if item.ident.name == *segment {
495 if path_it.peek().is_none() {
496 return Some(ty::Instance::mono(self.tcx, item.def.def_id()));
497 }
498
499 items = self.tcx.item_children(item.def.def_id());
500 break;
501 }
502 }
503 }
504 None
505 })
506 .ok_or_else(|| {
507 let path = path.iter().map(|&s| s.to_owned()).collect();
508 EvalErrorKind::PathNotFound(path).into()
509 })
510 }
511
512 fn call_missing_fn(
513 &mut self,
514 instance: ty::Instance<'tcx>,
515 destination: Option<(Lvalue, mir::BasicBlock)>,
516 args: &[ValTy<'tcx>],
517 sig: ty::FnSig<'tcx>,
518 path: String,
519 ) -> EvalResult<'tcx> {
520 // In some cases in non-MIR libstd-mode, not having a destination is legit. Handle these early.
521 match &path[..] {
522 "std::panicking::rust_panic_with_hook" |
523 "core::panicking::panic_fmt::::panic_impl" |
524 "std::rt::begin_panic_fmt" => return err!(Panic),
525 _ => {}
526 }
527
528 let dest_ty = sig.output();
529 let (dest, dest_block) = destination.ok_or_else(
530 || EvalErrorKind::NoMirFor(path.clone()),
531 )?;
532
533 if sig.abi == Abi::C {
534 // An external C function
535 // TODO: That functions actually has a similar preamble to what follows here. May make sense to
536 // unify these two mechanisms for "hooking into missing functions".
537 self.call_c_abi(
538 instance.def_id(),
539 args,
540 dest,
541 dest_ty,
542 dest_block,
543 )?;
544 return Ok(());
545 }
546
547 match &path[..] {
548 // Allocators are magic. They have no MIR, even when the rest of libstd does.
549 "alloc::heap::::__rust_alloc" => {
550 let size = self.value_to_primval(args[0])?.to_u64()?;
551 let align = self.value_to_primval(args[1])?.to_u64()?;
552 if size == 0 {
553 return err!(HeapAllocZeroBytes);
554 }
555 if !align.is_power_of_two() {
556 return err!(HeapAllocNonPowerOfTwoAlignment(align));
557 }
558 let ptr = self.memory.allocate(size, align, MemoryKind::Rust.into())?;
559 self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?;
560 }
561 "alloc::heap::::__rust_alloc_zeroed" => {
562 let size = self.value_to_primval(args[0])?.to_u64()?;
563 let align = self.value_to_primval(args[1])?.to_u64()?;
564 if size == 0 {
565 return err!(HeapAllocZeroBytes);
566 }
567 if !align.is_power_of_two() {
568 return err!(HeapAllocNonPowerOfTwoAlignment(align));
569 }
570 let ptr = self.memory.allocate(size, align, MemoryKind::Rust.into())?;
571 self.memory.write_repeat(ptr.into(), 0, size)?;
572 self.write_primval(dest, PrimVal::Ptr(ptr), dest_ty)?;
573 }
574 "alloc::heap::::__rust_dealloc" => {
575 let ptr = args[0].into_ptr(&mut self.memory)?.to_ptr()?;
576 let old_size = self.value_to_primval(args[1])?.to_u64()?;
577 let align = self.value_to_primval(args[2])?.to_u64()?;
578 if old_size == 0 {
579 return err!(HeapAllocZeroBytes);
580 }
581 if !align.is_power_of_two() {
582 return err!(HeapAllocNonPowerOfTwoAlignment(align));
583 }
584 self.memory.deallocate(
585 ptr,
586 Some((old_size, align)),
587 MemoryKind::Rust.into(),
588 )?;
589 }
590 "alloc::heap::::__rust_realloc" => {
591 let ptr = args[0].into_ptr(&mut self.memory)?.to_ptr()?;
592 let old_size = self.value_to_primval(args[1])?.to_u64()?;
593 let old_align = self.value_to_primval(args[2])?.to_u64()?;
594 let new_size = self.value_to_primval(args[3])?.to_u64()?;
595 let new_align = self.value_to_primval(args[4])?.to_u64()?;
596 if old_size == 0 || new_size == 0 {
597 return err!(HeapAllocZeroBytes);
598 }
599 if !old_align.is_power_of_two() {
600 return err!(HeapAllocNonPowerOfTwoAlignment(old_align));
601 }
602 if !new_align.is_power_of_two() {
603 return err!(HeapAllocNonPowerOfTwoAlignment(new_align));
604 }
605 let new_ptr = self.memory.reallocate(
606 ptr,
607 old_size,
608 old_align,
609 new_size,
610 new_align,
611 MemoryKind::Rust.into(),
612 )?;
613 self.write_primval(dest, PrimVal::Ptr(new_ptr), dest_ty)?;
614 }
615
616 // A Rust function is missing, which means we are running with MIR missing for libstd (or other dependencies).
617 // Still, we can make many things mostly work by "emulating" or ignoring some functions.
618 "std::io::_print" => {
619 warn!(
620 "Ignoring output. To run programs that print, make sure you have a libstd with full MIR."
621 );
622 }
623 "std::thread::Builder::new" => {
624 return err!(Unimplemented("miri does not support threading".to_owned()))
625 }
626 "std::env::args" => {
627 return err!(Unimplemented(
628 "miri does not support program arguments".to_owned(),
629 ))
630 }
631 "std::panicking::panicking" |
632 "std::rt::panicking" => {
633 // we abort on panic -> `std::rt::panicking` always returns false
634 let bool = self.tcx.types.bool;
635 self.write_primval(dest, PrimVal::from_bool(false), bool)?;
636 }
637 "std::sys::imp::c::::AddVectoredExceptionHandler" |
638 "std::sys::imp::c::::SetThreadStackGuarantee" => {
639 let usize = self.tcx.types.usize;
640 // any non zero value works for the stdlib. This is just used for stackoverflows anyway
641 self.write_primval(dest, PrimVal::Bytes(1), usize)?;
642 },
643 _ => return err!(NoMirFor(path)),
644 }
645
646 // Since we pushed no stack frame, the main loop will act
647 // as if the call just completed and it's returning to the
648 // current frame.
649 self.dump_local(dest);
650 self.goto_block(dest_block);
651 return Ok(());
652 }
653 }