1 use super::address_transform::AddressTransform; 2 use super::expression::{compile_expression, CompiledExpression, FunctionFrameInfo}; 3 use super::range_info_builder::RangeInfoBuilder; 4 use super::refs::{PendingDebugInfoRefs, PendingUnitRefs}; 5 use super::{DebugInputContext, Reader, TransformError}; 6 use anyhow::{bail, Error}; 7 use cranelift_codegen::isa::TargetIsa; 8 use gimli::{ 9 write, AttributeValue, DebugLineOffset, DebugLineStr, DebugStr, DebugStrOffsets, 10 DebuggingInformationEntry, Unit, 11 }; 12 13 #[derive(Debug)] 14 pub(crate) enum FileAttributeContext<'a> { 15 Root(Option<DebugLineOffset>), 16 Children { 17 file_map: &'a [write::FileId], 18 file_index_base: u64, 19 frame_base: Option<&'a CompiledExpression>, 20 }, 21 } 22 23 fn is_exprloc_to_loclist_allowed(attr_name: gimli::constants::DwAt) -> bool { 24 match attr_name { 25 gimli::DW_AT_location 26 | gimli::DW_AT_string_length 27 | gimli::DW_AT_return_addr 28 | gimli::DW_AT_data_member_location 29 | gimli::DW_AT_frame_base 30 | gimli::DW_AT_segment 31 | gimli::DW_AT_static_link 32 | gimli::DW_AT_use_location 33 | gimli::DW_AT_vtable_elem_location => true, 34 _ => false, 35 } 36 } 37 38 pub(crate) fn clone_die_attributes<'a, R>( 39 dwarf: &gimli::Dwarf<R>, 40 unit: &Unit<R, R::Offset>, 41 entry: &DebuggingInformationEntry<R>, 42 context: &DebugInputContext<R>, 43 addr_tr: &'a AddressTransform, 44 frame_info: Option<&FunctionFrameInfo>, 45 out_unit: &mut write::Unit, 46 current_scope_id: write::UnitEntryId, 47 subprogram_range_builder: Option<RangeInfoBuilder>, 48 scope_ranges: Option<&Vec<(u64, u64)>>, 49 cu_low_pc: u64, 50 out_strings: &mut write::StringTable, 51 pending_die_refs: &mut PendingUnitRefs, 52 pending_di_refs: &mut PendingDebugInfoRefs, 53 file_context: FileAttributeContext<'a>, 54 isa: &dyn TargetIsa, 55 ) -> Result<(), Error> 56 where 57 R: Reader, 58 { 59 let unit_encoding = unit.encoding(); 60 61 let range_info = if let Some(subprogram_range_builder) = subprogram_range_builder { 62 subprogram_range_builder 63 } else { 64 // FIXME for CU: currently address_transform operate on a single 65 // function range, and when CU spans multiple ranges the 66 // transformation may be incomplete. 67 RangeInfoBuilder::from(dwarf, unit, entry, context, cu_low_pc)? 68 }; 69 range_info.build(addr_tr, out_unit, current_scope_id); 70 71 let mut attrs = entry.attrs(); 72 while let Some(attr) = attrs.next()? { 73 let attr_value = match attr.value() { 74 AttributeValue::Addr(_) | AttributeValue::DebugAddrIndex(_) 75 if attr.name() == gimli::DW_AT_low_pc => 76 { 77 continue; 78 } 79 AttributeValue::Udata(_) if attr.name() == gimli::DW_AT_high_pc => { 80 continue; 81 } 82 AttributeValue::RangeListsRef(_) if attr.name() == gimli::DW_AT_ranges => { 83 continue; 84 } 85 AttributeValue::Exprloc(_) if attr.name() == gimli::DW_AT_frame_base => { 86 continue; 87 } 88 AttributeValue::DebugAddrBase(_) | AttributeValue::DebugStrOffsetsBase(_) => { 89 continue; 90 } 91 92 AttributeValue::Addr(u) => { 93 let addr = addr_tr.translate(u).unwrap_or(write::Address::Constant(0)); 94 write::AttributeValue::Address(addr) 95 } 96 AttributeValue::DebugAddrIndex(i) => { 97 let u = context.debug_addr.get_address(4, unit.addr_base, i)?; 98 let addr = addr_tr.translate(u).unwrap_or(write::Address::Constant(0)); 99 write::AttributeValue::Address(addr) 100 } 101 AttributeValue::Udata(u) => write::AttributeValue::Udata(u), 102 AttributeValue::Data1(d) => write::AttributeValue::Data1(d), 103 AttributeValue::Data2(d) => write::AttributeValue::Data2(d), 104 AttributeValue::Data4(d) => write::AttributeValue::Data4(d), 105 AttributeValue::Sdata(d) => write::AttributeValue::Sdata(d), 106 AttributeValue::Flag(f) => write::AttributeValue::Flag(f), 107 AttributeValue::DebugLineRef(line_program_offset) => { 108 if let FileAttributeContext::Root(o) = file_context { 109 if o != Some(line_program_offset) { 110 return Err(TransformError("invalid debug_line offset").into()); 111 } 112 write::AttributeValue::LineProgramRef 113 } else { 114 return Err(TransformError("unexpected debug_line index attribute").into()); 115 } 116 } 117 AttributeValue::FileIndex(i) => { 118 if let FileAttributeContext::Children { 119 file_map, 120 file_index_base, 121 .. 122 } = file_context 123 { 124 write::AttributeValue::FileIndex(Some(file_map[(i - file_index_base) as usize])) 125 } else { 126 return Err(TransformError("unexpected file index attribute").into()); 127 } 128 } 129 AttributeValue::DebugStrRef(str_offset) => { 130 let s = context.debug_str.get_str(str_offset)?.to_slice()?.to_vec(); 131 write::AttributeValue::StringRef(out_strings.add(s)) 132 } 133 AttributeValue::DebugStrOffsetsIndex(i) => { 134 let str_offset = context.debug_str_offsets.get_str_offset( 135 gimli::Format::Dwarf32, 136 unit.str_offsets_base, 137 i, 138 )?; 139 let s = context.debug_str.get_str(str_offset)?.to_slice()?.to_vec(); 140 write::AttributeValue::StringRef(out_strings.add(s)) 141 } 142 AttributeValue::RangeListsRef(r) => { 143 let r = dwarf.ranges_offset_from_raw(unit, r); 144 let range_info = RangeInfoBuilder::from_ranges_ref(unit, r, context, cu_low_pc)?; 145 let range_list_id = range_info.build_ranges(addr_tr, &mut out_unit.ranges); 146 write::AttributeValue::RangeListRef(range_list_id) 147 } 148 AttributeValue::LocationListsRef(r) => { 149 let low_pc = 0; 150 let mut locs = context.loclists.locations( 151 r, 152 unit_encoding, 153 low_pc, 154 &context.debug_addr, 155 unit.addr_base, 156 )?; 157 let frame_base = 158 if let FileAttributeContext::Children { frame_base, .. } = file_context { 159 frame_base 160 } else { 161 None 162 }; 163 164 let mut result: Option<Vec<_>> = None; 165 while let Some(loc) = locs.next()? { 166 if let Some(expr) = compile_expression(&loc.data, unit_encoding, frame_base)? { 167 let chunk = expr 168 .build_with_locals( 169 &[(loc.range.begin, loc.range.end)], 170 addr_tr, 171 frame_info, 172 isa, 173 ) 174 .filter(|i| { 175 // Ignore empty range 176 if let Ok((_, 0, _)) = i { 177 false 178 } else { 179 true 180 } 181 }) 182 .map(|i| { 183 i.map(|(start, len, expr)| write::Location::StartLength { 184 begin: start, 185 length: len, 186 data: expr, 187 }) 188 }) 189 .collect::<Result<Vec<_>, _>>()?; 190 match &mut result { 191 Some(r) => r.extend(chunk), 192 x @ None => *x = Some(chunk), 193 } 194 } else { 195 // FIXME _expr contains invalid expression 196 continue; // ignore entry 197 } 198 } 199 if result.is_none() { 200 continue; // no valid locations 201 } 202 let list_id = out_unit.locations.add(write::LocationList(result.unwrap())); 203 write::AttributeValue::LocationListRef(list_id) 204 } 205 AttributeValue::Exprloc(ref expr) => { 206 let frame_base = 207 if let FileAttributeContext::Children { frame_base, .. } = file_context { 208 frame_base 209 } else { 210 None 211 }; 212 if let Some(expr) = compile_expression(expr, unit_encoding, frame_base)? { 213 if expr.is_simple() { 214 if let Some(expr) = expr.build() { 215 write::AttributeValue::Exprloc(expr) 216 } else { 217 continue; 218 } 219 } else { 220 // Conversion to loclist is required. 221 if let Some(scope_ranges) = scope_ranges { 222 let exprs = expr 223 .build_with_locals(scope_ranges, addr_tr, frame_info, isa) 224 .collect::<Result<Vec<_>, _>>()?; 225 if exprs.is_empty() { 226 continue; 227 } 228 let found_single_expr = { 229 // Micro-optimization all expressions alike, use one exprloc. 230 let mut found_expr: Option<write::Expression> = None; 231 for (_, _, expr) in &exprs { 232 if let Some(ref prev_expr) = found_expr { 233 if expr == prev_expr { 234 continue; // the same expression 235 } 236 found_expr = None; 237 break; 238 } 239 found_expr = Some(expr.clone()) 240 } 241 found_expr 242 }; 243 if let Some(expr) = found_single_expr { 244 write::AttributeValue::Exprloc(expr) 245 } else if is_exprloc_to_loclist_allowed(attr.name()) { 246 // Converting exprloc to loclist. 247 let mut locs = Vec::new(); 248 for (begin, length, data) in exprs { 249 if length == 0 { 250 // Ignore empty range 251 continue; 252 } 253 locs.push(write::Location::StartLength { 254 begin, 255 length, 256 data, 257 }); 258 } 259 let list_id = out_unit.locations.add(write::LocationList(locs)); 260 write::AttributeValue::LocationListRef(list_id) 261 } else { 262 continue; 263 } 264 } else { 265 continue; 266 } 267 } 268 } else { 269 // FIXME _expr contains invalid expression 270 continue; // ignore attribute 271 } 272 } 273 AttributeValue::Encoding(e) => write::AttributeValue::Encoding(e), 274 AttributeValue::DecimalSign(e) => write::AttributeValue::DecimalSign(e), 275 AttributeValue::Endianity(e) => write::AttributeValue::Endianity(e), 276 AttributeValue::Accessibility(e) => write::AttributeValue::Accessibility(e), 277 AttributeValue::Visibility(e) => write::AttributeValue::Visibility(e), 278 AttributeValue::Virtuality(e) => write::AttributeValue::Virtuality(e), 279 AttributeValue::Language(e) => write::AttributeValue::Language(e), 280 AttributeValue::AddressClass(e) => write::AttributeValue::AddressClass(e), 281 AttributeValue::IdentifierCase(e) => write::AttributeValue::IdentifierCase(e), 282 AttributeValue::CallingConvention(e) => write::AttributeValue::CallingConvention(e), 283 AttributeValue::Inline(e) => write::AttributeValue::Inline(e), 284 AttributeValue::Ordering(e) => write::AttributeValue::Ordering(e), 285 AttributeValue::UnitRef(offset) => { 286 pending_die_refs.insert(current_scope_id, attr.name(), offset); 287 continue; 288 } 289 AttributeValue::DebugInfoRef(offset) => { 290 pending_di_refs.insert(current_scope_id, attr.name(), offset); 291 continue; 292 } 293 a => bail!("Unexpected attribute: {:?}", a), 294 }; 295 let current_scope = out_unit.get_mut(current_scope_id); 296 current_scope.set(attr.name(), attr_value); 297 } 298 Ok(()) 299 } 300 301 pub(crate) fn clone_attr_string<R>( 302 attr_value: &AttributeValue<R>, 303 form: gimli::DwForm, 304 unit: &Unit<R, R::Offset>, 305 debug_str: &DebugStr<R>, 306 debug_str_offsets: &DebugStrOffsets<R>, 307 debug_line_str: &DebugLineStr<R>, 308 out_strings: &mut write::StringTable, 309 ) -> Result<write::LineString, Error> 310 where 311 R: Reader, 312 { 313 let content = match attr_value { 314 AttributeValue::DebugStrRef(str_offset) => { 315 debug_str.get_str(*str_offset)?.to_slice()?.to_vec() 316 } 317 AttributeValue::DebugStrOffsetsIndex(i) => { 318 let str_offset = debug_str_offsets.get_str_offset( 319 gimli::Format::Dwarf32, 320 unit.str_offsets_base, 321 *i, 322 )?; 323 debug_str.get_str(str_offset)?.to_slice()?.to_vec() 324 } 325 AttributeValue::DebugLineStrRef(str_offset) => { 326 debug_line_str.get_str(*str_offset)?.to_slice()?.to_vec() 327 } 328 AttributeValue::String(b) => b.to_slice()?.to_vec(), 329 v => bail!("Unexpected attribute value: {:?}", v), 330 }; 331 Ok(match form { 332 gimli::DW_FORM_strp => { 333 let id = out_strings.add(content); 334 write::LineString::StringRef(id) 335 } 336 gimli::DW_FORM_string => write::LineString::String(content), 337 _ => bail!("DW_FORM_line_strp or other not supported"), 338 }) 339 } 340