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