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