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