rust_debug/
utils.rs

1use gimli::{
2    DebuggingInformationEntry, Dwarf, Error, Range, RangeIter, Reader, Unit, UnitOffset,
3    UnitSectionOffset,
4};
5use log::error;
6
7pub struct DwarfOffset {
8    pub section_offset: UnitSectionOffset,
9    pub unit_offset: UnitOffset,
10}
11
12/// Check if the given address is withing range of any of the given ranges.
13///
14/// Description:
15///
16/// * `pc` - A 32 bit machine code address, which is most commonly the current program counter value.
17/// * `rang` - A iterator over machine code address ranges.
18///
19/// It checks if the given address is within the range of each given address ranges.
20/// If the address is in range of one of them then it will return `Some(true)`, otherwise it will return
21/// `Some(false)`.
22/// The function will only return `None` if the address range iterator does not contain any address
23/// ranges.
24pub fn in_ranges<R>(pc: u32, rang: &mut RangeIter<R>) -> Option<bool>
25where
26    R: Reader<Offset = usize>,
27{
28    let mut no_range = true;
29    while let Ok(Some(range)) = rang.next() {
30        if in_range(pc, &range) {
31            return Some(true);
32        }
33        no_range = false;
34    }
35    if no_range {
36        return None;
37    }
38    Some(false)
39}
40
41/// Check if the given address is withing a range of addresses.
42///
43/// Description:
44///
45/// * `pc` - A 32 bit machine code address, which is most commonly the current program counter value.
46/// * `range` - A reference to a machine code address range.
47///
48/// It checks if the given address is within the range of machine code addresses.
49/// If the address is in range it will return `true`, otherwise `false`.
50/// return false.
51pub fn in_range(pc: u32, range: &Range) -> bool {
52    range.begin <= pc as u64 && range.end > pc as u64
53}
54
55/// Check if the given address is withing a DIEs address range.
56///
57/// Description:
58///
59/// * `dwarf` - A reference to gimli-rs Dwarf struct.
60/// * `unit` - A reference to a gimli-rs Unit struct, which contains the DIE to check.
61/// * `die` - A reference to a gimli-rs Die struct that will be checked.
62/// * `pc` - A 32 bit machine code address, which is most commonly the current program counter value.
63///
64/// It checks if the given address is within the address range of the given DIE.
65/// If the address is in range it will return `Some(true)`, otherwise it will return `Some(false)`.
66/// If the DIE has no address ranges it will return `None`.
67pub fn die_in_range<'a, R>(
68    dwarf: &'a Dwarf<R>,
69    unit: &'a Unit<R>,
70    die: &DebuggingInformationEntry<'_, '_, R>,
71    pc: u32,
72) -> Option<bool>
73where
74    R: Reader<Offset = usize>,
75{
76    match dwarf.die_ranges(unit, die) {
77        Ok(mut range) => in_ranges(pc, &mut range),
78        Err(_) => None,
79    }
80}
81
82/// Find a compilation unit(gimli-rs Unit) using a address.
83///
84/// Description:
85///
86/// * `dwarf` - A reference to gimli-rs Dwarf struct.
87/// * `pc` - A 32 bit machine code address, which is most commonly the current program counter value.
88///
89/// This function will check if the given address is within range of all the compilation units in the `.debug_info` DWARF section.
90/// If there is only one unit in range it will return it, otherwise it will return a error.
91pub fn get_current_unit<R>(dwarf: &'_ Dwarf<R>, pc: u32) -> Result<Unit<R>, Error>
92where
93    R: Reader<Offset = usize>,
94{
95    // TODO: Maybe return a Vec of units
96    let mut res = None;
97
98    let mut iter = dwarf.units();
99    let mut i = 0;
100    while let Some(header) = iter.next()? {
101        let unit = dwarf.unit(header)?;
102        if Some(true) == in_ranges(pc, &mut dwarf.unit_ranges(&unit)?) {
103            res = Some(unit);
104            i += 1;
105        }
106    }
107
108    if i > 1 {
109        error!("Found more then one unit in range {}", i);
110    }
111
112    match res {
113        Some(u) => Ok(u),
114        None => Err(Error::MissingUnitDie),
115    }
116}