1 use super::address_transform::AddressTransform;
2 use super::attr::{clone_die_attributes, FileAttributeContext};
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;
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 struct InheritedAttr<T> {
25     stack: Vec<(usize, T)>,
26 }
27 
28 impl<T> InheritedAttr<T> {
29     fn new() -> Self {
30         InheritedAttr { stack: Vec::new() }
31     }
32 
33     fn update(&mut self, depth: usize) {
34         while !self.stack.is_empty() && self.stack.last().unwrap().0 >= depth {
35             self.stack.pop();
36         }
37     }
38 
39     fn push(&mut self, depth: usize, value: T) {
40         self.stack.push((depth, value));
41     }
42 
43     fn top(&self) -> Option<&T> {
44         self.stack.last().map(|entry| &entry.1)
45     }
46 
47     fn is_empty(&self) -> bool {
48         self.stack.is_empty()
49     }
50 }
51 
52 fn get_base_type_name(
53     type_entry: &DebuggingInformationEntry<Reader<'_>>,
54     unit: &Unit<Reader<'_>, usize>,
55     dwarf: &Dwarf<Reader<'_>>,
56 ) -> Result<String, Error> {
57     // FIXME remove recursion.
58     if let Some(AttributeValue::UnitRef(ref offset)) = type_entry.attr_value(gimli::DW_AT_type)? {
59         let mut entries = unit.entries_at_offset(*offset)?;
60         entries.next_entry()?;
61         if let Some(die) = entries.current() {
62             if let Some(value) = die.attr_value(gimli::DW_AT_name)? {
63                 return Ok(String::from(dwarf.attr_string(unit, value)?.to_string()?));
64             }
65             match die.tag() {
66                 gimli::DW_TAG_const_type => {
67                     return Ok(format!("const {}", get_base_type_name(die, unit, dwarf)?));
68                 }
69                 gimli::DW_TAG_pointer_type => {
70                     return Ok(format!("{}*", get_base_type_name(die, unit, dwarf)?));
71                 }
72                 gimli::DW_TAG_reference_type => {
73                     return Ok(format!("{}&", get_base_type_name(die, unit, dwarf)?));
74                 }
75                 gimli::DW_TAG_array_type => {
76                     return Ok(format!("{}[]", get_base_type_name(die, unit, dwarf)?));
77                 }
78                 _ => (),
79             }
80         }
81     }
82     Ok(String::from("??"))
83 }
84 
85 enum WebAssemblyPtrKind {
86     Reference,
87     Pointer,
88 }
89 
90 /// Replaces WebAssembly pointer type DIE with the wrapper
91 /// which natively represented by offset in a Wasm memory.
92 ///
93 /// `pointer_type_entry` is a DW_TAG_pointer_type entry (e.g. `T*`),
94 /// which refers its base type (e.g. `T`), or is a
95 /// DW_TAG_reference_type (e.g. `T&`).
96 ///
97 /// The generated wrapper is a structure that contains only the
98 /// `__ptr` field. The utility operators overloads is added to
99 /// provide better debugging experience.
100 ///
101 /// Wrappers of pointer and reference types are identical except for
102 /// their name -- they are formatted and accessed from a debugger
103 /// the same way.
104 ///
105 /// Notice that "resolve_vmctx_memory_ptr" is external/builtin
106 /// subprogram that is not part of Wasm code.
107 fn replace_pointer_type(
108     parent_id: write::UnitEntryId,
109     kind: WebAssemblyPtrKind,
110     comp_unit: &mut write::Unit,
111     wasm_ptr_die_ref: write::Reference,
112     pointer_type_entry: &DebuggingInformationEntry<Reader<'_>>,
113     unit: &Unit<Reader<'_>, usize>,
114     dwarf: &Dwarf<Reader<'_>>,
115     out_strings: &mut write::StringTable,
116     pending_die_refs: &mut PendingUnitRefs,
117 ) -> Result<write::UnitEntryId, Error> {
118     const WASM_PTR_LEN: u8 = 4;
119 
120     macro_rules! add_tag {
121         ($parent_id:ident, $tag:expr => $die:ident as $die_id:ident { $($a:path = $v:expr),* }) => {
122             let $die_id = comp_unit.add($parent_id, $tag);
123             #[allow(unused_variables, reason = "sometimes not used below")]
124             let $die = comp_unit.get_mut($die_id);
125             $( $die.set($a, $v); )*
126         };
127     }
128 
129     // Build DW_TAG_structure_type for the wrapper:
130     //  .. DW_AT_name = "WebAssemblyPtrWrapper<T>",
131     //  .. DW_AT_byte_size = 4,
132     let name = match kind {
133         WebAssemblyPtrKind::Pointer => format!(
134             "WebAssemblyPtrWrapper<{}>",
135             get_base_type_name(pointer_type_entry, unit, dwarf)?
136         ),
137         WebAssemblyPtrKind::Reference => format!(
138             "WebAssemblyRefWrapper<{}>",
139             get_base_type_name(pointer_type_entry, unit, dwarf)?
140         ),
141     };
142     add_tag!(parent_id, gimli::DW_TAG_structure_type => wrapper_die as wrapper_die_id {
143         gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add(name.as_str())),
144         gimli::DW_AT_byte_size = write::AttributeValue::Data1(WASM_PTR_LEN)
145     });
146 
147     // Build DW_TAG_pointer_type for `WebAssemblyPtrWrapper<T>*`:
148     //  .. DW_AT_type = <wrapper_die>
149     add_tag!(parent_id, gimli::DW_TAG_pointer_type => wrapper_ptr_type as wrapper_ptr_type_id {
150         gimli::DW_AT_type = write::AttributeValue::UnitRef(wrapper_die_id)
151     });
152 
153     let base_type_id = pointer_type_entry.attr_value(gimli::DW_AT_type)?;
154     // Build DW_TAG_reference_type for `T&`:
155     //  .. DW_AT_type = <base_type>
156     add_tag!(parent_id, gimli::DW_TAG_reference_type => ref_type as ref_type_id {});
157     if let Some(AttributeValue::UnitRef(ref offset)) = base_type_id {
158         pending_die_refs.insert(ref_type_id, gimli::DW_AT_type, *offset);
159     }
160 
161     // Build DW_TAG_pointer_type for `T*`:
162     //  .. DW_AT_type = <base_type>
163     add_tag!(parent_id, gimli::DW_TAG_pointer_type => ptr_type as ptr_type_id {});
164     if let Some(AttributeValue::UnitRef(ref offset)) = base_type_id {
165         pending_die_refs.insert(ptr_type_id, gimli::DW_AT_type, *offset);
166     }
167 
168     // Build wrapper_die's DW_TAG_template_type_parameter:
169     //  .. DW_AT_name = "T"
170     //  .. DW_AT_type = <base_type>
171     add_tag!(wrapper_die_id, gimli::DW_TAG_template_type_parameter => t_param_die as t_param_die_id {
172         gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("T"))
173     });
174     if let Some(AttributeValue::UnitRef(ref offset)) = base_type_id {
175         pending_die_refs.insert(t_param_die_id, gimli::DW_AT_type, *offset);
176     }
177 
178     // Build wrapper_die's DW_TAG_member for `__ptr`:
179     //  .. DW_AT_name = "__ptr"
180     //  .. DW_AT_type = <wp_die>
181     //  .. DW_AT_location = 0
182     add_tag!(wrapper_die_id, gimli::DW_TAG_member => m_die as m_die_id {
183         gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("__ptr")),
184         gimli::DW_AT_type = write::AttributeValue::DebugInfoRef(wasm_ptr_die_ref),
185         gimli::DW_AT_data_member_location = write::AttributeValue::Data1(0)
186     });
187 
188     // Build wrapper_die's DW_TAG_subprogram for `ptr()`:
189     //  .. DW_AT_linkage_name = "resolve_vmctx_memory_ptr"
190     //  .. DW_AT_name = "ptr"
191     //  .. DW_AT_type = <ptr_type>
192     //  .. DW_TAG_formal_parameter
193     //  ..  .. DW_AT_type = <wrapper_ptr_type>
194     //  ..  .. DW_AT_artificial = 1
195     add_tag!(wrapper_die_id, gimli::DW_TAG_subprogram => deref_op_die as deref_op_die_id {
196         gimli::DW_AT_linkage_name = write::AttributeValue::StringRef(out_strings.add(versioned_stringify_ident!(resolve_vmctx_memory_ptr))),
197         gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("ptr")),
198         gimli::DW_AT_type = write::AttributeValue::UnitRef(ptr_type_id)
199     });
200     add_tag!(deref_op_die_id, gimli::DW_TAG_formal_parameter => deref_op_this_param as deref_op_this_param_id {
201         gimli::DW_AT_type = write::AttributeValue::UnitRef(wrapper_ptr_type_id),
202         gimli::DW_AT_artificial = write::AttributeValue::Flag(true)
203     });
204 
205     // Build wrapper_die's DW_TAG_subprogram for `operator*`:
206     //  .. DW_AT_linkage_name = "resolve_vmctx_memory_ptr"
207     //  .. DW_AT_name = "operator*"
208     //  .. DW_AT_type = <ref_type>
209     //  .. DW_TAG_formal_parameter
210     //  ..  .. DW_AT_type = <wrapper_ptr_type>
211     //  ..  .. DW_AT_artificial = 1
212     add_tag!(wrapper_die_id, gimli::DW_TAG_subprogram => deref_op_die as deref_op_die_id {
213         gimli::DW_AT_linkage_name = write::AttributeValue::StringRef(out_strings.add(versioned_stringify_ident!(resolve_vmctx_memory_ptr))),
214         gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("operator*")),
215         gimli::DW_AT_type = write::AttributeValue::UnitRef(ref_type_id)
216     });
217     add_tag!(deref_op_die_id, gimli::DW_TAG_formal_parameter => deref_op_this_param as deref_op_this_param_id {
218         gimli::DW_AT_type = write::AttributeValue::UnitRef(wrapper_ptr_type_id),
219         gimli::DW_AT_artificial = write::AttributeValue::Flag(true)
220     });
221 
222     // Build wrapper_die's DW_TAG_subprogram for `operator->`:
223     //  .. DW_AT_linkage_name = "resolve_vmctx_memory_ptr"
224     //  .. DW_AT_name = "operator->"
225     //  .. DW_AT_type = <ptr_type>
226     //  .. DW_TAG_formal_parameter
227     //  ..  .. DW_AT_type = <wrapper_ptr_type>
228     //  ..  .. DW_AT_artificial = 1
229     add_tag!(wrapper_die_id, gimli::DW_TAG_subprogram => deref_op_die as deref_op_die_id {
230         gimli::DW_AT_linkage_name = write::AttributeValue::StringRef(out_strings.add(versioned_stringify_ident!(resolve_vmctx_memory_ptr))),
231         gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("operator->")),
232         gimli::DW_AT_type = write::AttributeValue::UnitRef(ptr_type_id)
233     });
234     add_tag!(deref_op_die_id, gimli::DW_TAG_formal_parameter => deref_op_this_param as deref_op_this_param_id {
235         gimli::DW_AT_type = write::AttributeValue::UnitRef(wrapper_ptr_type_id),
236         gimli::DW_AT_artificial = write::AttributeValue::Flag(true)
237     });
238 
239     Ok(wrapper_die_id)
240 }
241 
242 fn is_dead_code(entry: &DebuggingInformationEntry<Reader<'_>>) -> bool {
243     const TOMBSTONE: u64 = u32::MAX as u64;
244 
245     match entry.attr_value(gimli::DW_AT_low_pc) {
246         Ok(Some(AttributeValue::Addr(addr))) => addr == TOMBSTONE,
247         _ => false,
248     }
249 }
250 
251 pub(crate) fn clone_unit(
252     compilation: &mut Compilation<'_>,
253     module: StaticModuleIndex,
254     skeleton_unit: &Unit<Reader<'_>, usize>,
255     split_unit: Option<&Unit<Reader<'_>, usize>>,
256     split_dwarf: Option<&Dwarf<Reader<'_>>>,
257     context: &DebugInputContext,
258     addr_tr: &AddressTransform,
259     out_encoding: gimli::Encoding,
260     out_module_synthetic_unit: &ModuleSyntheticUnit,
261     out_units: &mut write::UnitTable,
262     out_strings: &mut write::StringTable,
263     translated: &mut HashSet<usize>,
264     isa: &dyn TargetIsa,
265 ) -> Result<Option<(write::UnitId, UnitRefsMap, PendingDebugInfoRefs)>, Error> {
266     let mut die_ref_map = UnitRefsMap::new();
267     let mut pending_die_refs = PendingUnitRefs::new();
268     let mut pending_di_refs = PendingDebugInfoRefs::new();
269     let mut stack = Vec::new();
270 
271     let skeleton_dwarf = &compilation.translations[module].debuginfo.dwarf;
272 
273     // Iterate over all of this compilation unit's entries.
274     let dwarf = split_dwarf.unwrap_or(skeleton_dwarf);
275     let unit = split_unit.unwrap_or(skeleton_unit);
276     let mut entries = unit.entries();
277     dbi_log!("Cloning CU {:?}", log_get_cu_summary(unit));
278 
279     let (mut out_unit, out_unit_id, file_map, file_index_base) = if let Some((depth_delta, entry)) =
280         entries.next_dfs()?
281     {
282         assert_eq!(depth_delta, 0);
283         let (out_line_program, debug_line_offset, file_map, file_index_base) = clone_line_program(
284             skeleton_dwarf,
285             skeleton_unit,
286             unit.name,
287             addr_tr,
288             out_encoding,
289             out_strings,
290         )?;
291 
292         if entry.tag() == gimli::DW_TAG_compile_unit {
293             log_begin_input_die(dwarf, unit, entry, 0);
294             let out_unit_id = out_units.add(write::Unit::new(out_encoding, out_line_program));
295             let out_unit = out_units.get_mut(out_unit_id);
296 
297             let out_root_id = out_unit.root();
298             die_ref_map.insert(entry.offset(), out_root_id);
299 
300             clone_die_attributes(
301                 dwarf,
302                 &unit,
303                 entry,
304                 addr_tr,
305                 None,
306                 out_unit,
307                 out_root_id,
308                 None,
309                 None,
310                 out_strings,
311                 &mut pending_die_refs,
312                 &mut pending_di_refs,
313                 FileAttributeContext::Root(Some(debug_line_offset)),
314                 isa,
315             )?;
316             if split_unit.is_some() {
317                 if let Some((_, skeleton_entry)) = skeleton_unit.entries().next_dfs()? {
318                     clone_die_attributes(
319                         skeleton_dwarf,
320                         skeleton_unit,
321                         skeleton_entry,
322                         addr_tr,
323                         None,
324                         out_unit,
325                         out_root_id,
326                         None,
327                         None,
328                         out_strings,
329                         &mut pending_die_refs,
330                         &mut pending_di_refs,
331                         FileAttributeContext::Root(Some(debug_line_offset)),
332                         isa,
333                     )?;
334                 }
335             }
336 
337             log_end_output_die(entry, unit, out_root_id, out_unit, out_strings, 0);
338             stack.push(out_root_id);
339             (out_unit, out_unit_id, file_map, file_index_base)
340         } else {
341             // Can happen when the DWARF is split and we dont have the package/dwo files.
342             // This is a better user experience than errorring.
343             dbi_log!("... skipped: split DW_TAG_compile_unit entry missing");
344             return Ok(None); // empty:
345         }
346     } else {
347         dbi_log!("... skipped: empty CU (no DW_TAG_compile_unit entry)");
348         return Ok(None); // empty
349     };
350     let mut current_depth = 0;
351     let mut skip_at_depth = None;
352     let mut current_frame_base = InheritedAttr::new();
353     let mut current_value_range = InheritedAttr::new();
354     let mut current_scope_ranges = InheritedAttr::new();
355     while let Some((depth_delta, entry)) = entries.next_dfs()? {
356         current_depth += depth_delta;
357         log_begin_input_die(dwarf, unit, entry, current_depth);
358 
359         // If `skip_at_depth` is `Some` then we previously decided to skip over
360         // a node and all it's children. Let A be the last node processed, B be
361         // the first node skipped, C be previous node, and D the current node.
362         // Then `cached` is the difference from A to B, `depth` is the difference
363         // from B to C, and `depth_delta` is the differenc from C to D.
364         let depth_delta = if let Some((depth, cached)) = skip_at_depth {
365             // `new_depth` = B to D
366             let new_depth = depth + depth_delta;
367             // if D is below B continue to skip
368             if new_depth > 0 {
369                 skip_at_depth = Some((new_depth, cached));
370                 log_end_output_die_skipped(entry, unit, "unreachable", current_depth);
371                 continue;
372             }
373             // otherwise process D with `depth_delta` being the difference from A to D
374             skip_at_depth = None;
375             new_depth + cached
376         } else {
377             depth_delta
378         };
379 
380         if !context
381             .reachable
382             .contains(&entry.offset().to_unit_section_offset(&unit))
383             || is_dead_code(&entry)
384         {
385             // entry is not reachable: discarding all its info.
386             // Here B = C so `depth` is 0. A is the previous node so `cached` =
387             // `depth_delta`.
388             skip_at_depth = Some((0, depth_delta));
389             log_end_output_die_skipped(entry, unit, "unreachable", current_depth);
390             continue;
391         }
392 
393         let new_stack_len = stack.len().wrapping_add(depth_delta as usize);
394         current_frame_base.update(new_stack_len);
395         current_scope_ranges.update(new_stack_len);
396         current_value_range.update(new_stack_len);
397         let range_builder = if entry.tag() == gimli::DW_TAG_subprogram {
398             let range_builder =
399                 RangeInfoBuilder::from_subprogram_die(dwarf, &unit, entry, addr_tr)?;
400             if let RangeInfoBuilder::Function(func) = range_builder {
401                 let frame_info = compilation.function_frame_info(module, func);
402                 current_value_range.push(new_stack_len, frame_info);
403                 let (symbol, _) = compilation.function(module, func);
404                 translated.insert(symbol);
405                 current_scope_ranges.push(new_stack_len, range_builder.get_ranges(addr_tr));
406                 Some(range_builder)
407             } else {
408                 // FIXME current_scope_ranges.push()
409                 None
410             }
411         } else {
412             let high_pc = entry.attr_value(gimli::DW_AT_high_pc)?;
413             let ranges = entry.attr_value(gimli::DW_AT_ranges)?;
414             if high_pc.is_some() || ranges.is_some() {
415                 let range_builder = RangeInfoBuilder::from(dwarf, &unit, entry)?;
416                 current_scope_ranges.push(new_stack_len, range_builder.get_ranges(addr_tr));
417                 Some(range_builder)
418             } else {
419                 None
420             }
421         };
422 
423         if depth_delta <= 0 {
424             for _ in depth_delta..1 {
425                 stack.pop();
426             }
427         } else {
428             assert_eq!(depth_delta, 1);
429         }
430 
431         if let Some(AttributeValue::Exprloc(expr)) = entry.attr_value(gimli::DW_AT_frame_base)? {
432             if let Some(expr) = compile_expression(&expr, unit.encoding(), None)? {
433                 current_frame_base.push(new_stack_len, expr);
434             }
435         }
436 
437         let parent = stack.last().unwrap();
438 
439         if entry.tag() == gimli::DW_TAG_pointer_type || entry.tag() == gimli::DW_TAG_reference_type
440         {
441             // Wrap pointer types.
442             let pointer_kind = match entry.tag() {
443                 gimli::DW_TAG_pointer_type => WebAssemblyPtrKind::Pointer,
444                 gimli::DW_TAG_reference_type => WebAssemblyPtrKind::Reference,
445                 _ => panic!(),
446             };
447             let die_id = replace_pointer_type(
448                 *parent,
449                 pointer_kind,
450                 out_unit,
451                 out_module_synthetic_unit.wasm_ptr_die_ref(),
452                 entry,
453                 unit,
454                 dwarf,
455                 out_strings,
456                 &mut pending_die_refs,
457             )?;
458             stack.push(die_id);
459             assert_eq!(stack.len(), new_stack_len);
460             die_ref_map.insert(entry.offset(), die_id);
461             log_end_output_die(entry, unit, die_id, out_unit, out_strings, current_depth);
462             continue;
463         }
464 
465         let out_die_id = out_unit.add(*parent, entry.tag());
466 
467         stack.push(out_die_id);
468         assert_eq!(stack.len(), new_stack_len);
469         die_ref_map.insert(entry.offset(), out_die_id);
470 
471         clone_die_attributes(
472             dwarf,
473             &unit,
474             entry,
475             addr_tr,
476             current_value_range.top(),
477             &mut out_unit,
478             out_die_id,
479             range_builder,
480             current_scope_ranges.top(),
481             out_strings,
482             &mut pending_die_refs,
483             &mut pending_di_refs,
484             FileAttributeContext::Children {
485                 file_map: &file_map,
486                 file_index_base,
487                 frame_base: current_frame_base.top(),
488             },
489             isa,
490         )?;
491 
492         // Data in WebAssembly memory always uses little-endian byte order.
493         // If the native architecture is big-endian, we need to mark all
494         // base types used to refer to WebAssembly memory as little-endian
495         // using the DW_AT_endianity attribute, so that the debugger will
496         // be able to correctly access them.
497         if entry.tag() == gimli::DW_TAG_base_type && isa.endianness() == Endianness::Big {
498             let current_scope = out_unit.get_mut(out_die_id);
499             current_scope.set(
500                 gimli::DW_AT_endianity,
501                 write::AttributeValue::Endianity(gimli::DW_END_little),
502             );
503         }
504 
505         if entry.tag() == gimli::DW_TAG_subprogram && !current_scope_ranges.is_empty() {
506             append_vmctx_info(
507                 out_unit,
508                 out_die_id,
509                 out_module_synthetic_unit.vmctx_ptr_die_ref(),
510                 addr_tr,
511                 current_value_range.top(),
512                 current_scope_ranges.top().context("range")?,
513                 out_strings,
514                 isa,
515             )?;
516         }
517 
518         log_end_output_die(
519             entry,
520             unit,
521             out_die_id,
522             out_unit,
523             out_strings,
524             current_depth,
525         );
526     }
527     die_ref_map.patch(pending_die_refs, out_unit);
528     Ok(Some((out_unit_id, die_ref_map, pending_di_refs)))
529 }
530