1 use crate::debug::transform::utils::resolve_die_ref; 2 use crate::debug::Reader; 3 4 use super::address_transform::AddressTransform; 5 use super::expression::{compile_expression, CompiledExpression, FunctionFrameInfo}; 6 use super::range_info_builder::RangeInfoBuilder; 7 use super::refs::{PendingDebugInfoRefs, PendingUnitRefs}; 8 use super::unit::InheritedAttr; 9 use super::{dbi_log, TransformError}; 10 use anyhow::{bail, Error}; 11 use cranelift_codegen::isa::TargetIsa; 12 use gimli::{ 13 write, AttributeValue, DebugLineOffset, DebuggingInformationEntry, Dwarf, Unit, UnitOffset, 14 }; 15 16 #[derive(Debug)] 17 pub(crate) enum EntryAttributesContext<'a> { 18 Root(Option<DebugLineOffset>), 19 Children { 20 depth: usize, 21 subprograms: &'a mut InheritedAttr<SubprogramContext>, 22 file_map: &'a [write::FileId], 23 file_index_base: u64, 24 frame_base: Option<&'a CompiledExpression>, 25 }, 26 } 27 28 #[derive(Debug)] 29 pub struct SubprogramContext { 30 pub obj_ptr: UnitOffset, 31 pub param_num: isize, 32 } 33 34 fn is_exprloc_to_loclist_allowed(attr_name: gimli::constants::DwAt) -> bool { 35 match attr_name { 36 gimli::DW_AT_location 37 | gimli::DW_AT_string_length 38 | gimli::DW_AT_return_addr 39 | gimli::DW_AT_data_member_location 40 | gimli::DW_AT_frame_base 41 | gimli::DW_AT_segment 42 | gimli::DW_AT_static_link 43 | gimli::DW_AT_use_location 44 | gimli::DW_AT_vtable_elem_location => true, 45 _ => false, 46 } 47 } 48 49 pub(crate) fn clone_die_attributes<'a>( 50 dwarf: &gimli::Dwarf<Reader<'a>>, 51 unit: &Unit<Reader<'a>>, 52 entry: &DebuggingInformationEntry<Reader<'a>>, 53 addr_tr: &'a AddressTransform, 54 frame_info: Option<&FunctionFrameInfo>, 55 out_unit: &mut write::Unit, 56 out_entry_id: write::UnitEntryId, 57 subprogram_range_builder: Option<RangeInfoBuilder>, 58 scope_ranges: Option<&Vec<(u64, u64)>>, 59 out_strings: &mut write::StringTable, 60 pending_die_refs: &mut PendingUnitRefs, 61 pending_di_refs: &mut PendingDebugInfoRefs, 62 mut attr_context: EntryAttributesContext<'a>, 63 isa: &dyn TargetIsa, 64 ) -> Result<(), Error> { 65 let unit_encoding = unit.encoding(); 66 67 let range_info = if let Some(subprogram_range_builder) = subprogram_range_builder { 68 subprogram_range_builder 69 } else { 70 // FIXME for CU: currently address_transform operate on a single 71 // function range, and when CU spans multiple ranges the 72 // transformation may be incomplete. 73 RangeInfoBuilder::from(dwarf, unit, entry)? 74 }; 75 range_info.build(addr_tr, out_unit, out_entry_id); 76 77 let mut is_obj_ptr = false; 78 prepare_die_context(dwarf, unit, entry, &mut attr_context, &mut is_obj_ptr)?; 79 80 let mut attrs = entry.attrs(); 81 while let Some(attr) = attrs.next()? { 82 match attr.name() { 83 gimli::DW_AT_low_pc | gimli::DW_AT_high_pc | gimli::DW_AT_ranges => { 84 // Handled by RangeInfoBuilder. 85 continue; 86 } 87 gimli::DW_AT_object_pointer => { 88 // Our consumers cannot handle 'this' typed as a non-pointer (recall 89 // we translate all pointers to wrapper types), making it unusable. 90 // To remedy this, we 'strip' instance-ness off of methods by removing 91 // DW_AT_object_pointer and renaming 'this' to '__this'. 92 if let EntryAttributesContext::Children { 93 depth, 94 ref mut subprograms, 95 .. 96 } = attr_context 97 { 98 if let Some(ref mut subprogram) = subprograms.top_with_depth_mut(depth) { 99 // We expect this to reference a child entry in the same unit. 100 if let Some(unit_offs) = match attr.value() { 101 AttributeValue::DebugInfoRef(di_ref) => { 102 di_ref.to_unit_offset(&unit.header) 103 } 104 AttributeValue::UnitRef(unit_ref) => Some(unit_ref), 105 _ => None, 106 } { 107 subprogram.obj_ptr = unit_offs; 108 dbi_log!("Stripped DW_AT_object_pointer"); 109 continue; 110 } 111 } 112 } 113 } 114 gimli::DW_AT_str_offsets_base 115 | gimli::DW_AT_addr_base 116 | gimli::DW_AT_rnglists_base 117 | gimli::DW_AT_loclists_base 118 | gimli::DW_AT_dwo_name 119 | gimli::DW_AT_GNU_addr_base 120 | gimli::DW_AT_GNU_ranges_base 121 | gimli::DW_AT_GNU_dwo_name 122 | gimli::DW_AT_GNU_dwo_id => { 123 // DWARF encoding details that we don't need to copy. 124 continue; 125 } 126 _ => {} 127 } 128 129 if is_obj_ptr { 130 match attr.name() { 131 gimli::DW_AT_artificial => { 132 dbi_log!("Object pointer: stripped DW_AT_artificial"); 133 continue; 134 } 135 gimli::DW_AT_name => { 136 let old_name: &str = &dwarf.attr_string(unit, attr.value())?.to_string_lossy(); 137 let new_name = format!("__{old_name}"); 138 dbi_log!( 139 "Object pointer: renamed '{}' -> '{}'", 140 old_name, 141 new_name.as_str() 142 ); 143 144 let attr_value = write::AttributeValue::StringRef(out_strings.add(new_name)); 145 out_unit 146 .get_mut(out_entry_id) 147 .set(gimli::DW_AT_name, attr_value); 148 continue; 149 } 150 _ => {} 151 } 152 } 153 154 let attr_value = attr.value(); 155 let out_attr_value = match attr_value { 156 AttributeValue::Addr(u) => { 157 let addr = addr_tr.translate(u).unwrap_or(write::Address::Constant(0)); 158 write::AttributeValue::Address(addr) 159 } 160 AttributeValue::DebugAddrIndex(i) => { 161 let u = dwarf.address(unit, i)?; 162 let addr = addr_tr.translate(u).unwrap_or(write::Address::Constant(0)); 163 write::AttributeValue::Address(addr) 164 } 165 AttributeValue::Block(d) => write::AttributeValue::Block(d.to_vec()), 166 AttributeValue::Udata(u) => write::AttributeValue::Udata(u), 167 AttributeValue::Data1(d) => write::AttributeValue::Data1(d), 168 AttributeValue::Data2(d) => write::AttributeValue::Data2(d), 169 AttributeValue::Data4(d) => write::AttributeValue::Data4(d), 170 AttributeValue::Data8(d) => write::AttributeValue::Data8(d), 171 AttributeValue::Sdata(d) => write::AttributeValue::Sdata(d), 172 AttributeValue::Flag(f) => write::AttributeValue::Flag(f), 173 AttributeValue::DebugLineRef(line_program_offset) => { 174 if let EntryAttributesContext::Root(o) = attr_context { 175 if o != Some(line_program_offset) { 176 return Err(TransformError("invalid debug_line offset").into()); 177 } 178 write::AttributeValue::LineProgramRef 179 } else { 180 return Err(TransformError("unexpected debug_line index attribute").into()); 181 } 182 } 183 AttributeValue::FileIndex(i) => { 184 if let EntryAttributesContext::Children { 185 file_map, 186 file_index_base, 187 .. 188 } = attr_context 189 { 190 let index = usize::try_from(i - file_index_base) 191 .ok() 192 .and_then(|i| file_map.get(i).copied()); 193 match index { 194 Some(index) => write::AttributeValue::FileIndex(Some(index)), 195 // This was seen to be invalid in #8884 and #8904 so 196 // ignore this seemingly invalid DWARF from LLVM 197 None => continue, 198 } 199 } else { 200 return Err(TransformError("unexpected file index attribute").into()); 201 } 202 } 203 AttributeValue::String(d) => write::AttributeValue::String(d.to_vec()), 204 AttributeValue::DebugStrRef(_) | AttributeValue::DebugStrOffsetsIndex(_) => { 205 let s = dwarf 206 .attr_string(unit, attr_value)? 207 .to_string_lossy() 208 .into_owned(); 209 write::AttributeValue::StringRef(out_strings.add(s)) 210 } 211 AttributeValue::RangeListsRef(_) | AttributeValue::DebugRngListsIndex(_) => { 212 let r = dwarf.attr_ranges_offset(unit, attr_value)?.unwrap(); 213 let range_info = RangeInfoBuilder::from_ranges_ref(dwarf, unit, r)?; 214 let range_list_id = range_info.build_ranges(addr_tr, &mut out_unit.ranges); 215 write::AttributeValue::RangeListRef(range_list_id) 216 } 217 AttributeValue::LocationListsRef(_) | AttributeValue::DebugLocListsIndex(_) => { 218 let r = dwarf.attr_locations_offset(unit, attr_value)?.unwrap(); 219 let low_pc = 0; 220 let mut locs = dwarf.locations.locations( 221 r, 222 unit_encoding, 223 low_pc, 224 &dwarf.debug_addr, 225 unit.addr_base, 226 )?; 227 let frame_base = 228 if let EntryAttributesContext::Children { frame_base, .. } = attr_context { 229 frame_base 230 } else { 231 None 232 }; 233 234 let mut result: Option<Vec<_>> = None; 235 while let Some(loc) = locs.next()? { 236 if let Some(expr) = compile_expression(&loc.data, unit_encoding, frame_base)? { 237 let chunk = expr 238 .build_with_locals( 239 &[(loc.range.begin, loc.range.end)], 240 addr_tr, 241 frame_info, 242 isa, 243 ) 244 .filter(|i| { 245 // Ignore empty range 246 if let Ok((_, 0, _)) = i { 247 false 248 } else { 249 true 250 } 251 }) 252 .map(|i| { 253 i.map(|(start, len, expr)| write::Location::StartLength { 254 begin: start, 255 length: len, 256 data: expr, 257 }) 258 }) 259 .collect::<Result<Vec<_>, _>>()?; 260 match &mut result { 261 Some(r) => r.extend(chunk), 262 x @ None => *x = Some(chunk), 263 } 264 } else { 265 // FIXME _expr contains invalid expression 266 continue; // ignore entry 267 } 268 } 269 if result.is_none() { 270 continue; // no valid locations 271 } 272 let list_id = out_unit.locations.add(write::LocationList(result.unwrap())); 273 write::AttributeValue::LocationListRef(list_id) 274 } 275 AttributeValue::Exprloc(_) if attr.name() == gimli::DW_AT_frame_base => { 276 // We do not really "rewrite" the frame base so much as replace it outright. 277 // References to it through the DW_OP_fbreg opcode will be expanded below. 278 let mut cfa = write::Expression::new(); 279 cfa.op(gimli::DW_OP_call_frame_cfa); 280 write::AttributeValue::Exprloc(cfa) 281 } 282 AttributeValue::Exprloc(ref expr) => { 283 let frame_base = 284 if let EntryAttributesContext::Children { frame_base, .. } = attr_context { 285 frame_base 286 } else { 287 None 288 }; 289 if let Some(expr) = compile_expression(expr, unit_encoding, frame_base)? { 290 if expr.is_simple() { 291 if let Some(expr) = expr.build() { 292 write::AttributeValue::Exprloc(expr) 293 } else { 294 continue; 295 } 296 } else { 297 // Conversion to loclist is required. 298 if let Some(scope_ranges) = scope_ranges { 299 let exprs = expr 300 .build_with_locals(scope_ranges, addr_tr, frame_info, isa) 301 .collect::<Result<Vec<_>, _>>()?; 302 if exprs.is_empty() { 303 continue; 304 } 305 let found_single_expr = { 306 // Micro-optimization all expressions alike, use one exprloc. 307 let mut found_expr: Option<write::Expression> = None; 308 for (_, _, expr) in &exprs { 309 if let Some(ref prev_expr) = found_expr { 310 if expr == prev_expr { 311 continue; // the same expression 312 } 313 found_expr = None; 314 break; 315 } 316 found_expr = Some(expr.clone()) 317 } 318 found_expr 319 }; 320 if let Some(expr) = found_single_expr { 321 write::AttributeValue::Exprloc(expr) 322 } else if is_exprloc_to_loclist_allowed(attr.name()) { 323 // Converting exprloc to loclist. 324 let mut locs = Vec::new(); 325 for (begin, length, data) in exprs { 326 if length == 0 { 327 // Ignore empty range 328 continue; 329 } 330 locs.push(write::Location::StartLength { 331 begin, 332 length, 333 data, 334 }); 335 } 336 let list_id = out_unit.locations.add(write::LocationList(locs)); 337 write::AttributeValue::LocationListRef(list_id) 338 } else { 339 continue; 340 } 341 } else { 342 continue; 343 } 344 } 345 } else { 346 // FIXME _expr contains invalid expression 347 continue; // ignore attribute 348 } 349 } 350 AttributeValue::Encoding(e) => write::AttributeValue::Encoding(e), 351 AttributeValue::DecimalSign(e) => write::AttributeValue::DecimalSign(e), 352 AttributeValue::Endianity(e) => write::AttributeValue::Endianity(e), 353 AttributeValue::Accessibility(e) => write::AttributeValue::Accessibility(e), 354 AttributeValue::Visibility(e) => write::AttributeValue::Visibility(e), 355 AttributeValue::Virtuality(e) => write::AttributeValue::Virtuality(e), 356 AttributeValue::Language(e) => write::AttributeValue::Language(e), 357 AttributeValue::AddressClass(e) => write::AttributeValue::AddressClass(e), 358 AttributeValue::IdentifierCase(e) => write::AttributeValue::IdentifierCase(e), 359 AttributeValue::CallingConvention(e) => write::AttributeValue::CallingConvention(e), 360 AttributeValue::Inline(e) => write::AttributeValue::Inline(e), 361 AttributeValue::Ordering(e) => write::AttributeValue::Ordering(e), 362 AttributeValue::UnitRef(offset) => { 363 pending_die_refs.insert(out_entry_id, attr.name(), offset); 364 continue; 365 } 366 AttributeValue::DebugInfoRef(offset) => { 367 pending_di_refs.insert(out_entry_id, attr.name(), offset); 368 continue; 369 } 370 a => bail!("Unexpected attribute: {:?}", a), 371 }; 372 let out_entry: &mut write::DebuggingInformationEntry = out_unit.get_mut(out_entry_id); 373 out_entry.set(attr.name(), out_attr_value); 374 } 375 Ok(()) 376 } 377 378 fn prepare_die_context( 379 dwarf: &Dwarf<Reader<'_>>, 380 unit: &Unit<Reader<'_>>, 381 entry: &DebuggingInformationEntry<Reader<'_>>, 382 attr_context: &mut EntryAttributesContext<'_>, 383 is_obj_ptr: &mut bool, 384 ) -> Result<(), Error> { 385 let EntryAttributesContext::Children { 386 depth, subprograms, .. 387 } = attr_context 388 else { 389 return Ok(()); 390 }; 391 392 // Update the current context based on what kind of entry this is. 393 match entry.tag() { 394 gimli::DW_TAG_subprogram | gimli::DW_TAG_inlined_subroutine | gimli::DW_TAG_entry_point => { 395 // Push the 'context' of there being no parameters (yet). 396 subprograms.push( 397 *depth, 398 SubprogramContext { 399 obj_ptr: UnitOffset { 0: 0 }, 400 param_num: -1, 401 }, 402 ); 403 } 404 gimli::DW_TAG_formal_parameter => { 405 // Formal parameter tags can be parented by catch blocks 406 // and such - not just subprogram DIEs. So we need to check 407 // that this DIE is indeed a direct child of a subprogram. 408 if let Some(subprogram) = subprograms.top_with_depth_mut(*depth - 1) { 409 subprogram.param_num += 1; 410 411 if subprogram.obj_ptr == entry.offset() 412 || is_obj_ptr_param(dwarf, unit, entry, subprogram.param_num)? 413 { 414 *is_obj_ptr = true; 415 } 416 } 417 } 418 _ => {} 419 } 420 Ok(()) 421 } 422 423 fn is_obj_ptr_param( 424 dwarf: &Dwarf<Reader<'_>>, 425 unit: &Unit<Reader<'_>>, 426 entry: &DebuggingInformationEntry<Reader<'_>>, 427 param_num: isize, 428 ) -> Result<bool, Error> { 429 debug_assert!(entry.tag() == gimli::DW_TAG_formal_parameter); 430 431 // This logic was taken loosely from LLDB. It is known 432 // that it is not fully correct (doesn't handle 'deduced 433 // this', for example). 434 // Q: DWARF includes DW_AT_object_pointer as we use it, 435 // why do we need this heuristic as well? 436 // A: Declarations do not include DW_AT_object_pointer. 437 if param_num == 0 438 && entry.attr_value(gimli::DW_AT_artificial)? == Some(AttributeValue::Flag(true)) 439 { 440 // Either this has no name (declarations omit them), or its explicitly "this". 441 let name = entry.attr_value(gimli::DW_AT_name)?; 442 if name.is_none() || dwarf.attr_string(unit, name.unwrap())?.slice().eq(b"this") { 443 // Finally, a type check. We expect a pointer. 444 if let Some(type_attr) = entry.attr_value(gimli::DW_AT_type)? { 445 if let Some(type_die) = resolve_die_ref(unit, &type_attr)? { 446 return Ok(type_die.tag() == gimli::DW_TAG_pointer_type); 447 } 448 } 449 } 450 }; 451 452 return Ok(false); 453 } 454