|
| 1 | +use crate::*; |
| 2 | +use helpers::check_arg_count; |
| 3 | +use rustc_middle::ty::{self, TypeAndMut}; |
| 4 | +use rustc_ast::ast::Mutability; |
| 5 | +use rustc_span::BytePos; |
| 6 | +use rustc_target::abi::Size; |
| 7 | +use std::convert::TryInto as _; |
| 8 | +use crate::rustc_target::abi::LayoutOf as _; |
| 9 | + |
| 10 | +impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {} |
| 11 | +pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> { |
| 12 | + |
| 13 | + fn handle_miri_get_backtrace( |
| 14 | + &mut self, |
| 15 | + args: &[OpTy<'tcx, Tag>], |
| 16 | + dest: PlaceTy<'tcx, Tag> |
| 17 | + ) -> InterpResult<'tcx> { |
| 18 | + let this = self.eval_context_mut(); |
| 19 | + let tcx = this.tcx; |
| 20 | + let &[flags] = check_arg_count(args)?; |
| 21 | + |
| 22 | + let flags = this.read_scalar(flags)?.to_u64()?; |
| 23 | + if flags != 0 { |
| 24 | + throw_unsup_format!("unknown `miri_get_backtrace` flags {}", flags); |
| 25 | + } |
| 26 | + |
| 27 | + let mut data = Vec::new(); |
| 28 | + for frame in this.active_thread_stack().iter().rev() { |
| 29 | + data.push((frame.instance, frame.current_span().lo())); |
| 30 | + } |
| 31 | + |
| 32 | + let ptrs: Vec<_> = data.into_iter().map(|(instance, pos)| { |
| 33 | + // We represent a frame pointer by using the `span.lo` value |
| 34 | + // as an offset into the function's allocation. This gives us an |
| 35 | + // opaque pointer that we can return to user code, and allows us |
| 36 | + // to reconstruct the needed frame information in `handle_miri_resolve_frame`. |
| 37 | + // Note that we never actually read or write anything from/to this pointer - |
| 38 | + // all of the data is represented by the pointer value itself. |
| 39 | + let mut fn_ptr = this.memory.create_fn_alloc(FnVal::Instance(instance)); |
| 40 | + fn_ptr.offset = Size::from_bytes(pos.0); |
| 41 | + Scalar::Ptr(fn_ptr) |
| 42 | + }).collect(); |
| 43 | + |
| 44 | + let len = ptrs.len(); |
| 45 | + |
| 46 | + let ptr_ty = tcx.mk_ptr(TypeAndMut { |
| 47 | + ty: tcx.types.unit, |
| 48 | + mutbl: Mutability::Mut |
| 49 | + }); |
| 50 | + |
| 51 | + let array_ty = tcx.mk_array(ptr_ty, ptrs.len().try_into().unwrap()); |
| 52 | + |
| 53 | + // Write pointers into array |
| 54 | + let alloc = this.allocate(this.layout_of(array_ty).unwrap(), MiriMemoryKind::Rust.into()); |
| 55 | + for (i, ptr) in ptrs.into_iter().enumerate() { |
| 56 | + let place = this.mplace_index(alloc, i as u64)?; |
| 57 | + this.write_immediate_to_mplace(ptr.into(), place)?; |
| 58 | + } |
| 59 | + |
| 60 | + this.write_immediate(Immediate::new_slice(alloc.ptr.into(), len.try_into().unwrap(), this), dest)?; |
| 61 | + Ok(()) |
| 62 | + } |
| 63 | + |
| 64 | + fn handle_miri_resolve_frame( |
| 65 | + &mut self, |
| 66 | + args: &[OpTy<'tcx, Tag>], |
| 67 | + dest: PlaceTy<'tcx, Tag> |
| 68 | + ) -> InterpResult<'tcx> { |
| 69 | + let this = self.eval_context_mut(); |
| 70 | + let tcx = this.tcx; |
| 71 | + let &[ptr, flags] = check_arg_count(args)?; |
| 72 | + |
| 73 | + let flags = this.read_scalar(flags)?.to_u64()?; |
| 74 | + if flags != 0 { |
| 75 | + throw_unsup_format!("unknown `miri_resolve_frame` flags {}", flags); |
| 76 | + } |
| 77 | + |
| 78 | + let ptr = match this.read_scalar(ptr)?.check_init()? { |
| 79 | + Scalar::Ptr(ptr) => ptr, |
| 80 | + Scalar::Raw { .. } => throw_ub_format!("expected a pointer in `rust_miri_resolve_frame`, found {:?}", ptr) |
| 81 | + }; |
| 82 | + |
| 83 | + let fn_instance = if let Some(GlobalAlloc::Function(instance)) = this.tcx.get_global_alloc(ptr.alloc_id) { |
| 84 | + instance |
| 85 | + } else { |
| 86 | + throw_ub_format!("expected function pointer, found {:?}", ptr); |
| 87 | + }; |
| 88 | + |
| 89 | + if dest.layout.layout.fields.count() != 4 { |
| 90 | + throw_ub_format!("bad declaration of miri_resolve_frame - should return a struct with 4 fields"); |
| 91 | + } |
| 92 | + |
| 93 | + let pos = BytePos(ptr.offset.bytes().try_into().unwrap()); |
| 94 | + let name = fn_instance.to_string(); |
| 95 | + |
| 96 | + let lo = tcx.sess.source_map().lookup_char_pos(pos); |
| 97 | + |
| 98 | + let filename = lo.file.name.to_string(); |
| 99 | + let lineno: u32 = lo.line as u32; |
| 100 | + // `lo.col` is 0-based - add 1 to make it 1-based for the caller. |
| 101 | + let colno: u32 = lo.col.0 as u32 + 1; |
| 102 | + |
| 103 | + let name_alloc = this.allocate_str(&name, MiriMemoryKind::Rust.into()); |
| 104 | + let filename_alloc = this.allocate_str(&filename, MiriMemoryKind::Rust.into()); |
| 105 | + let lineno_alloc = Scalar::from_u32(lineno); |
| 106 | + let colno_alloc = Scalar::from_u32(colno); |
| 107 | + |
| 108 | + let dest = this.force_allocation(dest)?; |
| 109 | + if let ty::Adt(adt, _) = dest.layout.ty.kind() { |
| 110 | + if !adt.repr.c() { |
| 111 | + throw_ub_format!("miri_resolve_frame must be declared with a `#[repr(C)]` return type"); |
| 112 | + } |
| 113 | + } |
| 114 | + |
| 115 | + this.write_immediate(name_alloc.to_ref(), this.mplace_field(dest, 0)?.into())?; |
| 116 | + this.write_immediate(filename_alloc.to_ref(), this.mplace_field(dest, 1)?.into())?; |
| 117 | + this.write_scalar(lineno_alloc, this.mplace_field(dest, 2)?.into())?; |
| 118 | + this.write_scalar(colno_alloc, this.mplace_field(dest, 3)?.into())?; |
| 119 | + Ok(()) |
| 120 | + } |
| 121 | +} |
0 commit comments