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_sig_lenient(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_sig_lenient(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 Some(GlobalAlloc::Function { instance, .. }) = this.tcx.try_get_global_alloc(alloc_id)
96 else {
97 throw_ub_format!("expected static function pointer, found {:?}", ptr);
98 };
99
100 let lo =
101 this.tcx.sess.source_map().lookup_char_pos(BytePos(offset.bytes().try_into().unwrap()));
102
103 let name = instance.to_string();
104 let filename = lo.file.name.prefer_remapped_unconditionally().to_string();
105
106 interp_ok((instance, lo, name, filename))
107 }
108
109 fn handle_miri_resolve_frame(
110 &mut self,
111 abi: &FnAbi<'tcx, Ty<'tcx>>,
112 link_name: Symbol,
113 args: &[OpTy<'tcx>],
114 dest: &MPlaceTy<'tcx>,
115 ) -> InterpResult<'tcx> {
116 let this = self.eval_context_mut();
117 let [ptr, flags] = this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
118
119 let flags = this.read_scalar(flags)?.to_u64()?;
120
121 let (fn_instance, lo, name, filename) = this.resolve_frame_pointer(ptr)?;
122
123 let fn_ptr = this.fn_ptr(FnVal::Instance(fn_instance));
126
127 let num_fields = dest.layout.fields.count();
128
129 if !(4..=5).contains(&num_fields) {
130 throw_ub_format!(
133 "bad declaration of miri_resolve_frame - should return a struct with 5 fields"
134 );
135 }
136
137 let lineno: u32 = u32::try_from(lo.line).unwrap_or(0);
140 let colno: u32 = u32::try_from(lo.col.0.saturating_add(1)).unwrap_or(0);
142
143 if let ty::Adt(adt, _) = dest.layout.ty.kind() {
144 if !adt.repr().c() {
145 throw_ub_format!(
146 "miri_resolve_frame must be declared with a `#[repr(C)]` return type"
147 );
148 }
149 }
150
151 match flags {
152 0 => {
153 throw_unsup_format!("miri_resolve_frame: v0 is not supported any more");
154 }
155 1 => {
156 this.write_scalar(
157 Scalar::from_target_usize(name.len().to_u64(), this),
158 &this.project_field(dest, FieldIdx::from_u32(0))?,
159 )?;
160 this.write_scalar(
161 Scalar::from_target_usize(filename.len().to_u64(), this),
162 &this.project_field(dest, FieldIdx::from_u32(1))?,
163 )?;
164 }
165 _ => throw_unsup_format!("unknown `miri_resolve_frame` flags {}", flags),
166 }
167
168 this.write_scalar(
169 Scalar::from_u32(lineno),
170 &this.project_field(dest, FieldIdx::from_u32(2))?,
171 )?;
172 this.write_scalar(
173 Scalar::from_u32(colno),
174 &this.project_field(dest, FieldIdx::from_u32(3))?,
175 )?;
176
177 if num_fields == 5 {
180 this.write_pointer(fn_ptr, &this.project_field(dest, FieldIdx::from_u32(4))?)?;
181 }
182
183 interp_ok(())
184 }
185
186 fn handle_miri_resolve_frame_names(
187 &mut self,
188 abi: &FnAbi<'tcx, Ty<'tcx>>,
189 link_name: Symbol,
190 args: &[OpTy<'tcx>],
191 ) -> InterpResult<'tcx> {
192 let this = self.eval_context_mut();
193
194 let [ptr, flags, name_ptr, filename_ptr] =
195 this.check_shim_sig_lenient(abi, CanonAbi::Rust, link_name, args)?;
196
197 let flags = this.read_scalar(flags)?.to_u64()?;
198 if flags != 0 {
199 throw_unsup_format!("unknown `miri_resolve_frame_names` flags {}", flags);
200 }
201
202 let (_, _, name, filename) = this.resolve_frame_pointer(ptr)?;
203
204 this.write_bytes_ptr(this.read_pointer(name_ptr)?, name.bytes())?;
205 this.write_bytes_ptr(this.read_pointer(filename_ptr)?, filename.bytes())?;
206
207 interp_ok(())
208 }
209}