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