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