1 use super::address_transform::AddressTransform;
2 use super::attr::{clone_die_attributes, FileAttributeContext};
3 use super::expression::compile_expression;
4 use super::line_program::clone_line_program;
5 use super::range_info_builder::RangeInfoBuilder;
6 use super::refs::{PendingDebugInfoRefs, PendingUnitRefs, UnitRefsMap};
7 use super::utils::{add_internal_types, append_vmctx_info, get_function_frame_info};
8 use super::{DebugInputContext, Reader, TransformError};
9 use crate::CompiledFunctions;
10 use anyhow::{Context, Error};
11 use cranelift_codegen::ir::Endianness;
12 use cranelift_codegen::isa::TargetIsa;
13 use gimli::write;
14 use gimli::{AttributeValue, DebuggingInformationEntry, Unit};
15 use std::collections::HashSet;
16 use wasmtime_environ::{DefinedFuncIndex, ModuleMemoryOffset};
17 
18 struct InheritedAttr<T> {
19     stack: Vec<(usize, T)>,
20 }
21 
22 impl<T> InheritedAttr<T> {
23     fn new() -> Self {
24         InheritedAttr { stack: Vec::new() }
25     }
26 
27     fn update(&mut self, depth: usize) {
28         while !self.stack.is_empty() && self.stack.last().unwrap().0 >= depth {
29             self.stack.pop();
30         }
31     }
32 
33     fn push(&mut self, depth: usize, value: T) {
34         self.stack.push((depth, value));
35     }
36 
37     fn top(&self) -> Option<&T> {
38         self.stack.last().map(|entry| &entry.1)
39     }
40 
41     fn is_empty(&self) -> bool {
42         self.stack.is_empty()
43     }
44 }
45 
46 fn get_base_type_name<R>(
47     type_entry: &DebuggingInformationEntry<R>,
48     unit: &Unit<R, R::Offset>,
49     context: &DebugInputContext<R>,
50 ) -> Result<String, Error>
51 where
52     R: Reader,
53 {
54     // FIXME remove recursion.
55     if let Some(AttributeValue::UnitRef(ref offset)) = type_entry.attr_value(gimli::DW_AT_type)? {
56         let mut entries = unit.entries_at_offset(*offset)?;
57         entries.next_entry()?;
58         if let Some(die) = entries.current() {
59             if let Some(AttributeValue::DebugStrRef(str_offset)) =
60                 die.attr_value(gimli::DW_AT_name)?
61             {
62                 return Ok(String::from(
63                     context.debug_str.get_str(str_offset)?.to_string()?,
64                 ));
65             }
66             match die.tag() {
67                 gimli::DW_TAG_const_type => {
68                     return Ok(format!("const {}", get_base_type_name(die, unit, context)?));
69                 }
70                 gimli::DW_TAG_pointer_type => {
71                     return Ok(format!("{}*", get_base_type_name(die, unit, context)?));
72                 }
73                 gimli::DW_TAG_reference_type => {
74                     return Ok(format!("{}&", get_base_type_name(die, unit, context)?));
75                 }
76                 gimli::DW_TAG_array_type => {
77                     return Ok(format!("{}[]", get_base_type_name(die, unit, context)?));
78                 }
79                 _ => (),
80             }
81         }
82     }
83     Ok(String::from("??"))
84 }
85 
86 enum WebAssemblyPtrKind {
87     Reference,
88     Pointer,
89 }
90 
91 /// Replaces WebAssembly pointer type DIE with the wrapper
92 /// which natively represented by offset in a Wasm memory.
93 ///
94 /// `pointer_type_entry` is a DW_TAG_pointer_type entry (e.g. `T*`),
95 /// which refers its base type (e.g. `T`), or is a
96 /// DW_TAG_reference_type (e.g. `T&`).
97 ///
98 /// The generated wrapper is a structure that contains only the
99 /// `__ptr` field. The utility operators overloads is added to
100 /// provide better debugging experience.
101 ///
102 /// Wrappers of pointer and reference types are identical except for
103 /// their name -- they are formatted and accessed from a debugger
104 /// the same way.
105 ///
106 /// Notice that "resolve_vmctx_memory_ptr" is external/builtin
107 /// subprogram that is not part of Wasm code.
108 fn replace_pointer_type<R>(
109     parent_id: write::UnitEntryId,
110     kind: WebAssemblyPtrKind,
111     comp_unit: &mut write::Unit,
112     wp_die_id: write::UnitEntryId,
113     pointer_type_entry: &DebuggingInformationEntry<R>,
114     unit: &Unit<R, R::Offset>,
115     context: &DebugInputContext<R>,
116     out_strings: &mut write::StringTable,
117     pending_die_refs: &mut PendingUnitRefs,
118 ) -> Result<write::UnitEntryId, Error>
119 where
120     R: Reader,
121 {
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)]
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, context)?
140         ),
141         WebAssemblyPtrKind::Reference => format!(
142             "WebAssemblyRefWrapper<{}>",
143             get_base_type_name(pointer_type_entry, unit, context)?
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::UnitRef(wp_die_id),
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 = "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("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 = "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("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 = "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("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, R>(
247     dwarf: &gimli::Dwarf<R>,
248     unit: Unit<R, R::Offset>,
249     context: &DebugInputContext<R>,
250     addr_tr: &'a AddressTransform,
251     funcs: &'a CompiledFunctions,
252     memory_offset: &ModuleMemoryOffset,
253     out_encoding: gimli::Encoding,
254     out_units: &mut write::UnitTable,
255     out_strings: &mut write::StringTable,
256     translated: &mut HashSet<DefinedFuncIndex>,
257     isa: &dyn TargetIsa,
258 ) -> Result<Option<(write::UnitId, UnitRefsMap, PendingDebugInfoRefs)>, Error>
259 where
260     R: Reader,
261 {
262     let mut die_ref_map = UnitRefsMap::new();
263     let mut pending_die_refs = PendingUnitRefs::new();
264     let mut pending_di_refs = PendingDebugInfoRefs::new();
265     let mut stack = Vec::new();
266 
267     // Iterate over all of this compilation unit's entries.
268     let mut entries = unit.entries();
269     let (mut comp_unit, unit_id, file_map, file_index_base, cu_low_pc, wp_die_id, vmctx_die_id) =
270         if let Some((depth_delta, entry)) = entries.next_dfs()? {
271             assert_eq!(depth_delta, 0);
272             let (out_line_program, debug_line_offset, file_map, file_index_base) =
273                 clone_line_program(
274                     &unit,
275                     entry,
276                     addr_tr,
277                     out_encoding,
278                     context.debug_str,
279                     context.debug_str_offsets,
280                     context.debug_line_str,
281                     context.debug_line,
282                     out_strings,
283                 )?;
284 
285             if entry.tag() == gimli::DW_TAG_compile_unit {
286                 let unit_id = out_units.add(write::Unit::new(out_encoding, out_line_program));
287                 let comp_unit = out_units.get_mut(unit_id);
288 
289                 let root_id = comp_unit.root();
290                 die_ref_map.insert(entry.offset(), root_id);
291 
292                 let cu_low_pc = if let Some(AttributeValue::Addr(addr)) =
293                     entry.attr_value(gimli::DW_AT_low_pc)?
294                 {
295                     addr
296                 } else if let Some(AttributeValue::DebugAddrIndex(i)) =
297                     entry.attr_value(gimli::DW_AT_low_pc)?
298                 {
299                     context.debug_addr.get_address(4, unit.addr_base, i)?
300                 } else {
301                     // FIXME? return Err(TransformError("No low_pc for unit header").into());
302                     0
303                 };
304 
305                 clone_die_attributes(
306                     dwarf,
307                     &unit,
308                     entry,
309                     context,
310                     addr_tr,
311                     None,
312                     comp_unit,
313                     root_id,
314                     None,
315                     None,
316                     cu_low_pc,
317                     out_strings,
318                     &mut pending_die_refs,
319                     &mut pending_di_refs,
320                     FileAttributeContext::Root(Some(debug_line_offset)),
321                     isa,
322                 )?;
323 
324                 let (wp_die_id, vmctx_die_id) =
325                     add_internal_types(comp_unit, root_id, out_strings, memory_offset);
326 
327                 stack.push(root_id);
328                 (
329                     comp_unit,
330                     unit_id,
331                     file_map,
332                     file_index_base,
333                     cu_low_pc,
334                     wp_die_id,
335                     vmctx_die_id,
336                 )
337             } else {
338                 return Err(TransformError("Unexpected unit header").into());
339             }
340         } else {
341             return Ok(None); // empty
342         };
343     let mut skip_at_depth = None;
344     let mut current_frame_base = InheritedAttr::new();
345     let mut current_value_range = InheritedAttr::new();
346     let mut current_scope_ranges = InheritedAttr::new();
347     while let Some((depth_delta, entry)) = entries.next_dfs()? {
348         let depth_delta = if let Some((depth, cached)) = skip_at_depth {
349             let new_depth = depth + depth_delta;
350             if new_depth > 0 {
351                 skip_at_depth = Some((new_depth, cached));
352                 continue;
353             }
354             skip_at_depth = None;
355             new_depth + cached
356         } else {
357             depth_delta
358         };
359 
360         if !context
361             .reachable
362             .contains(&entry.offset().to_unit_section_offset(&unit))
363         {
364             // entry is not reachable: discarding all its info.
365             skip_at_depth = Some((0, depth_delta));
366             continue;
367         }
368 
369         let new_stack_len = stack.len().wrapping_add(depth_delta as usize);
370         current_frame_base.update(new_stack_len);
371         current_scope_ranges.update(new_stack_len);
372         current_value_range.update(new_stack_len);
373         let range_builder = if entry.tag() == gimli::DW_TAG_subprogram {
374             let range_builder = RangeInfoBuilder::from_subprogram_die(
375                 dwarf, &unit, entry, context, addr_tr, cu_low_pc,
376             )?;
377             if let RangeInfoBuilder::Function(func_index) = range_builder {
378                 if let Some(frame_info) = get_function_frame_info(memory_offset, funcs, func_index)
379                 {
380                     current_value_range.push(new_stack_len, frame_info);
381                 }
382                 translated.insert(func_index);
383                 current_scope_ranges.push(new_stack_len, range_builder.get_ranges(addr_tr));
384                 Some(range_builder)
385             } else {
386                 // FIXME current_scope_ranges.push()
387                 None
388             }
389         } else {
390             let high_pc = entry.attr_value(gimli::DW_AT_high_pc)?;
391             let ranges = entry.attr_value(gimli::DW_AT_ranges)?;
392             if high_pc.is_some() || ranges.is_some() {
393                 let range_builder =
394                     RangeInfoBuilder::from(dwarf, &unit, entry, context, cu_low_pc)?;
395                 current_scope_ranges.push(new_stack_len, range_builder.get_ranges(addr_tr));
396                 Some(range_builder)
397             } else {
398                 None
399             }
400         };
401 
402         if depth_delta <= 0 {
403             for _ in depth_delta..1 {
404                 stack.pop();
405             }
406         } else {
407             assert_eq!(depth_delta, 1);
408         }
409 
410         if let Some(AttributeValue::Exprloc(expr)) = entry.attr_value(gimli::DW_AT_frame_base)? {
411             if let Some(expr) = compile_expression(&expr, unit.encoding(), None)? {
412                 current_frame_base.push(new_stack_len, expr);
413             }
414         }
415 
416         let parent = stack.last().unwrap();
417 
418         if entry.tag() == gimli::DW_TAG_pointer_type || entry.tag() == gimli::DW_TAG_reference_type
419         {
420             // Wrap pointer types.
421             let pointer_kind = match entry.tag() {
422                 gimli::DW_TAG_pointer_type => WebAssemblyPtrKind::Pointer,
423                 gimli::DW_TAG_reference_type => WebAssemblyPtrKind::Reference,
424                 _ => panic!(),
425             };
426             let die_id = replace_pointer_type(
427                 *parent,
428                 pointer_kind,
429                 comp_unit,
430                 wp_die_id,
431                 entry,
432                 &unit,
433                 context,
434                 out_strings,
435                 &mut pending_die_refs,
436             )?;
437             stack.push(die_id);
438             assert_eq!(stack.len(), new_stack_len);
439             die_ref_map.insert(entry.offset(), die_id);
440             continue;
441         }
442 
443         let die_id = comp_unit.add(*parent, entry.tag());
444 
445         stack.push(die_id);
446         assert_eq!(stack.len(), new_stack_len);
447         die_ref_map.insert(entry.offset(), die_id);
448 
449         clone_die_attributes(
450             dwarf,
451             &unit,
452             entry,
453             context,
454             addr_tr,
455             current_value_range.top(),
456             &mut comp_unit,
457             die_id,
458             range_builder,
459             current_scope_ranges.top(),
460             cu_low_pc,
461             out_strings,
462             &mut pending_die_refs,
463             &mut pending_di_refs,
464             FileAttributeContext::Children {
465                 file_map: &file_map,
466                 file_index_base,
467                 frame_base: current_frame_base.top(),
468             },
469             isa,
470         )?;
471 
472         // Data in WebAssembly memory always uses little-endian byte order.
473         // If the native architecture is big-endian, we need to mark all
474         // base types used to refer to WebAssembly memory as little-endian
475         // using the DW_AT_endianity attribute, so that the debugger will
476         // be able to correctly access them.
477         if entry.tag() == gimli::DW_TAG_base_type && isa.endianness() == Endianness::Big {
478             let current_scope = comp_unit.get_mut(die_id);
479             current_scope.set(
480                 gimli::DW_AT_endianity,
481                 write::AttributeValue::Endianity(gimli::DW_END_little),
482             );
483         }
484 
485         if entry.tag() == gimli::DW_TAG_subprogram && !current_scope_ranges.is_empty() {
486             append_vmctx_info(
487                 comp_unit,
488                 die_id,
489                 vmctx_die_id,
490                 addr_tr,
491                 current_value_range.top(),
492                 current_scope_ranges.top().context("range")?,
493                 out_strings,
494                 isa,
495             )?;
496         }
497     }
498     die_ref_map.patch(pending_die_refs, comp_unit);
499     Ok(Some((unit_id, die_ref_map, pending_di_refs)))
500 }
501