1use super::address_transform::AddressTransform;
2use super::attr::{clone_die_attributes, EntryAttributesContext};
3use super::debug_transform_logging::{
4 dbi_log, log_begin_input_die, log_end_output_die, log_end_output_die_skipped,
5 log_get_cu_summary,
6};
7use super::expression::compile_expression;
8use super::line_program::clone_line_program;
9use super::range_info_builder::RangeInfoBuilder;
10use super::refs::{PendingDebugInfoRefs, PendingUnitRefs, UnitRefsMap};
11use super::synthetic::ModuleSyntheticUnit;
12use super::utils::{append_vmctx_info, resolve_die_ref};
13use super::DebugInputContext;
14use crate::debug::{Compilation, Reader};
15use anyhow::{Context, Error};
16use cranelift_codegen::ir::Endianness;
17use cranelift_codegen::isa::TargetIsa;
18use gimli::write;
19use gimli::{AttributeValue, DebuggingInformationEntry, Dwarf, Unit};
20use std::collections::HashSet;
21use wasmtime_environ::StaticModuleIndex;
22use wasmtime_versioned_export_macros::versioned_stringify_ident;
23
24#[derive(Debug)]
25pub struct InheritedAttr<T> {
26 stack: Vec<(usize, T)>,
27}
28
29impl<T> InheritedAttr<T> {
30 fn new() -> Self {
31 InheritedAttr { stack: Vec::new() }
32 }
33
34 fn update(&mut self, depth: usize) {
35 while !self.stack.is_empty() && self.stack.last().unwrap().0 >= depth {
36 self.stack.pop();
37 }
38 }
39
40 pub fn push(&mut self, depth: usize, value: T) {
41 self.stack.push((depth, value));
42 }
43
44 pub fn top(&self) -> Option<&T> {
45 self.stack.last().map(|entry| &entry.1)
46 }
47
48 pub fn top_with_depth_mut(&mut self, depth: usize) -> Option<&mut T> {
49 self.stack
50 .last_mut()
51 .filter(|entry| entry.0 == depth)
52 .map(|entry| &mut entry.1)
53 }
54
55 fn is_empty(&self) -> bool {
56 self.stack.is_empty()
57 }
58}
59
60fn get_base_type_name(
61 type_entry: &DebuggingInformationEntry<Reader<'_>>,
62 unit: &Unit<Reader<'_>>,
63 dwarf: &Dwarf<Reader<'_>>,
64) -> Result<String, Error> {
65 if let Some(die_ref) = type_entry.attr_value(gimli::DW_AT_type)? {
67 if let Some(ref die) = resolve_die_ref(unit, &die_ref)? {
68 if let Some(value) = die.attr_value(gimli::DW_AT_name)? {
69 return Ok(String::from(dwarf.attr_string(unit, value)?.to_string()?));
70 }
71 match die.tag() {
72 gimli::DW_TAG_const_type => {
73 return Ok(format!("const {}", get_base_type_name(die, unit, dwarf)?));
74 }
75 gimli::DW_TAG_pointer_type => {
76 return Ok(format!("{}*", get_base_type_name(die, unit, dwarf)?));
77 }
78 gimli::DW_TAG_reference_type => {
79 return Ok(format!("{}&", get_base_type_name(die, unit, dwarf)?));
80 }
81 gimli::DW_TAG_array_type => {
82 return Ok(format!("{}[]", get_base_type_name(die, unit, dwarf)?));
83 }
84 _ => (),
85 }
86 }
87 }
88 Ok(String::from("??"))
89}
90
91enum WebAssemblyPtrKind {
92 Reference,
93 Pointer,
94}
95
96fn replace_pointer_type(
114 parent_id: write::UnitEntryId,
115 kind: WebAssemblyPtrKind,
116 comp_unit: &mut write::Unit,
117 wasm_ptr_die_ref: write::Reference,
118 pointer_type_entry: &DebuggingInformationEntry<Reader<'_>>,
119 unit: &Unit<Reader<'_>, usize>,
120 dwarf: &Dwarf<Reader<'_>>,
121 out_strings: &mut write::StringTable,
122 pending_die_refs: &mut PendingUnitRefs,
123) -> Result<write::UnitEntryId, Error> {
124 const WASM_PTR_LEN: u8 = 4;
125
126 macro_rules! add_tag {
127 ($parent_id:ident, $tag:expr => $die:ident as $die_id:ident { $($a:path = $v:expr),* }) => {
128 let $die_id = comp_unit.add($parent_id, $tag);
129 #[allow(unused_variables, reason = "sometimes not used below")]
130 let $die = comp_unit.get_mut($die_id);
131 $( $die.set($a, $v); )*
132 };
133 }
134
135 let name = match kind {
139 WebAssemblyPtrKind::Pointer => format!(
140 "WebAssemblyPtrWrapper<{}>",
141 get_base_type_name(pointer_type_entry, unit, dwarf)?
142 ),
143 WebAssemblyPtrKind::Reference => format!(
144 "WebAssemblyRefWrapper<{}>",
145 get_base_type_name(pointer_type_entry, unit, dwarf)?
146 ),
147 };
148 add_tag!(parent_id, gimli::DW_TAG_structure_type => wrapper_die as wrapper_die_id {
149 gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add(name.as_str())),
150 gimli::DW_AT_byte_size = write::AttributeValue::Data1(WASM_PTR_LEN)
151 });
152
153 add_tag!(parent_id, gimli::DW_TAG_pointer_type => wrapper_ptr_type as wrapper_ptr_type_id {
156 gimli::DW_AT_type = write::AttributeValue::UnitRef(wrapper_die_id)
157 });
158
159 let base_type_id = pointer_type_entry.attr_value(gimli::DW_AT_type)?;
160 add_tag!(parent_id, gimli::DW_TAG_reference_type => ref_type as ref_type_id {});
163 if let Some(AttributeValue::UnitRef(ref offset)) = base_type_id {
164 pending_die_refs.insert(ref_type_id, gimli::DW_AT_type, *offset);
165 }
166
167 add_tag!(parent_id, gimli::DW_TAG_pointer_type => ptr_type as ptr_type_id {});
170 if let Some(AttributeValue::UnitRef(ref offset)) = base_type_id {
171 pending_die_refs.insert(ptr_type_id, gimli::DW_AT_type, *offset);
172 }
173
174 add_tag!(wrapper_die_id, gimli::DW_TAG_template_type_parameter => t_param_die as t_param_die_id {
178 gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("T"))
179 });
180 if let Some(AttributeValue::UnitRef(ref offset)) = base_type_id {
181 pending_die_refs.insert(t_param_die_id, gimli::DW_AT_type, *offset);
182 }
183
184 add_tag!(wrapper_die_id, gimli::DW_TAG_member => m_die as m_die_id {
189 gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("__ptr")),
190 gimli::DW_AT_type = write::AttributeValue::DebugInfoRef(wasm_ptr_die_ref),
191 gimli::DW_AT_data_member_location = write::AttributeValue::Data1(0)
192 });
193
194 add_tag!(wrapper_die_id, gimli::DW_TAG_subprogram => deref_op_die as deref_op_die_id {
202 gimli::DW_AT_linkage_name = write::AttributeValue::StringRef(out_strings.add(versioned_stringify_ident!(wasmtime_resolve_vmctx_memory_ptr))),
203 gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("ptr")),
204 gimli::DW_AT_type = write::AttributeValue::UnitRef(ptr_type_id)
205 });
206 add_tag!(deref_op_die_id, gimli::DW_TAG_formal_parameter => deref_op_this_param as deref_op_this_param_id {
207 gimli::DW_AT_type = write::AttributeValue::UnitRef(wrapper_ptr_type_id),
208 gimli::DW_AT_artificial = write::AttributeValue::Flag(true)
209 });
210
211 add_tag!(wrapper_die_id, gimli::DW_TAG_subprogram => deref_op_die as deref_op_die_id {
219 gimli::DW_AT_linkage_name = write::AttributeValue::StringRef(out_strings.add(versioned_stringify_ident!(wasmtime_resolve_vmctx_memory_ptr))),
220 gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("operator*")),
221 gimli::DW_AT_type = write::AttributeValue::UnitRef(ref_type_id)
222 });
223 add_tag!(deref_op_die_id, gimli::DW_TAG_formal_parameter => deref_op_this_param as deref_op_this_param_id {
224 gimli::DW_AT_type = write::AttributeValue::UnitRef(wrapper_ptr_type_id),
225 gimli::DW_AT_artificial = write::AttributeValue::Flag(true)
226 });
227
228 add_tag!(wrapper_die_id, gimli::DW_TAG_subprogram => deref_op_die as deref_op_die_id {
236 gimli::DW_AT_linkage_name = write::AttributeValue::StringRef(out_strings.add(versioned_stringify_ident!(wasmtime_resolve_vmctx_memory_ptr))),
237 gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("operator->")),
238 gimli::DW_AT_type = write::AttributeValue::UnitRef(ptr_type_id)
239 });
240 add_tag!(deref_op_die_id, gimli::DW_TAG_formal_parameter => deref_op_this_param as deref_op_this_param_id {
241 gimli::DW_AT_type = write::AttributeValue::UnitRef(wrapper_ptr_type_id),
242 gimli::DW_AT_artificial = write::AttributeValue::Flag(true)
243 });
244
245 Ok(wrapper_die_id)
246}
247
248fn is_dead_code(entry: &DebuggingInformationEntry<Reader<'_>>) -> bool {
249 const TOMBSTONE: u64 = u32::MAX as u64;
250
251 match entry.attr_value(gimli::DW_AT_low_pc) {
252 Ok(Some(AttributeValue::Addr(addr))) => addr == TOMBSTONE,
253 _ => false,
254 }
255}
256
257pub(crate) fn clone_unit(
258 compilation: &mut Compilation<'_>,
259 module: StaticModuleIndex,
260 skeleton_unit: &Unit<Reader<'_>>,
261 split_unit: Option<&Unit<Reader<'_>>>,
262 split_dwarf: Option<&Dwarf<Reader<'_>>>,
263 context: &DebugInputContext,
264 addr_tr: &AddressTransform,
265 out_encoding: gimli::Encoding,
266 out_module_synthetic_unit: &ModuleSyntheticUnit,
267 out_units: &mut write::UnitTable,
268 out_strings: &mut write::StringTable,
269 translated: &mut HashSet<usize>,
270 isa: &dyn TargetIsa,
271) -> Result<Option<(write::UnitId, UnitRefsMap, PendingDebugInfoRefs)>, Error> {
272 let mut die_ref_map = UnitRefsMap::new();
273 let mut pending_die_refs = PendingUnitRefs::new();
274 let mut pending_di_refs = PendingDebugInfoRefs::new();
275 let mut stack = Vec::new();
276
277 let skeleton_dwarf = &compilation.translations[module].debuginfo.dwarf;
278
279 let dwarf = split_dwarf.unwrap_or(skeleton_dwarf);
281 let unit = split_unit.unwrap_or(skeleton_unit);
282 let mut entries = unit.entries();
283 dbi_log!("Cloning CU {:?}", log_get_cu_summary(unit));
284
285 let (mut out_unit, out_unit_id, file_map, file_index_base) = if let Some((depth_delta, entry)) =
286 entries.next_dfs()?
287 {
288 assert_eq!(depth_delta, 0);
289 let (out_line_program, debug_line_offset, file_map, file_index_base) = clone_line_program(
290 skeleton_dwarf,
291 skeleton_unit,
292 unit.name,
293 addr_tr,
294 out_encoding,
295 out_strings,
296 )?;
297
298 if entry.tag() == gimli::DW_TAG_compile_unit {
299 log_begin_input_die(dwarf, unit, entry, 0);
300 let out_unit_id = out_units.add(write::Unit::new(out_encoding, out_line_program));
301 let out_unit = out_units.get_mut(out_unit_id);
302
303 let out_root_id = out_unit.root();
304 die_ref_map.insert(entry.offset(), out_root_id);
305
306 clone_die_attributes(
307 dwarf,
308 &unit,
309 entry,
310 addr_tr,
311 None,
312 out_unit,
313 out_root_id,
314 None,
315 None,
316 out_strings,
317 &mut pending_die_refs,
318 &mut pending_di_refs,
319 EntryAttributesContext::Root(Some(debug_line_offset)),
320 isa,
321 )?;
322 if split_unit.is_some() {
323 if let Some((_, skeleton_entry)) = skeleton_unit.entries().next_dfs()? {
324 clone_die_attributes(
325 skeleton_dwarf,
326 skeleton_unit,
327 skeleton_entry,
328 addr_tr,
329 None,
330 out_unit,
331 out_root_id,
332 None,
333 None,
334 out_strings,
335 &mut pending_die_refs,
336 &mut pending_di_refs,
337 EntryAttributesContext::Root(Some(debug_line_offset)),
338 isa,
339 )?;
340 }
341 }
342
343 log_end_output_die(entry, unit, out_root_id, out_unit, out_strings, 0);
344 stack.push(out_root_id);
345 (out_unit, out_unit_id, file_map, file_index_base)
346 } else {
347 dbi_log!("... skipped: split DW_TAG_compile_unit entry missing");
350 return Ok(None); }
352 } else {
353 dbi_log!("... skipped: empty CU (no DW_TAG_compile_unit entry)");
354 return Ok(None); };
356 let mut current_depth = 0;
357 let mut skip_at_depth = None;
358 let mut current_frame_base = InheritedAttr::new();
359 let mut current_value_range = InheritedAttr::new();
360 let mut current_scope_ranges = InheritedAttr::new();
361 let mut current_subprogram = InheritedAttr::new();
362 while let Some((depth_delta, entry)) = entries.next_dfs()? {
363 current_depth += depth_delta;
364 log_begin_input_die(dwarf, unit, entry, current_depth);
365
366 let depth_delta = if let Some((depth, cached)) = skip_at_depth {
372 let new_depth = depth + depth_delta;
374 if new_depth > 0 {
376 skip_at_depth = Some((new_depth, cached));
377 log_end_output_die_skipped(entry, unit, "unreachable", current_depth);
378 continue;
379 }
380 skip_at_depth = None;
382 new_depth + cached
383 } else {
384 depth_delta
385 };
386
387 if !context
388 .reachable
389 .contains(&entry.offset().to_unit_section_offset(&unit))
390 || is_dead_code(&entry)
391 {
392 skip_at_depth = Some((0, depth_delta));
396 log_end_output_die_skipped(entry, unit, "unreachable", current_depth);
397 continue;
398 }
399
400 let new_stack_len = stack.len().wrapping_add(depth_delta as usize);
401 current_frame_base.update(new_stack_len);
402 current_scope_ranges.update(new_stack_len);
403 current_value_range.update(new_stack_len);
404 current_subprogram.update(new_stack_len);
405 let range_builder = if entry.tag() == gimli::DW_TAG_subprogram {
406 let range_builder =
407 RangeInfoBuilder::from_subprogram_die(dwarf, &unit, entry, addr_tr)?;
408 if let RangeInfoBuilder::Function(func) = range_builder {
409 let frame_info = compilation.function_frame_info(module, func);
410 current_value_range.push(new_stack_len, frame_info);
411 let (symbol, _) = compilation.function(module, func);
412 translated.insert(symbol);
413 current_scope_ranges.push(new_stack_len, range_builder.get_ranges(addr_tr));
414 Some(range_builder)
415 } else {
416 None
418 }
419 } else {
420 let high_pc = entry.attr_value(gimli::DW_AT_high_pc)?;
421 let ranges = entry.attr_value(gimli::DW_AT_ranges)?;
422 if high_pc.is_some() || ranges.is_some() {
423 let range_builder = RangeInfoBuilder::from(dwarf, &unit, entry)?;
424 current_scope_ranges.push(new_stack_len, range_builder.get_ranges(addr_tr));
425 Some(range_builder)
426 } else {
427 None
428 }
429 };
430
431 if depth_delta <= 0 {
432 for _ in depth_delta..1 {
433 stack.pop();
434 }
435 } else {
436 assert_eq!(depth_delta, 1);
437 }
438
439 if let Some(AttributeValue::Exprloc(expr)) = entry.attr_value(gimli::DW_AT_frame_base)? {
440 if let Some(expr) = compile_expression(&expr, unit.encoding(), None)? {
441 current_frame_base.push(new_stack_len, expr);
442 }
443 }
444
445 let parent = stack.last().unwrap();
446
447 if entry.tag() == gimli::DW_TAG_pointer_type || entry.tag() == gimli::DW_TAG_reference_type
448 {
449 let pointer_kind = match entry.tag() {
451 gimli::DW_TAG_pointer_type => WebAssemblyPtrKind::Pointer,
452 gimli::DW_TAG_reference_type => WebAssemblyPtrKind::Reference,
453 _ => panic!(),
454 };
455 let die_id = replace_pointer_type(
456 *parent,
457 pointer_kind,
458 out_unit,
459 out_module_synthetic_unit.wasm_ptr_die_ref(),
460 entry,
461 unit,
462 dwarf,
463 out_strings,
464 &mut pending_die_refs,
465 )?;
466 stack.push(die_id);
467 assert_eq!(stack.len(), new_stack_len);
468 die_ref_map.insert(entry.offset(), die_id);
469 log_end_output_die(entry, unit, die_id, out_unit, out_strings, current_depth);
470 continue;
471 }
472
473 let out_die_id = out_unit.add(*parent, entry.tag());
474
475 stack.push(out_die_id);
476 assert_eq!(stack.len(), new_stack_len);
477 die_ref_map.insert(entry.offset(), out_die_id);
478
479 clone_die_attributes(
480 dwarf,
481 &unit,
482 entry,
483 addr_tr,
484 current_value_range.top(),
485 &mut out_unit,
486 out_die_id,
487 range_builder,
488 current_scope_ranges.top(),
489 out_strings,
490 &mut pending_die_refs,
491 &mut pending_di_refs,
492 EntryAttributesContext::Children {
493 depth: current_depth as usize,
494 subprograms: &mut current_subprogram,
495 file_map: &file_map,
496 file_index_base,
497 frame_base: current_frame_base.top(),
498 },
499 isa,
500 )?;
501
502 if entry.tag() == gimli::DW_TAG_base_type && isa.endianness() == Endianness::Big {
508 let current_scope = out_unit.get_mut(out_die_id);
509 current_scope.set(
510 gimli::DW_AT_endianity,
511 write::AttributeValue::Endianity(gimli::DW_END_little),
512 );
513 }
514
515 if entry.tag() == gimli::DW_TAG_subprogram && !current_scope_ranges.is_empty() {
516 append_vmctx_info(
517 out_unit,
518 out_die_id,
519 out_module_synthetic_unit.vmctx_ptr_die_ref(),
520 addr_tr,
521 current_value_range.top(),
522 current_scope_ranges.top().context("range")?,
523 out_strings,
524 isa,
525 )?;
526 }
527
528 log_end_output_die(
529 entry,
530 unit,
531 out_die_id,
532 out_unit,
533 out_strings,
534 current_depth,
535 );
536 }
537 die_ref_map.patch(pending_die_refs, out_unit);
538 Ok(Some((out_unit_id, die_ref_map, pending_di_refs)))
539}