1use rustc_abi::Size;
2use rustc_middle::ty::layout::LayoutOf as _;
3use rustc_middle::ty::{self, Instance, Ty};
4use rustc_span::{BytePos, Loc, Symbol, hygiene};
5use rustc_target::callconv::{Conv, FnAbi};
6
7use crate::*;
8
9impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
10pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
11 fn handle_miri_backtrace_size(
12 &mut self,
13 abi: &FnAbi<'tcx, Ty<'tcx>>,
14 link_name: Symbol,
15 args: &[OpTy<'tcx>],
16 dest: &MPlaceTy<'tcx>,
17 ) -> InterpResult<'tcx> {
18 let this = self.eval_context_mut();
19 let [flags] = this.check_shim(abi, Conv::Rust, link_name, args)?;
20
21 let flags = this.read_scalar(flags)?.to_u64()?;
22 if flags != 0 {
23 throw_unsup_format!("unknown `miri_backtrace_size` flags {}", flags);
24 }
25
26 let frame_count = this.active_thread_stack().len();
27
28 this.write_scalar(Scalar::from_target_usize(frame_count.try_into().unwrap(), this), dest)
29 }
30
31 fn handle_miri_get_backtrace(
32 &mut self,
33 abi: &FnAbi<'tcx, Ty<'tcx>>,
34 link_name: Symbol,
35 args: &[OpTy<'tcx>],
36 ) -> InterpResult<'tcx> {
37 let this = self.eval_context_mut();
38 let ptr_ty = this.machine.layouts.mut_raw_ptr.ty;
39 let ptr_layout = this.layout_of(ptr_ty)?;
40
41 let [flags, buf] = this.check_shim(abi, Conv::Rust, link_name, args)?;
42
43 let flags = this.read_scalar(flags)?.to_u64()?;
44 let buf_place = this.deref_pointer_as(buf, ptr_layout)?;
45
46 let mut data = Vec::new();
47 for frame in this.active_thread_stack().iter().rev() {
48 let span = hygiene::walk_chain_collapsed(frame.current_span(), frame.body().span);
50 data.push((frame.instance(), span.lo()));
51 }
52
53 let ptrs: Vec<_> = data
54 .into_iter()
55 .map(|(instance, pos)| {
56 let fn_ptr = this.fn_ptr(FnVal::Instance(instance));
63 fn_ptr.wrapping_offset(Size::from_bytes(pos.0), this)
64 })
65 .collect();
66
67 match flags {
68 0 => {
69 throw_unsup_format!("miri_get_backtrace: v0 is not supported any more");
70 }
71 1 =>
72 for (i, ptr) in ptrs.into_iter().enumerate() {
73 let offset = ptr_layout.size.checked_mul(i.try_into().unwrap(), this).unwrap();
74
75 let op_place = buf_place.offset(offset, ptr_layout, this)?;
76
77 this.write_pointer(ptr, &op_place)?;
78 },
79 _ => throw_unsup_format!("unknown `miri_get_backtrace` flags {}", flags),
80 };
81
82 interp_ok(())
83 }
84
85 fn resolve_frame_pointer(
86 &mut self,
87 ptr: &OpTy<'tcx>,
88 ) -> InterpResult<'tcx, (Instance<'tcx>, Loc, String, String)> {
89 let this = self.eval_context_mut();
90
91 let ptr = this.read_pointer(ptr)?;
92 let (alloc_id, offset, _prov) = this.ptr_get_alloc_id(ptr, 0)?;
94
95 let fn_instance = if let Some(GlobalAlloc::Function { instance, .. }) =
97 this.tcx.try_get_global_alloc(alloc_id)
98 {
99 instance
100 } else {
101 throw_ub_format!("expected static function pointer, found {:?}", ptr);
102 };
103
104 let lo =
105 this.tcx.sess.source_map().lookup_char_pos(BytePos(offset.bytes().try_into().unwrap()));
106
107 let name = fn_instance.to_string();
108 let filename = lo.file.name.prefer_remapped_unconditionaly().to_string();
109
110 interp_ok((fn_instance, lo, name, filename))
111 }
112
113 fn handle_miri_resolve_frame(
114 &mut self,
115 abi: &FnAbi<'tcx, Ty<'tcx>>,
116 link_name: Symbol,
117 args: &[OpTy<'tcx>],
118 dest: &MPlaceTy<'tcx>,
119 ) -> InterpResult<'tcx> {
120 let this = self.eval_context_mut();
121 let [ptr, flags] = this.check_shim(abi, Conv::Rust, link_name, args)?;
122
123 let flags = this.read_scalar(flags)?.to_u64()?;
124
125 let (fn_instance, lo, name, filename) = this.resolve_frame_pointer(ptr)?;
126
127 let fn_ptr = this.fn_ptr(FnVal::Instance(fn_instance));
130
131 let num_fields = dest.layout.fields.count();
132
133 if !(4..=5).contains(&num_fields) {
134 throw_ub_format!(
137 "bad declaration of miri_resolve_frame - should return a struct with 5 fields"
138 );
139 }
140
141 let lineno: u32 = u32::try_from(lo.line).unwrap_or(0);
144 let colno: u32 = u32::try_from(lo.col.0.saturating_add(1)).unwrap_or(0);
146
147 if let ty::Adt(adt, _) = dest.layout.ty.kind() {
148 if !adt.repr().c() {
149 throw_ub_format!(
150 "miri_resolve_frame must be declared with a `#[repr(C)]` return type"
151 );
152 }
153 }
154
155 match flags {
156 0 => {
157 throw_unsup_format!("miri_resolve_frame: v0 is not supported any more");
158 }
159 1 => {
160 this.write_scalar(
161 Scalar::from_target_usize(name.len().try_into().unwrap(), this),
162 &this.project_field(dest, 0)?,
163 )?;
164 this.write_scalar(
165 Scalar::from_target_usize(filename.len().try_into().unwrap(), this),
166 &this.project_field(dest, 1)?,
167 )?;
168 }
169 _ => throw_unsup_format!("unknown `miri_resolve_frame` flags {}", flags),
170 }
171
172 this.write_scalar(Scalar::from_u32(lineno), &this.project_field(dest, 2)?)?;
173 this.write_scalar(Scalar::from_u32(colno), &this.project_field(dest, 3)?)?;
174
175 if num_fields == 5 {
178 this.write_pointer(fn_ptr, &this.project_field(dest, 4)?)?;
179 }
180
181 interp_ok(())
182 }
183
184 fn handle_miri_resolve_frame_names(
185 &mut self,
186 abi: &FnAbi<'tcx, Ty<'tcx>>,
187 link_name: Symbol,
188 args: &[OpTy<'tcx>],
189 ) -> InterpResult<'tcx> {
190 let this = self.eval_context_mut();
191
192 let [ptr, flags, name_ptr, filename_ptr] =
193 this.check_shim(abi, Conv::Rust, link_name, args)?;
194
195 let flags = this.read_scalar(flags)?.to_u64()?;
196 if flags != 0 {
197 throw_unsup_format!("unknown `miri_resolve_frame_names` flags {}", flags);
198 }
199
200 let (_, _, name, filename) = this.resolve_frame_pointer(ptr)?;
201
202 this.write_bytes_ptr(this.read_pointer(name_ptr)?, name.bytes())?;
203 this.write_bytes_ptr(this.read_pointer(filename_ptr)?, filename.bytes())?;
204
205 interp_ok(())
206 }
207}