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::{Reader, TransformError}; 6 use anyhow::{bail, Error}; 7 use cranelift_codegen::isa::TargetIsa; 8 use gimli::{write, AttributeValue, DebugLineOffset, DebuggingInformationEntry, Unit}; 9 10 #[derive(Debug)] 11 pub(crate) enum FileAttributeContext<'a> { 12 Root(Option<DebugLineOffset>), 13 Children { 14 file_map: &'a [write::FileId], 15 file_index_base: u64, 16 frame_base: Option<&'a CompiledExpression>, 17 }, 18 } 19 20 fn is_exprloc_to_loclist_allowed(attr_name: gimli::constants::DwAt) -> bool { 21 match attr_name { 22 gimli::DW_AT_location 23 | gimli::DW_AT_string_length 24 | gimli::DW_AT_return_addr 25 | gimli::DW_AT_data_member_location 26 | gimli::DW_AT_frame_base 27 | gimli::DW_AT_segment 28 | gimli::DW_AT_static_link 29 | gimli::DW_AT_use_location 30 | gimli::DW_AT_vtable_elem_location => true, 31 _ => false, 32 } 33 } 34 35 pub(crate) fn clone_die_attributes<'a, R>( 36 dwarf: &gimli::Dwarf<R>, 37 unit: &Unit<R, R::Offset>, 38 entry: &DebuggingInformationEntry<R>, 39 addr_tr: &'a AddressTransform, 40 frame_info: Option<&FunctionFrameInfo>, 41 out_unit: &mut write::Unit, 42 current_scope_id: write::UnitEntryId, 43 subprogram_range_builder: Option<RangeInfoBuilder>, 44 scope_ranges: Option<&Vec<(u64, u64)>>, 45 out_strings: &mut write::StringTable, 46 pending_die_refs: &mut PendingUnitRefs, 47 pending_di_refs: &mut PendingDebugInfoRefs, 48 file_context: FileAttributeContext<'a>, 49 isa: &dyn TargetIsa, 50 ) -> Result<(), Error> 51 where 52 R: Reader, 53 { 54 let unit_encoding = unit.encoding(); 55 56 let range_info = if let Some(subprogram_range_builder) = subprogram_range_builder { 57 subprogram_range_builder 58 } else { 59 // FIXME for CU: currently address_transform operate on a single 60 // function range, and when CU spans multiple ranges the 61 // transformation may be incomplete. 62 RangeInfoBuilder::from(dwarf, unit, entry)? 63 }; 64 range_info.build(addr_tr, out_unit, current_scope_id); 65 66 let mut attrs = entry.attrs(); 67 while let Some(attr) = attrs.next()? { 68 match attr.name() { 69 gimli::DW_AT_low_pc | gimli::DW_AT_high_pc | gimli::DW_AT_ranges => { 70 // Handled by RangeInfoBuilder. 71 continue; 72 } 73 gimli::DW_AT_str_offsets_base 74 | gimli::DW_AT_addr_base 75 | gimli::DW_AT_rnglists_base 76 | gimli::DW_AT_loclists_base 77 | gimli::DW_AT_dwo_name 78 | gimli::DW_AT_GNU_addr_base 79 | gimli::DW_AT_GNU_ranges_base 80 | gimli::DW_AT_GNU_dwo_name 81 | gimli::DW_AT_GNU_dwo_id => { 82 // DWARF encoding details that we don't need to copy. 83 continue; 84 } 85 _ => {} 86 } 87 let attr_value = attr.value(); 88 let out_attr_value = match attr_value { 89 AttributeValue::Addr(u) => { 90 let addr = addr_tr.translate(u).unwrap_or(write::Address::Constant(0)); 91 write::AttributeValue::Address(addr) 92 } 93 AttributeValue::DebugAddrIndex(i) => { 94 let u = dwarf.address(unit, i)?; 95 let addr = addr_tr.translate(u).unwrap_or(write::Address::Constant(0)); 96 write::AttributeValue::Address(addr) 97 } 98 AttributeValue::Block(d) => write::AttributeValue::Block(d.to_slice()?.into_owned()), 99 AttributeValue::Udata(u) => write::AttributeValue::Udata(u), 100 AttributeValue::Data1(d) => write::AttributeValue::Data1(d), 101 AttributeValue::Data2(d) => write::AttributeValue::Data2(d), 102 AttributeValue::Data4(d) => write::AttributeValue::Data4(d), 103 AttributeValue::Sdata(d) => write::AttributeValue::Sdata(d), 104 AttributeValue::Flag(f) => write::AttributeValue::Flag(f), 105 AttributeValue::DebugLineRef(line_program_offset) => { 106 if let FileAttributeContext::Root(o) = file_context { 107 if o != Some(line_program_offset) { 108 return Err(TransformError("invalid debug_line offset").into()); 109 } 110 write::AttributeValue::LineProgramRef 111 } else { 112 return Err(TransformError("unexpected debug_line index attribute").into()); 113 } 114 } 115 AttributeValue::FileIndex(i) => { 116 if let FileAttributeContext::Children { 117 file_map, 118 file_index_base, 119 .. 120 } = file_context 121 { 122 let index = usize::try_from(i - file_index_base) 123 .ok() 124 .and_then(|i| file_map.get(i).copied()); 125 match index { 126 Some(index) => write::AttributeValue::FileIndex(Some(index)), 127 // This was seen to be invalid in #8884 and #8904 so 128 // ignore this seemingly invalid DWARF from LLVM 129 None => continue, 130 } 131 } else { 132 return Err(TransformError("unexpected file index attribute").into()); 133 } 134 } 135 AttributeValue::DebugStrRef(_) | AttributeValue::DebugStrOffsetsIndex(_) => { 136 let s = dwarf 137 .attr_string(unit, attr_value)? 138 .to_string_lossy()? 139 .into_owned(); 140 write::AttributeValue::StringRef(out_strings.add(s)) 141 } 142 AttributeValue::RangeListsRef(_) | AttributeValue::DebugRngListsIndex(_) => { 143 let r = dwarf.attr_ranges_offset(unit, attr_value)?.unwrap(); 144 let range_info = RangeInfoBuilder::from_ranges_ref(dwarf, unit, r)?; 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(_) | AttributeValue::DebugLocListsIndex(_) => { 149 let r = dwarf.attr_locations_offset(unit, attr_value)?.unwrap(); 150 let low_pc = 0; 151 let mut locs = dwarf.locations.locations( 152 r, 153 unit_encoding, 154 low_pc, 155 &dwarf.debug_addr, 156 unit.addr_base, 157 )?; 158 let frame_base = 159 if let FileAttributeContext::Children { frame_base, .. } = file_context { 160 frame_base 161 } else { 162 None 163 }; 164 165 let mut result: Option<Vec<_>> = None; 166 while let Some(loc) = locs.next()? { 167 if let Some(expr) = compile_expression(&loc.data, unit_encoding, frame_base)? { 168 let chunk = expr 169 .build_with_locals( 170 &[(loc.range.begin, loc.range.end)], 171 addr_tr, 172 frame_info, 173 isa, 174 ) 175 .filter(|i| { 176 // Ignore empty range 177 if let Ok((_, 0, _)) = i { 178 false 179 } else { 180 true 181 } 182 }) 183 .map(|i| { 184 i.map(|(start, len, expr)| write::Location::StartLength { 185 begin: start, 186 length: len, 187 data: expr, 188 }) 189 }) 190 .collect::<Result<Vec<_>, _>>()?; 191 match &mut result { 192 Some(r) => r.extend(chunk), 193 x @ None => *x = Some(chunk), 194 } 195 } else { 196 // FIXME _expr contains invalid expression 197 continue; // ignore entry 198 } 199 } 200 if result.is_none() { 201 continue; // no valid locations 202 } 203 let list_id = out_unit.locations.add(write::LocationList(result.unwrap())); 204 write::AttributeValue::LocationListRef(list_id) 205 } 206 AttributeValue::Exprloc(_) if attr.name() == gimli::DW_AT_frame_base => { 207 // We do not really "rewrite" the frame base so much as replace it outright. 208 // References to it through the DW_OP_fbreg opcode will be expanded below. 209 let mut cfa = write::Expression::new(); 210 cfa.op(gimli::DW_OP_call_frame_cfa); 211 write::AttributeValue::Exprloc(cfa) 212 } 213 AttributeValue::Exprloc(ref expr) => { 214 let frame_base = 215 if let FileAttributeContext::Children { frame_base, .. } = file_context { 216 frame_base 217 } else { 218 None 219 }; 220 if let Some(expr) = compile_expression(expr, unit_encoding, frame_base)? { 221 if expr.is_simple() { 222 if let Some(expr) = expr.build() { 223 write::AttributeValue::Exprloc(expr) 224 } else { 225 continue; 226 } 227 } else { 228 // Conversion to loclist is required. 229 if let Some(scope_ranges) = scope_ranges { 230 let exprs = expr 231 .build_with_locals(scope_ranges, addr_tr, frame_info, isa) 232 .collect::<Result<Vec<_>, _>>()?; 233 if exprs.is_empty() { 234 continue; 235 } 236 let found_single_expr = { 237 // Micro-optimization all expressions alike, use one exprloc. 238 let mut found_expr: Option<write::Expression> = None; 239 for (_, _, expr) in &exprs { 240 if let Some(ref prev_expr) = found_expr { 241 if expr == prev_expr { 242 continue; // the same expression 243 } 244 found_expr = None; 245 break; 246 } 247 found_expr = Some(expr.clone()) 248 } 249 found_expr 250 }; 251 if let Some(expr) = found_single_expr { 252 write::AttributeValue::Exprloc(expr) 253 } else if is_exprloc_to_loclist_allowed(attr.name()) { 254 // Converting exprloc to loclist. 255 let mut locs = Vec::new(); 256 for (begin, length, data) in exprs { 257 if length == 0 { 258 // Ignore empty range 259 continue; 260 } 261 locs.push(write::Location::StartLength { 262 begin, 263 length, 264 data, 265 }); 266 } 267 let list_id = out_unit.locations.add(write::LocationList(locs)); 268 write::AttributeValue::LocationListRef(list_id) 269 } else { 270 continue; 271 } 272 } else { 273 continue; 274 } 275 } 276 } else { 277 // FIXME _expr contains invalid expression 278 continue; // ignore attribute 279 } 280 } 281 AttributeValue::Encoding(e) => write::AttributeValue::Encoding(e), 282 AttributeValue::DecimalSign(e) => write::AttributeValue::DecimalSign(e), 283 AttributeValue::Endianity(e) => write::AttributeValue::Endianity(e), 284 AttributeValue::Accessibility(e) => write::AttributeValue::Accessibility(e), 285 AttributeValue::Visibility(e) => write::AttributeValue::Visibility(e), 286 AttributeValue::Virtuality(e) => write::AttributeValue::Virtuality(e), 287 AttributeValue::Language(e) => write::AttributeValue::Language(e), 288 AttributeValue::AddressClass(e) => write::AttributeValue::AddressClass(e), 289 AttributeValue::IdentifierCase(e) => write::AttributeValue::IdentifierCase(e), 290 AttributeValue::CallingConvention(e) => write::AttributeValue::CallingConvention(e), 291 AttributeValue::Inline(e) => write::AttributeValue::Inline(e), 292 AttributeValue::Ordering(e) => write::AttributeValue::Ordering(e), 293 AttributeValue::UnitRef(offset) => { 294 pending_die_refs.insert(current_scope_id, attr.name(), offset); 295 continue; 296 } 297 AttributeValue::DebugInfoRef(offset) => { 298 pending_di_refs.insert(current_scope_id, attr.name(), offset); 299 continue; 300 } 301 AttributeValue::String(d) => write::AttributeValue::String(d.to_slice()?.into_owned()), 302 a => bail!("Unexpected attribute: {:?}", a), 303 }; 304 let current_scope = out_unit.get_mut(current_scope_id); 305 current_scope.set(attr.name(), out_attr_value); 306 } 307 Ok(()) 308 } 309