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