1 use super::address_transform::AddressTransform;
2 use super::attr::{clone_die_attributes, EntryAttributesContext};
3 use 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 };
7 use super::expression::compile_expression;
8 use super::line_program::clone_line_program;
9 use super::range_info_builder::RangeInfoBuilder;
10 use super::refs::{PendingDebugInfoRefs, PendingUnitRefs, UnitRefsMap};
11 use super::synthetic::ModuleSyntheticUnit;
12 use super::utils::{append_vmctx_info, resolve_die_ref};
13 use super::DebugInputContext;
14 use crate::debug::{Compilation, Reader};
15 use anyhow::{Context, Error};
16 use cranelift_codegen::ir::Endianness;
17 use cranelift_codegen::isa::TargetIsa;
18 use gimli::write;
19 use gimli::{AttributeValue, DebuggingInformationEntry, Dwarf, Unit};
20 use std::collections::HashSet;
21 use wasmtime_environ::StaticModuleIndex;
22 use wasmtime_versioned_export_macros::versioned_stringify_ident;
23 
24 #[derive(Debug)]
25 pub struct InheritedAttr<T> {
26     stack: Vec<(usize, T)>,
27 }
28 
29 impl<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 
60 fn get_base_type_name(
61     type_entry: &DebuggingInformationEntry<Reader<'_>>,
62     unit: &Unit<Reader<'_>>,
63     dwarf: &Dwarf<Reader<'_>>,
64 ) -> Result<String, Error> {
65     // FIXME remove recursion.
66     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 
91 enum WebAssemblyPtrKind {
92     Reference,
93     Pointer,
94 }
95 
96 /// Replaces WebAssembly pointer type DIE with the wrapper
97 /// which natively represented by offset in a Wasm memory.
98 ///
99 /// `pointer_type_entry` is a DW_TAG_pointer_type entry (e.g. `T*`),
100 /// which refers its base type (e.g. `T`), or is a
101 /// DW_TAG_reference_type (e.g. `T&`).
102 ///
103 /// The generated wrapper is a structure that contains only the
104 /// `__ptr` field. The utility operators overloads is added to
105 /// provide better debugging experience.
106 ///
107 /// Wrappers of pointer and reference types are identical except for
108 /// their name -- they are formatted and accessed from a debugger
109 /// the same way.
110 ///
111 /// Notice that "resolve_vmctx_memory_ptr" is external/builtin
112 /// subprogram that is not part of Wasm code.
113 fn 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     // Build DW_TAG_structure_type for the wrapper:
136     //  .. DW_AT_name = "WebAssemblyPtrWrapper<T>",
137     //  .. DW_AT_byte_size = 4,
138     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     // Build DW_TAG_pointer_type for `WebAssemblyPtrWrapper<T>*`:
154     //  .. DW_AT_type = <wrapper_die>
155     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     // Build DW_TAG_reference_type for `T&`:
161     //  .. DW_AT_type = <base_type>
162     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     // Build DW_TAG_pointer_type for `T*`:
168     //  .. DW_AT_type = <base_type>
169     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     // Build wrapper_die's DW_TAG_template_type_parameter:
175     //  .. DW_AT_name = "T"
176     //  .. DW_AT_type = <base_type>
177     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     // Build wrapper_die's DW_TAG_member for `__ptr`:
185     //  .. DW_AT_name = "__ptr"
186     //  .. DW_AT_type = <wp_die>
187     //  .. DW_AT_location = 0
188     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     // Build wrapper_die's DW_TAG_subprogram for `ptr()`:
195     //  .. DW_AT_linkage_name = "wasmtime_resolve_vmctx_memory_ptr"
196     //  .. DW_AT_name = "ptr"
197     //  .. DW_AT_type = <ptr_type>
198     //  .. DW_TAG_formal_parameter
199     //  ..  .. DW_AT_type = <wrapper_ptr_type>
200     //  ..  .. DW_AT_artificial = 1
201     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     // Build wrapper_die's DW_TAG_subprogram for `operator*`:
212     //  .. DW_AT_linkage_name = "wasmtime_resolve_vmctx_memory_ptr"
213     //  .. DW_AT_name = "operator*"
214     //  .. DW_AT_type = <ref_type>
215     //  .. DW_TAG_formal_parameter
216     //  ..  .. DW_AT_type = <wrapper_ptr_type>
217     //  ..  .. DW_AT_artificial = 1
218     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     // Build wrapper_die's DW_TAG_subprogram for `operator->`:
229     //  .. DW_AT_linkage_name = "wasmtime_resolve_vmctx_memory_ptr"
230     //  .. DW_AT_name = "operator->"
231     //  .. DW_AT_type = <ptr_type>
232     //  .. DW_TAG_formal_parameter
233     //  ..  .. DW_AT_type = <wrapper_ptr_type>
234     //  ..  .. DW_AT_artificial = 1
235     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 
248 fn 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 
257 pub(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     // Iterate over all of this compilation unit's entries.
280     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             // Can happen when the DWARF is split and we dont have the package/dwo files.
348             // This is a better user experience than errorring.
349             dbi_log!("... skipped: split DW_TAG_compile_unit entry missing");
350             return Ok(None); // empty:
351         }
352     } else {
353         dbi_log!("... skipped: empty CU (no DW_TAG_compile_unit entry)");
354         return Ok(None); // empty
355     };
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         // If `skip_at_depth` is `Some` then we previously decided to skip over
367         // a node and all it's children. Let A be the last node processed, B be
368         // the first node skipped, C be previous node, and D the current node.
369         // Then `cached` is the difference from A to B, `depth` is the difference
370         // from B to C, and `depth_delta` is the differenc from C to D.
371         let depth_delta = if let Some((depth, cached)) = skip_at_depth {
372             // `new_depth` = B to D
373             let new_depth = depth + depth_delta;
374             // if D is below B continue to skip
375             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             // otherwise process D with `depth_delta` being the difference from A to D
381             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             // entry is not reachable: discarding all its info.
393             // Here B = C so `depth` is 0. A is the previous node so `cached` =
394             // `depth_delta`.
395             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                 // FIXME current_scope_ranges.push()
417                 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             // Wrap pointer types.
450             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         // Data in WebAssembly memory always uses little-endian byte order.
503         // If the native architecture is big-endian, we need to mark all
504         // base types used to refer to WebAssembly memory as little-endian
505         // using the DW_AT_endianity attribute, so that the debugger will
506         // be able to correctly access them.
507         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 }
540