1use core::marker::PhantomData;
2
3use alloc::vec::Vec;
4use gimli::{
5 CfaRule, CieOrFde, DebugFrame, EhFrame, EhFrameHdr, Encoding, EndianSlice, Evaluation,
6 EvaluationResult, EvaluationStorage, Expression, LittleEndian, Location, ParsedEhFrameHdr,
7 Reader, ReaderOffset, Register, RegisterRule, UnwindContext, UnwindContextStorage,
8 UnwindOffset, UnwindSection, UnwindTableRow, Value,
9};
10
11pub(crate) use gimli::BaseAddresses;
12
13use crate::{arch::Arch, unwind_result::UnwindResult, ModuleSectionInfo};
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum DwarfUnwinderError {
17 FdeFromOffsetFailed(gimli::Error),
18 UnwindInfoForAddressFailed(gimli::Error),
19 StackPointerMovedBackwards,
20 DidNotAdvance,
21 CouldNotRecoverCfa,
22 CouldNotRecoverReturnAddress,
23 CouldNotRecoverFramePointer,
24}
25
26impl core::fmt::Display for DwarfUnwinderError {
27 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
28 match self {
29 Self::FdeFromOffsetFailed(err) => {
30 write!(f, "Could not get the FDE for the supplied offset: {err}")
31 }
32 Self::UnwindInfoForAddressFailed(err) => write!(
33 f,
34 "Could not find DWARF unwind info for the requested address: {err}"
35 ),
36 Self::StackPointerMovedBackwards => write!(f, "Stack pointer moved backwards"),
37 Self::DidNotAdvance => write!(f, "Did not advance"),
38 Self::CouldNotRecoverCfa => write!(f, "Could not recover the CFA"),
39 Self::CouldNotRecoverReturnAddress => write!(f, "Could not recover the return address"),
40 Self::CouldNotRecoverFramePointer => write!(f, "Could not recover the frame pointer"),
41 }
42 }
43}
44
45#[cfg(feature = "std")]
46impl std::error::Error for DwarfUnwinderError {
47 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
48 match self {
49 Self::FdeFromOffsetFailed(e) => Some(e),
50 Self::UnwindInfoForAddressFailed(e) => Some(e),
51 _ => None,
52 }
53 }
54}
55
56#[derive(Clone, Debug)]
57pub enum ConversionError {
58 CfaIsExpression,
59 CfaIsOffsetFromUnknownRegister,
60 ReturnAddressRuleWithUnexpectedOffset,
61 ReturnAddressRuleWasWeird,
62 SpOffsetDoesNotFit,
63 RegisterNotStoredRelativeToCfa,
64 RestoringFpButNotLr,
65 LrStorageOffsetDoesNotFit,
66 FpStorageOffsetDoesNotFit,
67 SpOffsetFromFpDoesNotFit,
68 FramePointerRuleDoesNotRestoreLr,
69 FramePointerRuleDoesNotRestoreFp,
70 FramePointerRuleDoesNotRestoreBp,
71 FramePointerRuleHasStrangeBpOffset,
72}
73
74pub trait DwarfUnwinding: Arch {
75 fn unwind_frame<F, R, UCS, ES>(
76 section: &impl UnwindSection<R>,
77 unwind_info: &UnwindTableRow<R::Offset, UCS>,
78 encoding: Encoding,
79 regs: &mut Self::UnwindRegs,
80 is_first_frame: bool,
81 read_stack: &mut F,
82 ) -> Result<UnwindResult<Self::UnwindRule>, DwarfUnwinderError>
83 where
84 F: FnMut(u64) -> Result<u64, ()>,
85 R: Reader,
86 UCS: UnwindContextStorage<R::Offset>,
87 ES: EvaluationStorage<R>;
88
89 fn rule_if_uncovered_by_fde() -> Self::UnwindRule;
90}
91
92pub enum UnwindSectionType {
93 EhFrame,
94 DebugFrame,
95}
96
97pub struct DwarfUnwinder<'a, R, A, UCS>
98where
99 R: Reader,
100 A: DwarfUnwinding,
101 UCS: UnwindContextStorage<R::Offset>,
102{
103 unwind_section_data: R,
104 unwind_section_type: UnwindSectionType,
105 eh_frame_hdr: Option<ParsedEhFrameHdr<EndianSlice<'a, R::Endian>>>,
106 unwind_context: &'a mut UnwindContext<R::Offset, UCS>,
107 base_svma: u64,
108 bases: BaseAddresses,
109 _arch: PhantomData<A>,
110}
111
112impl<'a, R, A, UCS> DwarfUnwinder<'a, R, A, UCS>
113where
114 R: Reader,
115 A: DwarfUnwinding,
116 UCS: UnwindContextStorage<R::Offset>,
117{
118 pub fn new(
119 unwind_section_data: R,
120 unwind_section_type: UnwindSectionType,
121 eh_frame_hdr_data: Option<&'a [u8]>,
122 unwind_context: &'a mut UnwindContext<R::Offset, UCS>,
123 bases: BaseAddresses,
124 base_svma: u64,
125 ) -> Self {
126 let eh_frame_hdr = match eh_frame_hdr_data {
127 Some(eh_frame_hdr_data) => {
128 let hdr = EhFrameHdr::new(eh_frame_hdr_data, unwind_section_data.endian());
129 hdr.parse(&bases, 8).ok()
130 }
131 None => None,
132 };
133 Self {
134 unwind_section_data,
135 unwind_section_type,
136 eh_frame_hdr,
137 unwind_context,
138 bases,
139 base_svma,
140 _arch: PhantomData,
141 }
142 }
143
144 pub fn get_fde_offset_for_relative_address(&self, rel_lookup_address: u32) -> Option<u32> {
145 let lookup_svma = self.base_svma + rel_lookup_address as u64;
146 let eh_frame_hdr = self.eh_frame_hdr.as_ref()?;
147 let table = eh_frame_hdr.table()?;
148 let fde_ptr = table.lookup(lookup_svma, &self.bases).ok()?;
149 let fde_offset = table.pointer_to_offset(fde_ptr).ok()?;
150 fde_offset.0.into_u64().try_into().ok()
151 }
152
153 pub fn unwind_frame_with_fde<F, ES>(
154 &mut self,
155 regs: &mut A::UnwindRegs,
156 is_first_frame: bool,
157 rel_lookup_address: u32,
158 fde_offset: u32,
159 read_stack: &mut F,
160 ) -> Result<UnwindResult<A::UnwindRule>, DwarfUnwinderError>
161 where
162 F: FnMut(u64) -> Result<u64, ()>,
163 ES: EvaluationStorage<R>,
164 {
165 let lookup_svma = self.base_svma + rel_lookup_address as u64;
166 let unwind_section_data = self.unwind_section_data.clone();
167 match self.unwind_section_type {
168 UnwindSectionType::EhFrame => {
169 let mut eh_frame = EhFrame::from(unwind_section_data);
170 eh_frame.set_address_size(8);
171 let unwind_info = self.unwind_info_for_fde(&eh_frame, lookup_svma, fde_offset);
172 if let Err(DwarfUnwinderError::UnwindInfoForAddressFailed(_)) = unwind_info {
173 return Ok(UnwindResult::ExecRule(A::rule_if_uncovered_by_fde()));
174 }
175 let (unwind_info, encoding) = unwind_info?;
176 A::unwind_frame::<F, R, UCS, ES>(
177 &eh_frame,
178 unwind_info,
179 encoding,
180 regs,
181 is_first_frame,
182 read_stack,
183 )
184 }
185 UnwindSectionType::DebugFrame => {
186 let mut debug_frame = DebugFrame::from(unwind_section_data);
187 debug_frame.set_address_size(8);
188 let unwind_info = self.unwind_info_for_fde(&debug_frame, lookup_svma, fde_offset);
189 if let Err(DwarfUnwinderError::UnwindInfoForAddressFailed(_)) = unwind_info {
190 return Ok(UnwindResult::ExecRule(A::rule_if_uncovered_by_fde()));
191 }
192 let (unwind_info, encoding) = unwind_info?;
193 A::unwind_frame::<F, R, UCS, ES>(
194 &debug_frame,
195 unwind_info,
196 encoding,
197 regs,
198 is_first_frame,
199 read_stack,
200 )
201 }
202 }
203 }
204
205 fn unwind_info_for_fde<US: UnwindSection<R>>(
206 &mut self,
207 unwind_section: &US,
208 lookup_svma: u64,
209 fde_offset: u32,
210 ) -> Result<(&UnwindTableRow<R::Offset, UCS>, Encoding), DwarfUnwinderError> {
211 let fde = unwind_section.fde_from_offset(
212 &self.bases,
213 US::Offset::from(R::Offset::from_u32(fde_offset)),
214 US::cie_from_offset,
215 );
216 let fde = fde.map_err(DwarfUnwinderError::FdeFromOffsetFailed)?;
217 let encoding = fde.cie().encoding();
218 let unwind_info: &UnwindTableRow<_, _> = fde
219 .unwind_info_for_address(
220 unwind_section,
221 &self.bases,
222 self.unwind_context,
223 lookup_svma,
224 )
225 .map_err(DwarfUnwinderError::UnwindInfoForAddressFailed)?;
226 Ok((unwind_info, encoding))
227 }
228}
229
230pub(crate) fn base_addresses_for_sections<D>(
231 section_info: &mut impl ModuleSectionInfo<D>,
232) -> BaseAddresses {
233 let mut start_addr = |names: &[&[u8]]| -> u64 {
234 names
235 .iter()
236 .find_map(|name| section_info.section_svma_range(name))
237 .map(|r| r.start)
238 .unwrap_or_default()
239 };
240 BaseAddresses::default()
241 .set_eh_frame(start_addr(&[b"__eh_frame", b".eh_frame"]))
242 .set_eh_frame_hdr(start_addr(&[b"__eh_frame_hdr", b".eh_frame_hdr"]))
243 .set_text(start_addr(&[b"__text", b".text"]))
244 .set_got(start_addr(&[b"__got", b".got"]))
245}
246
247#[derive(Debug, Clone, Copy, PartialEq, Eq)]
248pub enum DwarfCfiIndexError {
249 Gimli(gimli::Error),
250 CouldNotSubtractBaseAddress,
251 RelativeAddressTooBig,
252 FdeOffsetTooBig,
253}
254
255impl core::fmt::Display for DwarfCfiIndexError {
256 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
257 match self {
258 Self::Gimli(e) => write!(f, "EhFrame processing failed: {e}"),
259 Self::CouldNotSubtractBaseAddress => {
260 write!(f, "Could not subtract base address to create relative pc")
261 }
262 Self::RelativeAddressTooBig => write!(f, "Relative address did not fit into u32"),
263 Self::FdeOffsetTooBig => write!(f, "FDE offset did not fit into u32"),
264 }
265 }
266}
267
268impl From<gimli::Error> for DwarfCfiIndexError {
269 fn from(e: gimli::Error) -> Self {
270 Self::Gimli(e)
271 }
272}
273
274#[cfg(feature = "std")]
275impl std::error::Error for DwarfCfiIndexError {
276 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
277 match self {
278 Self::Gimli(e) => Some(e),
279 _ => None,
280 }
281 }
282}
283
284pub struct DwarfCfiIndex {
287 sorted_fde_pc_starts: Vec<u32>,
291 fde_offsets: Vec<u32>,
294}
295
296impl DwarfCfiIndex {
297 pub fn try_new<R, US>(
298 unwind_section: US,
299 bases: BaseAddresses,
300 base_svma: u64,
301 ) -> Result<Self, DwarfCfiIndexError>
302 where
303 R: Reader,
304 R::Offset: TryInto<u32>,
305 US: UnwindSection<R>,
306 {
307 let mut fde_pc_and_offset = Vec::new();
308
309 let mut cur_cie = None;
310 let mut entries_iter = unwind_section.entries(&bases);
311 while let Some(entry) = entries_iter.next()? {
312 let fde = match entry {
313 CieOrFde::Cie(cie) => {
314 cur_cie = Some(cie);
315 continue;
316 }
317 CieOrFde::Fde(partial_fde) => {
318 partial_fde.parse(|unwind_section, bases, cie_offset| {
319 if let Some(cie) = &cur_cie {
320 if cie.offset()
321 == <US::Offset as UnwindOffset<R::Offset>>::into(cie_offset)
322 {
323 return Ok(cie.clone());
324 }
325 }
326 let cie = unwind_section.cie_from_offset(bases, cie_offset);
327 if let Ok(cie) = &cie {
328 cur_cie = Some(cie.clone());
329 }
330 cie
331 })?
332 }
333 };
334 let pc = fde.initial_address();
335 let relative_pc = pc
336 .checked_sub(base_svma)
337 .ok_or(DwarfCfiIndexError::CouldNotSubtractBaseAddress)?;
338 let relative_pc = u32::try_from(relative_pc)
339 .map_err(|_| DwarfCfiIndexError::RelativeAddressTooBig)?;
340 let fde_offset = <R::Offset as TryInto<u32>>::try_into(fde.offset())
341 .map_err(|_| DwarfCfiIndexError::FdeOffsetTooBig)?;
342 fde_pc_and_offset.push((relative_pc, fde_offset));
343 }
344 fde_pc_and_offset.sort_by_key(|(pc, _)| *pc);
345 let sorted_fde_pc_starts = fde_pc_and_offset.iter().map(|(pc, _)| *pc).collect();
346 let fde_offsets = fde_pc_and_offset.into_iter().map(|(_, fde)| fde).collect();
347 Ok(Self {
348 sorted_fde_pc_starts,
349 fde_offsets,
350 })
351 }
352
353 pub fn try_new_eh_frame<D>(
354 eh_frame_data: &[u8],
355 section_info: &mut impl ModuleSectionInfo<D>,
356 ) -> Result<Self, DwarfCfiIndexError> {
357 let bases = base_addresses_for_sections(section_info);
358 let mut eh_frame = EhFrame::from(EndianSlice::new(eh_frame_data, LittleEndian));
359 eh_frame.set_address_size(8);
360
361 Self::try_new(eh_frame, bases, section_info.base_svma())
362 }
363
364 pub fn try_new_debug_frame<D>(
365 debug_frame_data: &[u8],
366 section_info: &mut impl ModuleSectionInfo<D>,
367 ) -> Result<Self, DwarfCfiIndexError> {
368 let bases = base_addresses_for_sections(section_info);
369 let mut debug_frame = DebugFrame::from(EndianSlice::new(debug_frame_data, LittleEndian));
370 debug_frame.set_address_size(8);
371
372 Self::try_new(debug_frame, bases, section_info.base_svma())
373 }
374
375 pub fn fde_offset_for_relative_address(&self, rel_lookup_address: u32) -> Option<u32> {
376 let i = match self.sorted_fde_pc_starts.binary_search(&rel_lookup_address) {
377 Err(0) => return None,
378 Ok(i) => i,
379 Err(i) => i - 1,
380 };
381 Some(self.fde_offsets[i])
382 }
383}
384
385pub trait DwarfUnwindRegs {
386 fn get(&self, register: Register) -> Option<u64>;
387}
388
389pub fn eval_cfa_rule<R: Reader, UR: DwarfUnwindRegs, S: EvaluationStorage<R>>(
390 section: &impl UnwindSection<R>,
391 rule: &CfaRule<R::Offset>,
392 encoding: Encoding,
393 regs: &UR,
394) -> Option<u64> {
395 match rule {
396 CfaRule::RegisterAndOffset { register, offset } => {
397 let val = regs.get(*register)?;
398 u64::try_from(i64::try_from(val).ok()?.checked_add(*offset)?).ok()
399 }
400 CfaRule::Expression(expr) => {
401 let expr = expr.get(section).ok()?;
402 eval_expr::<R, UR, S>(expr, encoding, regs)
403 }
404 }
405}
406
407fn eval_expr<R: Reader, UR: DwarfUnwindRegs, S: EvaluationStorage<R>>(
408 expr: Expression<R>,
409 encoding: Encoding,
410 regs: &UR,
411) -> Option<u64> {
412 let mut eval = Evaluation::<R, S>::new_in(expr.0, encoding);
413 let mut result = eval.evaluate().ok()?;
414 loop {
415 match result {
416 EvaluationResult::Complete => break,
417 EvaluationResult::RequiresRegister { register, .. } => {
418 let value = regs.get(register)?;
419 result = eval.resume_with_register(Value::Generic(value as _)).ok()?;
420 }
421 _ => return None,
422 }
423 }
424 let x = &eval.as_result().last()?.location;
425 if let Location::Address { address } = x {
426 Some(*address)
427 } else {
428 None
429 }
430}
431
432pub fn eval_register_rule<R, F, UR, S>(
433 section: &impl UnwindSection<R>,
434 rule: RegisterRule<R::Offset>,
435 cfa: u64,
436 encoding: Encoding,
437 val: u64,
438 regs: &UR,
439 read_stack: &mut F,
440) -> Option<u64>
441where
442 R: Reader,
443 F: FnMut(u64) -> Result<u64, ()>,
444 UR: DwarfUnwindRegs,
445 S: EvaluationStorage<R>,
446{
447 match rule {
448 RegisterRule::Undefined => None,
449 RegisterRule::SameValue => Some(val),
450 RegisterRule::Offset(offset) => {
451 let cfa_plus_offset =
452 u64::try_from(i64::try_from(cfa).ok()?.checked_add(offset)?).ok()?;
453 read_stack(cfa_plus_offset).ok()
454 }
455 RegisterRule::ValOffset(offset) => {
456 u64::try_from(i64::try_from(cfa).ok()?.checked_add(offset)?).ok()
457 }
458 RegisterRule::Register(register) => regs.get(register),
459 RegisterRule::Expression(expr) => {
460 let expr = expr.get(section).ok()?;
461 let val = eval_expr::<R, UR, S>(expr, encoding, regs)?;
462 read_stack(val).ok()
463 }
464 RegisterRule::ValExpression(expr) => {
465 let expr = expr.get(section).ok()?;
466 eval_expr::<R, UR, S>(expr, encoding, regs)
467 }
468 RegisterRule::Architectural => {
469 None
472 }
473 _ => None,
474 }
475}