wasmtime_cranelift/debug/transform/
range_info_builder.rs

1use super::address_transform::AddressTransform;
2use super::Reader;
3use anyhow::Error;
4use gimli::{write, AttributeValue, DebuggingInformationEntry, RangeListsOffset, Unit};
5use wasmtime_environ::DefinedFuncIndex;
6
7pub(crate) enum RangeInfoBuilder {
8    Undefined,
9    Position(u64),
10    Ranges(Vec<(u64, u64)>),
11    Function(DefinedFuncIndex),
12}
13
14impl RangeInfoBuilder {
15    pub(crate) fn from<R>(
16        dwarf: &gimli::Dwarf<R>,
17        unit: &Unit<R, R::Offset>,
18        entry: &DebuggingInformationEntry<R>,
19    ) -> Result<Self, Error>
20    where
21        R: Reader,
22    {
23        if let Some(AttributeValue::RangeListsRef(r)) = entry.attr_value(gimli::DW_AT_ranges)? {
24            let r = dwarf.ranges_offset_from_raw(unit, r);
25            return RangeInfoBuilder::from_ranges_ref(dwarf, unit, r);
26        };
27
28        let low_pc =
29            if let Some(AttributeValue::Addr(addr)) = entry.attr_value(gimli::DW_AT_low_pc)? {
30                addr
31            } else if let Some(AttributeValue::DebugAddrIndex(i)) =
32                entry.attr_value(gimli::DW_AT_low_pc)?
33            {
34                dwarf.address(unit, i)?
35            } else {
36                return Ok(RangeInfoBuilder::Undefined);
37            };
38
39        Ok(
40            if let Some(AttributeValue::Udata(u)) = entry.attr_value(gimli::DW_AT_high_pc)? {
41                RangeInfoBuilder::Ranges(vec![(low_pc, low_pc + u)])
42            } else {
43                RangeInfoBuilder::Position(low_pc)
44            },
45        )
46    }
47
48    pub(crate) fn from_ranges_ref<R>(
49        dwarf: &gimli::Dwarf<R>,
50        unit: &Unit<R, R::Offset>,
51        ranges: RangeListsOffset,
52    ) -> Result<Self, Error>
53    where
54        R: Reader,
55    {
56        let mut ranges = dwarf.ranges(unit, ranges)?;
57        let mut result = Vec::new();
58        while let Some(range) = ranges.next()? {
59            if range.begin >= range.end {
60                // ignore empty ranges
61            }
62            result.push((range.begin, range.end));
63        }
64
65        Ok(if result.is_empty() {
66            RangeInfoBuilder::Undefined
67        } else {
68            RangeInfoBuilder::Ranges(result)
69        })
70    }
71
72    pub(crate) fn from_subprogram_die<R>(
73        dwarf: &gimli::Dwarf<R>,
74        unit: &Unit<R, R::Offset>,
75        entry: &DebuggingInformationEntry<R>,
76        addr_tr: &AddressTransform,
77    ) -> Result<Self, Error>
78    where
79        R: Reader,
80    {
81        let addr =
82            if let Some(AttributeValue::Addr(addr)) = entry.attr_value(gimli::DW_AT_low_pc)? {
83                addr
84            } else if let Some(AttributeValue::DebugAddrIndex(i)) =
85                entry.attr_value(gimli::DW_AT_low_pc)?
86            {
87                dwarf.address(unit, i)?
88            } else if let Some(AttributeValue::RangeListsRef(r)) =
89                entry.attr_value(gimli::DW_AT_ranges)?
90            {
91                let r = dwarf.ranges_offset_from_raw(unit, r);
92                let mut ranges = dwarf.ranges(unit, r)?;
93                if let Some(range) = ranges.next()? {
94                    range.begin
95                } else {
96                    return Ok(RangeInfoBuilder::Undefined);
97                }
98            } else {
99                return Ok(RangeInfoBuilder::Undefined);
100            };
101
102        let index = addr_tr.find_func_index(addr);
103        if index.is_none() {
104            return Ok(RangeInfoBuilder::Undefined);
105        }
106        Ok(RangeInfoBuilder::Function(index.unwrap()))
107    }
108
109    pub(crate) fn build(
110        &self,
111        addr_tr: &AddressTransform,
112        out_unit: &mut write::Unit,
113        current_scope_id: write::UnitEntryId,
114    ) {
115        match self {
116            RangeInfoBuilder::Undefined => (),
117            RangeInfoBuilder::Position(pc) => {
118                let addr = addr_tr
119                    .translate(*pc)
120                    .unwrap_or(write::Address::Constant(0));
121                let current_scope = out_unit.get_mut(current_scope_id);
122                current_scope.set(gimli::DW_AT_low_pc, write::AttributeValue::Address(addr));
123            }
124            RangeInfoBuilder::Ranges(ranges) => {
125                let mut result = Vec::new();
126                for (begin, end) in ranges {
127                    result.extend(addr_tr.translate_ranges(*begin, *end));
128                }
129
130                // If we're seeing the ranges for a `DW_TAG_compile_unit` DIE
131                // then don't use `DW_AT_low_pc` and `DW_AT_high_pc`. These
132                // attributes, if set, will configure the base address of all
133                // location lists that this unit refers to. Currently this
134                // debug transform does not take this base address into account
135                // when generate the `.debug_loc` section. Consequently when a
136                // compile unit is configured here the `DW_AT_ranges` attribute
137                // is unconditionally used instead of
138                // `DW_AT_low_pc`/`DW_AT_high_pc`.
139                let is_attr_for_compile_unit =
140                    out_unit.get(current_scope_id).tag() == gimli::DW_TAG_compile_unit;
141
142                if result.len() != 1 || is_attr_for_compile_unit {
143                    let range_list = result
144                        .iter()
145                        .map(|tr| write::Range::StartLength {
146                            begin: tr.0,
147                            length: tr.1,
148                        })
149                        .collect::<Vec<_>>();
150                    let range_list_id = out_unit.ranges.add(write::RangeList(range_list));
151                    let current_scope = out_unit.get_mut(current_scope_id);
152                    current_scope.set(
153                        gimli::DW_AT_ranges,
154                        write::AttributeValue::RangeListRef(range_list_id),
155                    );
156                } else {
157                    let current_scope = out_unit.get_mut(current_scope_id);
158                    current_scope.set(
159                        gimli::DW_AT_low_pc,
160                        write::AttributeValue::Address(result[0].0),
161                    );
162                    current_scope.set(
163                        gimli::DW_AT_high_pc,
164                        write::AttributeValue::Udata(result[0].1),
165                    );
166                }
167            }
168            RangeInfoBuilder::Function(index) => {
169                let symbol = addr_tr.map()[*index].symbol;
170                let range = addr_tr.func_range(*index);
171                let addr = write::Address::Symbol {
172                    symbol,
173                    addend: range.0 as i64,
174                };
175                let len = (range.1 - range.0) as u64;
176                let current_scope = out_unit.get_mut(current_scope_id);
177                current_scope.set(gimli::DW_AT_low_pc, write::AttributeValue::Address(addr));
178                current_scope.set(gimli::DW_AT_high_pc, write::AttributeValue::Udata(len));
179            }
180        }
181    }
182
183    pub(crate) fn get_ranges(&self, addr_tr: &AddressTransform) -> Vec<(u64, u64)> {
184        match self {
185            RangeInfoBuilder::Undefined | RangeInfoBuilder::Position(_) => vec![],
186            RangeInfoBuilder::Ranges(ranges) => ranges.clone(),
187            RangeInfoBuilder::Function(index) => {
188                let range = addr_tr.func_source_range(*index);
189                vec![(range.0, range.1)]
190            }
191        }
192    }
193
194    pub(crate) fn build_ranges(
195        &self,
196        addr_tr: &AddressTransform,
197        out_range_lists: &mut write::RangeListTable,
198    ) -> write::RangeListId {
199        if let RangeInfoBuilder::Ranges(ranges) = self {
200            let mut range_list = Vec::new();
201            for (begin, end) in ranges {
202                assert!(begin < end);
203                range_list.extend(addr_tr.translate_ranges(*begin, *end).map(|tr| {
204                    write::Range::StartLength {
205                        begin: tr.0,
206                        length: tr.1,
207                    }
208                }));
209            }
210            out_range_lists.add(write::RangeList(range_list))
211        } else {
212            unreachable!();
213        }
214    }
215}