1 use super::address_transform::AddressTransform;
2 use super::Reader;
3 use anyhow::Error;
4 use gimli::{write, AttributeValue, DebuggingInformationEntry, RangeListsOffset, Unit};
5 use wasmtime_environ::DefinedFuncIndex;
6 
7 pub(crate) enum RangeInfoBuilder {
8     Undefined,
9     Position(u64),
10     Ranges(Vec<(u64, u64)>),
11     Function(DefinedFuncIndex),
12 }
13 
14 impl 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 }
216