1 use rustc
::ty
::{self, Ty}
;
2 use rustc
::hir
::def_id
::{DefId, CRATE_DEF_INDEX}
;
6 use syntax
::codemap
::Span
;
10 use rustc_miri
::interpret
::*;
12 use super::{TlsKey, EvalContext}
;
16 use super::memory
::MemoryKind
;
18 pub trait EvalContextExt
<'tcx
> {
25 dest_block
: mir
::BasicBlock
,
26 ) -> EvalResult
<'tcx
>;
28 fn resolve_path(&self, path
: &[&str]) -> EvalResult
<'tcx
, ty
::Instance
<'tcx
>>;
32 instance
: ty
::Instance
<'tcx
>,
33 destination
: Option
<(Lvalue
, mir
::BasicBlock
)>,
37 ) -> EvalResult
<'tcx
>;
41 instance
: ty
::Instance
<'tcx
>,
42 destination
: Option
<(Lvalue
, mir
::BasicBlock
)>,
46 ) -> EvalResult
<'tcx
, bool
>;
49 impl<'a
, 'tcx
> EvalContextExt
<'tcx
> for EvalContext
<'a
, 'tcx
, super::Evaluator
> {
52 instance
: ty
::Instance
<'tcx
>,
53 destination
: Option
<(Lvalue
, mir
::BasicBlock
)>,
57 ) -> EvalResult
<'tcx
, bool
> {
58 trace
!("eval_fn_call: {:#?}, {:#?}", instance
, destination
);
60 let mir
= match self.load_mir(instance
.def
) {
62 Err(EvalError { kind: EvalErrorKind::NoMirFor(path), .. }
) => {
72 Err(other
) => return Err(other
),
75 let (return_lvalue
, return_to_block
) = match destination
{
76 Some((lvalue
, block
)) => (lvalue
, StackPopCleanup
::Goto(block
)),
77 None
=> (Lvalue
::undef(), StackPopCleanup
::None
),
80 self.push_stack_frame(
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
),
105 match &link_name
[..] {
107 let size
= self.value_to_primval(args
[0])?
.to_u64()?
;
109 self.write_null(dest
, dest_ty
)?
;
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
)?
;
118 let ptr
= args
[0].into_ptr(&mut self.memory
)?
;
120 self.memory
.deallocate(
123 MemoryKind
::C
.into(),
129 // TODO: read `syscall` ids like `sysconf` ids and
130 // figure out some way to actually process some of them
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()?
{
136 return err
!(Unimplemented(
137 "miri does not support random number generators".to_owned(),
141 return err
!(Unimplemented(
142 format
!("miri does not support syscall id {}", id
),
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: {})",
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
)?
;
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(
177 StackPopCleanup
::Goto(dest_block
),
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."
186 let arg_dest
= self.eval_lvalue(&mir
::Lvalue
::Local(arg_local
))?
;
187 self.write_ptr(arg_dest
, data
, u8_ptr_ty
)?
;
189 // We ourselves return 0
190 self.write_null(dest
, dest_ty
)?
;
192 // Don't fall through
196 "__rust_start_panic" => {
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()?
;
206 let left_bytes
= self.memory
.read_bytes(left
, n
)?
;
207 let right_bytes
= self.memory
.read_bytes(right
, n
)?
;
209 use std
::cmp
::Ordering
::*;
210 match left_bytes
.cmp(right_bytes
) {
219 PrimVal
::Bytes(result
as u128
),
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(
232 let new_ptr
= ptr
.offset(num
- idx
as u64 - 1, &self)?
;
233 self.write_ptr(dest
, new_ptr
, dest_ty
)?
;
235 self.write_null(dest
, dest_ty
)?
;
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(
247 let new_ptr
= ptr
.offset(idx
as u64, &self)?
;
248 self.write_ptr(dest
, new_ptr
, dest_ty
)?
;
250 self.write_null(dest
, dest_ty
)?
;
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),
263 self.write_primval(dest
, result
, dest_ty
)?
;
267 let mut success
= None
;
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
));
277 if let Some(old
) = success
{
278 if let Some(var
) = old
{
279 self.memory
.deallocate(var
, None
, MemoryKind
::Env
.into())?
;
281 self.write_null(dest
, dest_ty
)?
;
283 self.write_primval(dest
, PrimVal
::from_i128(-1), dest_ty
)?
;
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()));
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,
305 MemoryKind
::Env
.into(),
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(
315 self.memory
.deallocate(var
, None
, MemoryKind
::Env
.into())?
;
317 self.write_null(dest
, dest_ty
)?
;
319 self.write_primval(dest
, PrimVal
::from_i128(-1), dest_ty
)?
;
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 {
330 use std
::io
::{self, Write}
;
332 let buf_cont
= self.memory
.read_bytes(buf
, n
)?
;
333 let res
= if fd
== 1 {
334 io
::stdout().write(buf_cont
)
336 io
::stderr().write(buf_cont
)
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
348 PrimVal
::Bytes(result
as u128
),
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
)?
;
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
)?
;
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
369 (&["libc", "_SC_PAGESIZE"], PrimVal
::Bytes(4096)),
370 (&["libc", "_SC_GETPW_R_SIZE_MAX"], PrimVal
::from_i128(-1)),
372 let mut result
= None
;
373 for &(path
, path_value
) in paths
{
374 if let Ok(instance
) = self.resolve_path(path
) {
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()?
,
385 result
= Some(path_value
);
390 if let Some(result
) = result
{
391 self.write_primval(dest
, result
, dest_ty
)?
;
393 return err
!(Unimplemented(
394 format
!("Unimplemented sysconf name: {}", name
),
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
)?
;
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
),
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
;
415 let layout
= self.type_layout(key_type
)?
;
416 layout
.size(&self.tcx
.data_layout
)
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
);
424 self.memory
.write_primval(
431 // Return success (0)
432 self.write_null(dest
, dest_ty
)?
;
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
)?
;
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
)?
;
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
)?
;
453 // Return success (0)
454 self.write_null(dest
, dest_ty
)?
;
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
)?
;
464 return err
!(Unimplemented(
465 format
!("can't call C ABI function: {}", link_name
),
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
473 self.dump_local(dest
);
474 self.goto_block(dest_block
);
478 /// Get an instance for a path.
479 fn resolve_path(&self, path
: &[&str]) -> EvalResult
<'tcx
, ty
::Instance
<'tcx
>> {
483 .find(|&&krate
| self.tcx
.original_crate_name(krate
) == path
[0])
487 index
: CRATE_DEF_INDEX
,
489 let mut items
= self.tcx
.item_children(krate
);
490 let mut path_it
= path
.iter().skip(1).peekable();
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()));
499 items
= self.tcx
.item_children(item
.def
.def_id());
507 let path
= path
.iter().map(|&s
| s
.to_owned()).collect();
508 EvalErrorKind
::PathNotFound(path
).into()
514 instance
: ty
::Instance
<'tcx
>,
515 destination
: Option
<(Lvalue
, mir
::BasicBlock
)>,
516 args
: &[ValTy
<'tcx
>],
517 sig
: ty
::FnSig
<'tcx
>,
519 ) -> EvalResult
<'tcx
> {
520 // In some cases in non-MIR libstd-mode, not having a destination is legit. Handle these early.
522 "std::panicking::rust_panic_with_hook" |
523 "core::panicking::panic_fmt::::panic_impl" |
524 "std::rt::begin_panic_fmt" => return err
!(Panic
),
528 let dest_ty
= sig
.output();
529 let (dest
, dest_block
) = destination
.ok_or_else(
530 || EvalErrorKind
::NoMirFor(path
.clone()),
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".
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()?
;
553 return err
!(HeapAllocZeroBytes
);
555 if !align
.is_power_of_two() {
556 return err
!(HeapAllocNonPowerOfTwoAlignment(align
));
558 let ptr
= self.memory
.allocate(size
, align
, MemoryKind
::Rust
.into())?
;
559 self.write_primval(dest
, PrimVal
::Ptr(ptr
), dest_ty
)?
;
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()?
;
565 return err
!(HeapAllocZeroBytes
);
567 if !align
.is_power_of_two() {
568 return err
!(HeapAllocNonPowerOfTwoAlignment(align
));
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
)?
;
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()?
;
579 return err
!(HeapAllocZeroBytes
);
581 if !align
.is_power_of_two() {
582 return err
!(HeapAllocNonPowerOfTwoAlignment(align
));
584 self.memory
.deallocate(
586 Some((old_size
, align
)),
587 MemoryKind
::Rust
.into(),
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
);
599 if !old_align
.is_power_of_two() {
600 return err
!(HeapAllocNonPowerOfTwoAlignment(old_align
));
602 if !new_align
.is_power_of_two() {
603 return err
!(HeapAllocNonPowerOfTwoAlignment(new_align
));
605 let new_ptr
= self.memory
.reallocate(
611 MemoryKind
::Rust
.into(),
613 self.write_primval(dest
, PrimVal
::Ptr(new_ptr
), dest_ty
)?
;
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" => {
620 "Ignoring output. To run programs that print, make sure you have a libstd with full MIR."
623 "std::thread::Builder::new" => {
624 return err
!(Unimplemented("miri does not support threading".to_owned()))
626 "std::env::args" => {
627 return err
!(Unimplemented(
628 "miri does not support program arguments".to_owned(),
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
)?
;
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)?
;
643 _
=> return err
!(NoMirFor(path
)),
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
649 self.dump_local(dest
);
650 self.goto_block(dest_block
);