1use std::borrow::Cow;
2use std::path::Path;
3
4use symbolic::common::{ByteView, Language, Name, NameMangling};
5use symbolic::debuginfo::{Function, LineInfo, Object, ObjectError, Symbol};
6use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
7use tokio_stream::Stream;
8use anyhow::{Context, Result};
9
10use crate::fmt::addr::Addr;
11use crate::fmt::addr::DEF;
12use crate::fmt::report::{self, Report};
13
14
15pub struct Resolver<'elf> {
16 data: ByteView<'elf>,
17 functions: bool,
18 inlinees: bool,
19}
20
21impl<'t> Resolver<'t> {
22 pub fn new(path: &Path, functions: bool, inlinees: bool) -> Result<Self> {
23 let data = ByteView::open(path).context("failed to open file")?;
24 Ok(Self { data,
25 functions,
26 inlinees })
27 }
28
29
30 pub fn into_stream_lazy_from<'a: 't, T: 'a + IntoIterator<Item = Addr<DEF>>>(
82 self,
83 mut rx: UnboundedReceiver<Addr>,
84 addrs: Option<T>)
85 -> Result<impl Stream<Item = Result<Report, ObjectError>> + 't> {
86 let stream = async_stream::try_stream! {
87 let data = self.data;
88 let object = Object::parse(&data)?;
89 let session = object.debug_session()?;
90 let symbols = object.symbol_map();
91
92 let load_address = object.load_address();
93 debug!("Elf's load-address: {load_address}");
94
95 let doit = |mut addr: Addr| {
96 trace!(" Resolving {addr:#08x?}");
97 {
98 use crate::fmt::addr::USER_HEAP_REV1;
100 if USER_HEAP_REV1.contains(&addr.value()) && USER_HEAP_REV1.start >= load_address {
101 debug!("Seems to {addr:#08x?} is in the user-mem.");
102 let diff = USER_HEAP_REV1.start - load_address;
103 if addr.value() >= diff {
104 addr.set_fixed(addr.value() - diff);
105 trace!(" Now resolving {addr:#08x?}");
106 }
107 }
108 }
109
110 let mut result = None;
111 'funcs: for function in session.functions() {
112 match function.context("failed to read function") {
113 Ok(function) => {
114 let res = if self.inlinees {
115 resolve_with_inlinees(&function, addr).filter(|v| !v.is_empty())
116 } else {
117 resolve(&function, addr).map(|res| vec![res])
118 };
119
120 if let Some(resolved) = res {
121 debug_assert!(!resolved.is_empty());
122 let rep = report::Report::from_vec(addr, resolved);
123 trace!("Result of resolving fn: {addr:#08x?}, report addr: {:#08x?}", rep.addr);
124 result = Some(rep);
125 break 'funcs;
126 }
127 },
128 Err(err) => error!("{err}"),
129 }
130 }
131
132 if let Some(res) = result {
133 res
134 } else if self.functions {
135 trace!("Not found fn for {addr:#08x?}, symbols lookup...");
136 let resolved = symbols.lookup(addr.fixed()).or_else(||symbols.lookup(addr.value())).map(ResolvedAddrRef::Sym);
137
138 if let Some(resolved) = resolved {
139 trace!("Found sym for {addr:#08x?}");
140 report::Report::from_one(addr, resolved)
141 } else {
142 trace!("Not found sym for {addr:#08x?}");
143 report::Report{ addr, symbols: vec![] }
144 }
145 } else {
146 trace!("Not found fn for {addr:#08x?}");
147 report::Report{ addr: addr, symbols: vec![] }
148 }
149 };
150
151 if let Some(addrs) = addrs {
152 for addr in addrs {
153 yield doit(addr);
154 }
155 }
156
157 while let Some(addr) = rx.recv().await {
158 yield doit(addr);
159 }
160 };
161
162 Ok(stream)
163 }
164
165
166 pub fn into_stream_resolve<'a: 't, T>(self,
183 addrs: &'a [T])
184 -> Result<impl Stream<Item = Result<Report, ObjectError>> + 't>
185 where T: Copy + Into<Addr<u64>>
186 {
187 let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
188 drop(tx);
189 self.into_stream_lazy_from(
190 rx,
191 Some(addrs.into_iter().map(|v| (*v).into()).collect::<Vec<_>>()),
192 )
193 }
194
195
196 pub fn into_stream_lazy<'a: 't, T: 'a + IntoIterator<Item = Addr<DEF>>>(
197 self,
198 addrs: Option<T>)
199 -> Result<(UnboundedSender<Addr>, impl Stream<Item = Result<Report, ObjectError>> + 't)> {
200 let (tx, rx) = tokio::sync::mpsc::unbounded_channel::<Addr>();
201 self.into_stream_lazy_from(rx, addrs).map(|stream| (tx, stream))
202 }
203
204
205 }
210
211
212pub async fn map_result_fallback_os(res: Result<Report, ObjectError>,
213 db: &crate::db::Resolver)
214 -> Result<Report, anyhow::Error> {
215 match res {
216 Ok(rep) => {
217 if rep.symbols.is_empty() {
218 db.resolve(rep.addr.value() as _).await
219 } else {
220 Ok(rep)
221 }
222 },
223 Err(err) => Err(err.into()),
224 }
225}
226
227
228pub fn resolve_with_inlinees<'t, 'elf>(f: &'t Function<'elf>,
229 addr: Addr)
230 -> Option<Vec<ResolvedAddrRef<'t, 'elf>>> {
231 if f.address > addr || f.address + f.size <= addr {
232 return None;
233 }
234
235 let main = resolve(f, addr)?;
236 let mut results = Vec::with_capacity(3); for il in &f.inlinees {
239 if let Some(mut res) = resolve_with_inlinees(il, addr) {
240 results.append(&mut res);
241 }
242 }
243
244 results.push(main);
245 Some(results)
246}
247
248pub fn resolve<'t, 'elf>(f: &'t Function<'elf>, addr: Addr) -> Option<ResolvedAddrRef<'t, 'elf>> {
249 if f.address > addr || f.address + f.size <= addr {
250 return None;
251 }
252
253 let mut indices = Vec::new();
254
255 for (i, line) in f.lines.iter().enumerate() {
256 if line.address + line.size.unwrap_or(1) <= addr {
257 continue;
259 } else if line.address > addr {
260 break;
262 }
263 trace!("{addr:#08x}: found line {i}: ({})", line.file.name_str());
264 indices.push(i);
265 }
266
267 let result = ResolvedAddrRef::Fn { function: f,
268 lines: indices };
269 Some(result)
270}
271
272
273pub enum ResolvedAddrRef<'t, 'elf> {
274 Fn {
275 function: &'t Function<'elf>,
276 lines: Vec<usize>,
278 },
279 Sym(&'t Symbol<'elf>),
280}
281
282#[derive(Debug)]
283pub enum ResolvedAddr<'elf> {
284 Fn {
285 function: Function<'elf>,
286 lines: Vec<usize>,
288 },
289 Sym(&'elf Symbol<'elf>),
290}
291
292impl<'t> From<ResolvedAddrRef<'t, 't>> for ResolvedAddr<'t> {
293 fn from(value: ResolvedAddrRef<'t, 't>) -> Self {
294 match value {
295 ResolvedAddrRef::Fn { function, lines } => {
296 Self::Fn { function: function.to_owned(),
297 lines }
298 },
299 ResolvedAddrRef::Sym(sym) => Self::Sym(sym),
300 }
301 }
302}
303
304
305impl Report {
306 pub fn from_vec<T: Into<report::Symbol>>(addr: Addr, vec: Vec<T>) -> Self {
307 Self { addr,
308 symbols: vec.into_iter().map(Into::into).collect() }
309 }
310 pub fn from_slice<T: Into<report::Symbol>>(addr: Addr, slice: &[T]) -> Self
311 where for<'t> &'t T: Into<report::Symbol> {
312 Self { addr,
313 symbols: slice.into_iter().map(Into::into).collect() }
314 }
315 pub fn from_one<T: Into<report::Symbol>>(addr: Addr, result: T) -> Self {
316 Self { addr,
317 symbols: vec![result.into()] }
318 }
319}
320
321
322impl Into<report::Symbol> for ResolvedAddrRef<'_, '_> {
323 fn into(self) -> report::Symbol {
324 let owned: ResolvedAddr = self.into();
325 owned.into()
326 }
327}
328impl Into<report::Symbol> for ResolvedAddr<'_> {
329 fn into(self) -> report::Symbol { (&self).into() }
330}
331
332impl Into<report::Symbol> for &ResolvedAddr<'_> {
333 fn into(self) -> report::Symbol {
334 match self {
335 ResolvedAddr::Fn { function, lines } => {
336 let name = Name::new(
337 function.name.as_str().to_string(),
338 function.name.mangling(),
339 function.name.language(),
340 );
341 let lines = lines.into_iter()
342 .map(|l| &function.lines[*l])
343 .map(Into::into)
344 .collect::<Vec<_>>();
345 report::Symbol { hw_id: None,
346 name: Some(name),
347 address: function.address,
348 size: Some(function.size),
349 lines }
350 },
351
352 ResolvedAddr::Sym(sym) => {
353 let name = sym.name
354 .as_ref()
355 .map(|s| Cow::<'static, str>::Owned(s.to_string()))
356 .map(|name| Name::new(name, NameMangling::Unknown, Language::Rust));
357 let size = (sym.size != 0).then_some(sym.size);
358 report::Symbol { hw_id: None,
359 name,
360 address: sym.address,
361 size,
362 lines: Vec::with_capacity(0) }
363 },
364 }
365 }
366}
367
368
369impl<'elf> Into<report::Span> for &LineInfo<'elf> {
370 fn into(self) -> report::Span {
371 let line = (self.line != 0).then_some(self.line);
372 report::Span { hw_id: None,
373 address: self.address,
374 size: self.size,
375 file: self.file.path_str().into(),
376 line }
377 }
378}