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::Data8(d) => write::AttributeValue::Data8(d), 104 AttributeValue::Sdata(d) => write::AttributeValue::Sdata(d), 105 AttributeValue::Flag(f) => write::AttributeValue::Flag(f), 106 AttributeValue::DebugLineRef(line_program_offset) => { 107 if let FileAttributeContext::Root(o) = file_context { 108 if o != Some(line_program_offset) { 109 return Err(TransformError("invalid debug_line offset").into()); 110 } 111 write::AttributeValue::LineProgramRef 112 } else { 113 return Err(TransformError("unexpected debug_line index attribute").into()); 114 } 115 } 116 AttributeValue::FileIndex(i) => { 117 if let FileAttributeContext::Children { 118 file_map, 119 file_index_base, 120 .. 121 } = file_context 122 { 123 let index = usize::try_from(i - file_index_base) 124 .ok() 125 .and_then(|i| file_map.get(i).copied()); 126 match index { 127 Some(index) => write::AttributeValue::FileIndex(Some(index)), 128 // This was seen to be invalid in #8884 and #8904 so 129 // ignore this seemingly invalid DWARF from LLVM 130 None => continue, 131 } 132 } else { 133 return Err(TransformError("unexpected file index attribute").into()); 134 } 135 } 136 AttributeValue::DebugStrRef(_) | AttributeValue::DebugStrOffsetsIndex(_) => { 137 let s = dwarf 138 .attr_string(unit, attr_value)? 139 .to_string_lossy()? 140 .into_owned(); 141 write::AttributeValue::StringRef(out_strings.add(s)) 142 } 143 AttributeValue::RangeListsRef(_) | AttributeValue::DebugRngListsIndex(_) => { 144 let r = dwarf.attr_ranges_offset(unit, attr_value)?.unwrap(); 145 let range_info = RangeInfoBuilder::from_ranges_ref(dwarf, unit, r)?; 146 let range_list_id = range_info.build_ranges(addr_tr, &mut out_unit.ranges); 147 write::AttributeValue::RangeListRef(range_list_id) 148 } 149 AttributeValue::LocationListsRef(_) | AttributeValue::DebugLocListsIndex(_) => { 150 let r = dwarf.attr_locations_offset(unit, attr_value)?.unwrap(); 151 let low_pc = 0; 152 let mut locs = dwarf.locations.locations( 153 r, 154 unit_encoding, 155 low_pc, 156 &dwarf.debug_addr, 157 unit.addr_base, 158 )?; 159 let frame_base = 160 if let FileAttributeContext::Children { frame_base, .. } = file_context { 161 frame_base 162 } else { 163 None 164 }; 165 166 let mut result: Option<Vec<_>> = None; 167 while let Some(loc) = locs.next()? { 168 if let Some(expr) = compile_expression(&loc.data, unit_encoding, frame_base)? { 169 let chunk = expr 170 .build_with_locals( 171 &[(loc.range.begin, loc.range.end)], 172 addr_tr, 173 frame_info, 174 isa, 175 ) 176 .filter(|i| { 177 // Ignore empty range 178 if let Ok((_, 0, _)) = i { 179 false 180 } else { 181 true 182 } 183 }) 184 .map(|i| { 185 i.map(|(start, len, expr)| write::Location::StartLength { 186 begin: start, 187 length: len, 188 data: expr, 189 }) 190 }) 191 .collect::<Result<Vec<_>, _>>()?; 192 match &mut result { 193 Some(r) => r.extend(chunk), 194 x @ None => *x = Some(chunk), 195 } 196 } else { 197 // FIXME _expr contains invalid expression 198 continue; // ignore entry 199 } 200 } 201 if result.is_none() { 202 continue; // no valid locations 203 } 204 let list_id = out_unit.locations.add(write::LocationList(result.unwrap())); 205 write::AttributeValue::LocationListRef(list_id) 206 } 207 AttributeValue::Exprloc(_) if attr.name() == gimli::DW_AT_frame_base => { 208 // We do not really "rewrite" the frame base so much as replace it outright. 209 // References to it through the DW_OP_fbreg opcode will be expanded below. 210 let mut cfa = write::Expression::new(); 211 cfa.op(gimli::DW_OP_call_frame_cfa); 212 write::AttributeValue::Exprloc(cfa) 213 } 214 AttributeValue::Exprloc(ref expr) => { 215 let frame_base = 216 if let FileAttributeContext::Children { frame_base, .. } = file_context { 217 frame_base 218 } else { 219 None 220 }; 221 if let Some(expr) = compile_expression(expr, unit_encoding, frame_base)? { 222 if expr.is_simple() { 223 if let Some(expr) = expr.build() { 224 write::AttributeValue::Exprloc(expr) 225 } else { 226 continue; 227 } 228 } else { 229 // Conversion to loclist is required. 230 if let Some(scope_ranges) = scope_ranges { 231 let exprs = expr 232 .build_with_locals(scope_ranges, addr_tr, frame_info, isa) 233 .collect::<Result<Vec<_>, _>>()?; 234 if exprs.is_empty() { 235 continue; 236 } 237 let found_single_expr = { 238 // Micro-optimization all expressions alike, use one exprloc. 239 let mut found_expr: Option<write::Expression> = None; 240 for (_, _, expr) in &exprs { 241 if let Some(ref prev_expr) = found_expr { 242 if expr == prev_expr { 243 continue; // the same expression 244 } 245 found_expr = None; 246 break; 247 } 248 found_expr = Some(expr.clone()) 249 } 250 found_expr 251 }; 252 if let Some(expr) = found_single_expr { 253 write::AttributeValue::Exprloc(expr) 254 } else if is_exprloc_to_loclist_allowed(attr.name()) { 255 // Converting exprloc to loclist. 256 let mut locs = Vec::new(); 257 for (begin, length, data) in exprs { 258 if length == 0 { 259 // Ignore empty range 260 continue; 261 } 262 locs.push(write::Location::StartLength { 263 begin, 264 length, 265 data, 266 }); 267 } 268 let list_id = out_unit.locations.add(write::LocationList(locs)); 269 write::AttributeValue::LocationListRef(list_id) 270 } else { 271 continue; 272 } 273 } else { 274 continue; 275 } 276 } 277 } else { 278 // FIXME _expr contains invalid expression 279 continue; // ignore attribute 280 } 281 } 282 AttributeValue::Encoding(e) => write::AttributeValue::Encoding(e), 283 AttributeValue::DecimalSign(e) => write::AttributeValue::DecimalSign(e), 284 AttributeValue::Endianity(e) => write::AttributeValue::Endianity(e), 285 AttributeValue::Accessibility(e) => write::AttributeValue::Accessibility(e), 286 AttributeValue::Visibility(e) => write::AttributeValue::Visibility(e), 287 AttributeValue::Virtuality(e) => write::AttributeValue::Virtuality(e), 288 AttributeValue::Language(e) => write::AttributeValue::Language(e), 289 AttributeValue::AddressClass(e) => write::AttributeValue::AddressClass(e), 290 AttributeValue::IdentifierCase(e) => write::AttributeValue::IdentifierCase(e), 291 AttributeValue::CallingConvention(e) => write::AttributeValue::CallingConvention(e), 292 AttributeValue::Inline(e) => write::AttributeValue::Inline(e), 293 AttributeValue::Ordering(e) => write::AttributeValue::Ordering(e), 294 AttributeValue::UnitRef(offset) => { 295 pending_die_refs.insert(current_scope_id, attr.name(), offset); 296 continue; 297 } 298 AttributeValue::DebugInfoRef(offset) => { 299 pending_di_refs.insert(current_scope_id, attr.name(), offset); 300 continue; 301 } 302 AttributeValue::String(d) => write::AttributeValue::String(d.to_slice()?.into_owned()), 303 a => bail!("Unexpected attribute: {:?}", a), 304 }; 305 let current_scope = out_unit.get_mut(current_scope_id); 306 current_scope.set(attr.name(), out_attr_value); 307 } 308 Ok(()) 309 } 310