wasmtime_internal_cranelift/debug/transform/
attr.rs1use crate::debug::Reader;
2use crate::debug::transform::utils::resolve_die_ref;
3
4use super::address_transform::AddressTransform;
5use super::expression::{CompiledExpression, FunctionFrameInfo, compile_expression};
6use super::range_info_builder::RangeInfoBuilder;
7use super::refs::{PendingDebugInfoRefs, PendingUnitRefs};
8use super::unit::InheritedAttr;
9use super::{TransformError, dbi_log};
10use anyhow::{Error, bail};
11use cranelift_codegen::isa::TargetIsa;
12use gimli::{
13 AttributeValue, DebugLineOffset, DebuggingInformationEntry, Dwarf, Unit, UnitOffset, write,
14};
15
16#[derive(Debug)]
17pub(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)]
29pub struct SubprogramContext {
30 pub obj_ptr: UnitOffset,
31 pub param_num: isize,
32}
33
34fn 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
49pub(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 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 continue;
86 }
87 gimli::DW_AT_object_pointer => {
88 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 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 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 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 .expressions
245 .filter(|i| {
246 if let Ok((_, 0, _)) = i { false } else { true }
248 })
249 .map(|i| {
250 i.map(|(start, len, expr)| write::Location::StartLength {
251 begin: start,
252 length: len,
253 data: expr,
254 })
255 })
256 .collect::<Result<Vec<_>, _>>()?;
257 match &mut result {
258 Some(r) => r.extend(chunk),
259 x @ None => *x = Some(chunk),
260 }
261 } else {
262 continue; }
265 }
266 if result.is_none() {
267 continue; }
269 let list_id = out_unit.locations.add(write::LocationList(result.unwrap()));
270 write::AttributeValue::LocationListRef(list_id)
271 }
272 AttributeValue::Exprloc(_) if attr.name() == gimli::DW_AT_frame_base => {
273 let mut cfa = write::Expression::new();
276 cfa.op(gimli::DW_OP_call_frame_cfa);
277 write::AttributeValue::Exprloc(cfa)
278 }
279 AttributeValue::Exprloc(ref expr) => {
280 let frame_base =
281 if let EntryAttributesContext::Children { frame_base, .. } = attr_context {
282 frame_base
283 } else {
284 None
285 };
286 if let Some(expr) = compile_expression(expr, unit_encoding, frame_base)? {
287 if expr.is_simple() {
288 if let Some(expr) = expr.build() {
289 write::AttributeValue::Exprloc(expr)
290 } else {
291 continue;
292 }
293 } else {
294 if let Some(scope_ranges) = scope_ranges {
296 let built_expression =
297 expr.build_with_locals(scope_ranges, addr_tr, frame_info, isa);
298 let exprs = built_expression
299 .expressions
300 .collect::<Result<Vec<_>, _>>()?;
301 if exprs.is_empty() {
302 continue;
303 }
304 let mut single_expr: Option<write::Expression> = None;
306 if built_expression.covers_entire_scope {
307 for (_, _, expr) in &exprs {
308 if let Some(ref prev_expr) = single_expr {
309 if expr == prev_expr {
310 continue; }
312 single_expr = None;
313 break;
314 }
315 single_expr = Some(expr.clone())
316 }
317 }
318 if let Some(expr) = single_expr {
319 write::AttributeValue::Exprloc(expr)
320 } else if is_exprloc_to_loclist_allowed(attr.name()) {
321 let mut locs = Vec::new();
323 for (begin, length, data) in exprs {
324 if length == 0 {
325 continue;
327 }
328 locs.push(write::Location::StartLength {
329 begin,
330 length,
331 data,
332 });
333 }
334 let list_id = out_unit.locations.add(write::LocationList(locs));
335 write::AttributeValue::LocationListRef(list_id)
336 } else {
337 continue;
338 }
339 } else {
340 continue;
341 }
342 }
343 } else {
344 continue; }
347 }
348 AttributeValue::Encoding(e) => write::AttributeValue::Encoding(e),
349 AttributeValue::DecimalSign(e) => write::AttributeValue::DecimalSign(e),
350 AttributeValue::Endianity(e) => write::AttributeValue::Endianity(e),
351 AttributeValue::Accessibility(e) => write::AttributeValue::Accessibility(e),
352 AttributeValue::Visibility(e) => write::AttributeValue::Visibility(e),
353 AttributeValue::Virtuality(e) => write::AttributeValue::Virtuality(e),
354 AttributeValue::Language(e) => write::AttributeValue::Language(e),
355 AttributeValue::AddressClass(e) => write::AttributeValue::AddressClass(e),
356 AttributeValue::IdentifierCase(e) => write::AttributeValue::IdentifierCase(e),
357 AttributeValue::CallingConvention(e) => write::AttributeValue::CallingConvention(e),
358 AttributeValue::Inline(e) => write::AttributeValue::Inline(e),
359 AttributeValue::Ordering(e) => write::AttributeValue::Ordering(e),
360 AttributeValue::UnitRef(offset) => {
361 pending_die_refs.insert(out_entry_id, attr.name(), offset);
362 continue;
363 }
364 AttributeValue::DebugInfoRef(offset) => {
365 pending_di_refs.insert(out_entry_id, attr.name(), offset);
366 continue;
367 }
368 a => bail!("Unexpected attribute: {:?}", a),
369 };
370 let out_entry: &mut write::DebuggingInformationEntry = out_unit.get_mut(out_entry_id);
371 out_entry.set(attr.name(), out_attr_value);
372 }
373 Ok(())
374}
375
376fn prepare_die_context(
377 dwarf: &Dwarf<Reader<'_>>,
378 unit: &Unit<Reader<'_>>,
379 entry: &DebuggingInformationEntry<Reader<'_>>,
380 attr_context: &mut EntryAttributesContext<'_>,
381 is_obj_ptr: &mut bool,
382) -> Result<(), Error> {
383 let EntryAttributesContext::Children {
384 depth, subprograms, ..
385 } = attr_context
386 else {
387 return Ok(());
388 };
389
390 match entry.tag() {
392 gimli::DW_TAG_subprogram | gimli::DW_TAG_inlined_subroutine | gimli::DW_TAG_entry_point => {
393 subprograms.push(
395 *depth,
396 SubprogramContext {
397 obj_ptr: UnitOffset { 0: 0 },
398 param_num: -1,
399 },
400 );
401 }
402 gimli::DW_TAG_formal_parameter => {
403 if let Some(subprogram) = subprograms.top_with_depth_mut(*depth - 1) {
407 subprogram.param_num += 1;
408
409 if subprogram.obj_ptr == entry.offset()
410 || is_obj_ptr_param(dwarf, unit, entry, subprogram.param_num)?
411 {
412 *is_obj_ptr = true;
413 }
414 }
415 }
416 _ => {}
417 }
418 Ok(())
419}
420
421fn is_obj_ptr_param(
422 dwarf: &Dwarf<Reader<'_>>,
423 unit: &Unit<Reader<'_>>,
424 entry: &DebuggingInformationEntry<Reader<'_>>,
425 param_num: isize,
426) -> Result<bool, Error> {
427 debug_assert!(entry.tag() == gimli::DW_TAG_formal_parameter);
428
429 if param_num == 0
436 && entry.attr_value(gimli::DW_AT_artificial)? == Some(AttributeValue::Flag(true))
437 {
438 let name = entry.attr_value(gimli::DW_AT_name)?;
440 if name.is_none() || dwarf.attr_string(unit, name.unwrap())?.slice().eq(b"this") {
441 if let Some(type_attr) = entry.attr_value(gimli::DW_AT_type)? {
443 if let Some(type_die) = resolve_die_ref(unit, &type_attr)? {
444 return Ok(type_die.tag() == gimli::DW_TAG_pointer_type);
445 }
446 }
447 }
448 };
449
450 return Ok(false);
451}