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