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