1 use super::address_transform::AddressTransform;
2 use super::attr::{EntryAttributesContext, clone_die_attributes};
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::synthetic::ModuleSyntheticUnit;
11 use super::utils::{append_vmctx_info, resolve_die_ref};
12 use crate::debug::{Compilation, Reader};
13 use cranelift_codegen::ir::Endianness;
14 use cranelift_codegen::isa::TargetIsa;
15 use gimli::AttributeValue;
16 use gimli::write;
17 use std::collections::HashSet;
18 use wasmtime_environ::StaticModuleIndex;
19 use wasmtime_environ::error::{Context, Error};
20 use wasmtime_versioned_export_macros::versioned_stringify_ident;
21 
22 #[derive(Debug)]
23 pub struct InheritedAttr<T> {
24     stack: Vec<(isize, T)>,
25 }
26 
27 impl<T> InheritedAttr<T> {
new() -> Self28     fn new() -> Self {
29         InheritedAttr { stack: Vec::new() }
30     }
31 
update(&mut self, depth: isize)32     fn update(&mut self, depth: isize) {
33         while !self.stack.is_empty() && self.stack.last().unwrap().0 >= depth {
34             self.stack.pop();
35         }
36     }
37 
push(&mut self, depth: isize, value: T)38     pub fn push(&mut self, depth: isize, value: T) {
39         self.stack.push((depth, value));
40     }
41 
top(&self) -> Option<&T>42     pub fn top(&self) -> Option<&T> {
43         self.stack.last().map(|entry| &entry.1)
44     }
45 
top_with_depth_mut(&mut self, depth: isize) -> Option<&mut T>46     pub fn top_with_depth_mut(&mut self, depth: isize) -> Option<&mut T> {
47         self.stack
48             .last_mut()
49             .filter(|entry| entry.0 == depth)
50             .map(|entry| &mut entry.1)
51     }
52 
is_empty(&self) -> bool53     fn is_empty(&self) -> bool {
54         self.stack.is_empty()
55     }
56 }
57 
get_base_type_name(type_entry: &write::ConvertUnitEntry<Reader<'_>>) -> Result<String, Error>58 fn get_base_type_name(type_entry: &write::ConvertUnitEntry<Reader<'_>>) -> Result<String, Error> {
59     // FIXME remove recursion.
60     if let Some(die_ref) = type_entry.attr_value(gimli::DW_AT_type) {
61         if let Some(ref die) = resolve_die_ref(type_entry.read_unit, &die_ref)? {
62             if let Some(value) = die.attr_value(gimli::DW_AT_name) {
63                 return Ok(String::from(die.read_unit.attr_string(value)?.to_string()?));
64             }
65             match die.tag {
66                 gimli::DW_TAG_const_type => {
67                     return Ok(format!("const {}", get_base_type_name(die)?));
68                 }
69                 gimli::DW_TAG_pointer_type => {
70                     return Ok(format!("{}*", get_base_type_name(die)?));
71                 }
72                 gimli::DW_TAG_reference_type => {
73                     return Ok(format!("{}&", get_base_type_name(die)?));
74                 }
75                 gimli::DW_TAG_array_type => {
76                     return Ok(format!("{}[]", get_base_type_name(die)?));
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.
replace_pointer_type<'a>( wrapper_die_id: write::UnitEntryId, parent_id: write::UnitEntryId, kind: WebAssemblyPtrKind, wasm_ptr_die_ref: write::DebugInfoRef, pointer_type_entry: &write::ConvertUnitEntry<Reader<'a>>, unit: &mut write::ConvertUnit<'_, Reader<'a>>, ) -> Result<(), Error>107 fn replace_pointer_type<'a>(
108     wrapper_die_id: write::UnitEntryId,
109     parent_id: write::UnitEntryId,
110     kind: WebAssemblyPtrKind,
111     wasm_ptr_die_ref: write::DebugInfoRef,
112     pointer_type_entry: &write::ConvertUnitEntry<Reader<'a>>,
113     unit: &mut write::ConvertUnit<'_, Reader<'a>>,
114 ) -> Result<(), Error> {
115     const WASM_PTR_LEN: u8 = 4;
116 
117     macro_rules! add_tag {
118         ($parent_id:ident, $tag:expr => $die:ident as $die_id:ident { $($a:path = $v:expr),* }) => {
119             let $die_id = unit.unit.add($parent_id, $tag);
120             #[allow(unused_variables, reason = "sometimes not used below")]
121             let $die = unit.unit.get_mut($die_id);
122             $( $die.set($a, $v); )*
123         };
124     }
125 
126     // Build DW_TAG_structure_type for the wrapper:
127     //  .. DW_AT_name = "WebAssemblyPtrWrapper<T>",
128     //  .. DW_AT_byte_size = 4,
129     let name = match kind {
130         WebAssemblyPtrKind::Pointer => format!(
131             "WebAssemblyPtrWrapper<{}>",
132             get_base_type_name(pointer_type_entry)?
133         ),
134         WebAssemblyPtrKind::Reference => format!(
135             "WebAssemblyRefWrapper<{}>",
136             get_base_type_name(pointer_type_entry)?
137         ),
138     };
139     unit.unit
140         .add_reserved(wrapper_die_id, parent_id, gimli::DW_TAG_structure_type);
141     let wrapper_die = unit.unit.get_mut(wrapper_die_id);
142     wrapper_die.set(
143         gimli::DW_AT_name,
144         write::AttributeValue::StringRef(unit.strings.add(name.as_str())),
145     );
146     wrapper_die.set(
147         gimli::DW_AT_byte_size,
148         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 = pointer_type_entry.attr_value(gimli::DW_AT_type);
158     let base_type_id = if let Some(AttributeValue::UnitRef(offset)) = base_type {
159         unit.convert_unit_ref(offset).ok()
160     } else {
161         None
162     };
163 
164     // Build DW_TAG_reference_type for `T&`:
165     //  .. DW_AT_type = <base_type>
166     add_tag!(parent_id, gimli::DW_TAG_reference_type => ref_type as ref_type_id {});
167     if let Some(base_type_id) = base_type_id {
168         ref_type.set(
169             gimli::DW_AT_type,
170             write::AttributeValue::UnitRef(base_type_id),
171         );
172     }
173 
174     // Build DW_TAG_pointer_type for `T*`:
175     //  .. DW_AT_type = <base_type>
176     add_tag!(parent_id, gimli::DW_TAG_pointer_type => ptr_type as ptr_type_id {});
177     if let Some(base_type_id) = base_type_id {
178         ptr_type.set(
179             gimli::DW_AT_type,
180             write::AttributeValue::UnitRef(base_type_id),
181         );
182     }
183 
184     // Build wrapper_die's DW_TAG_template_type_parameter:
185     //  .. DW_AT_name = "T"
186     //  .. DW_AT_type = <base_type>
187     add_tag!(wrapper_die_id, gimli::DW_TAG_template_type_parameter => t_param_die as t_param_die_id {
188         gimli::DW_AT_name = write::AttributeValue::StringRef(unit.strings.add("T"))
189     });
190     if let Some(base_type_id) = base_type_id {
191         t_param_die.set(
192             gimli::DW_AT_type,
193             write::AttributeValue::UnitRef(base_type_id),
194         );
195     }
196 
197     // Build wrapper_die's DW_TAG_member for `__ptr`:
198     //  .. DW_AT_name = "__ptr"
199     //  .. DW_AT_type = <wp_die>
200     //  .. DW_AT_location = 0
201     add_tag!(wrapper_die_id, gimli::DW_TAG_member => m_die as m_die_id {
202         gimli::DW_AT_name = write::AttributeValue::StringRef(unit.strings.add("__ptr")),
203         gimli::DW_AT_type = write::AttributeValue::DebugInfoRef(wasm_ptr_die_ref),
204         gimli::DW_AT_data_member_location = write::AttributeValue::Data1(0)
205     });
206 
207     // Build wrapper_die's DW_TAG_subprogram for `ptr()`:
208     //  .. DW_AT_linkage_name = "wasmtime_resolve_vmctx_memory_ptr"
209     //  .. DW_AT_name = "ptr"
210     //  .. DW_AT_type = <ptr_type>
211     //  .. DW_TAG_formal_parameter
212     //  ..  .. DW_AT_type = <wrapper_ptr_type>
213     //  ..  .. DW_AT_artificial = 1
214     add_tag!(wrapper_die_id, gimli::DW_TAG_subprogram => deref_op_die as deref_op_die_id {
215         gimli::DW_AT_linkage_name = write::AttributeValue::StringRef(unit.strings.add(versioned_stringify_ident!(wasmtime_resolve_vmctx_memory_ptr))),
216         gimli::DW_AT_name = write::AttributeValue::StringRef(unit.strings.add("ptr")),
217         gimli::DW_AT_type = write::AttributeValue::UnitRef(ptr_type_id)
218     });
219     add_tag!(deref_op_die_id, gimli::DW_TAG_formal_parameter => deref_op_this_param as deref_op_this_param_id {
220         gimli::DW_AT_type = write::AttributeValue::UnitRef(wrapper_ptr_type_id),
221         gimli::DW_AT_artificial = write::AttributeValue::Flag(true)
222     });
223 
224     // Build wrapper_die's DW_TAG_subprogram for `operator*`:
225     //  .. DW_AT_linkage_name = "wasmtime_resolve_vmctx_memory_ptr"
226     //  .. DW_AT_name = "operator*"
227     //  .. DW_AT_type = <ref_type>
228     //  .. DW_TAG_formal_parameter
229     //  ..  .. DW_AT_type = <wrapper_ptr_type>
230     //  ..  .. DW_AT_artificial = 1
231     add_tag!(wrapper_die_id, gimli::DW_TAG_subprogram => deref_op_die as deref_op_die_id {
232         gimli::DW_AT_linkage_name = write::AttributeValue::StringRef(unit.strings.add(versioned_stringify_ident!(wasmtime_resolve_vmctx_memory_ptr))),
233         gimli::DW_AT_name = write::AttributeValue::StringRef(unit.strings.add("operator*")),
234         gimli::DW_AT_type = write::AttributeValue::UnitRef(ref_type_id)
235     });
236     add_tag!(deref_op_die_id, gimli::DW_TAG_formal_parameter => deref_op_this_param as deref_op_this_param_id {
237         gimli::DW_AT_type = write::AttributeValue::UnitRef(wrapper_ptr_type_id),
238         gimli::DW_AT_artificial = write::AttributeValue::Flag(true)
239     });
240 
241     // Build wrapper_die's DW_TAG_subprogram for `operator->`:
242     //  .. DW_AT_linkage_name = "wasmtime_resolve_vmctx_memory_ptr"
243     //  .. DW_AT_name = "operator->"
244     //  .. DW_AT_type = <ptr_type>
245     //  .. DW_TAG_formal_parameter
246     //  ..  .. DW_AT_type = <wrapper_ptr_type>
247     //  ..  .. DW_AT_artificial = 1
248     add_tag!(wrapper_die_id, gimli::DW_TAG_subprogram => deref_op_die as deref_op_die_id {
249         gimli::DW_AT_linkage_name = write::AttributeValue::StringRef(unit.strings.add(versioned_stringify_ident!(wasmtime_resolve_vmctx_memory_ptr))),
250         gimli::DW_AT_name = write::AttributeValue::StringRef(unit.strings.add("operator->")),
251         gimli::DW_AT_type = write::AttributeValue::UnitRef(ptr_type_id)
252     });
253     add_tag!(deref_op_die_id, gimli::DW_TAG_formal_parameter => deref_op_this_param as deref_op_this_param_id {
254         gimli::DW_AT_type = write::AttributeValue::UnitRef(wrapper_ptr_type_id),
255         gimli::DW_AT_artificial = write::AttributeValue::Flag(true)
256     });
257 
258     Ok(())
259 }
260 
clone_unit<'a>( compilation: &mut Compilation<'_>, module: StaticModuleIndex, unit: &mut write::ConvertUnit<'_, Reader<'a>>, root_entry: &write::ConvertUnitEntry<Reader<'a>>, skeleton_root_entry: Option<&write::ConvertUnitEntry<Reader<'a>>>, addr_tr: &AddressTransform, out_module_synthetic_unit: &ModuleSyntheticUnit, translated: &mut HashSet<usize>, isa: &dyn TargetIsa, ) -> Result<(), Error>261 pub(crate) fn clone_unit<'a>(
262     compilation: &mut Compilation<'_>,
263     module: StaticModuleIndex,
264     unit: &mut write::ConvertUnit<'_, Reader<'a>>,
265     root_entry: &write::ConvertUnitEntry<Reader<'a>>,
266     skeleton_root_entry: Option<&write::ConvertUnitEntry<Reader<'a>>>,
267     addr_tr: &AddressTransform,
268     out_module_synthetic_unit: &ModuleSyntheticUnit,
269     translated: &mut HashSet<usize>,
270     isa: &dyn TargetIsa,
271 ) -> Result<(), Error> {
272     let mut current_frame_base = InheritedAttr::new();
273     let mut current_value_range = InheritedAttr::new();
274     let mut current_scope_ranges = InheritedAttr::new();
275     let mut current_subprogram = InheritedAttr::new();
276 
277     dbi_log!("Cloning CU {:?}", log_get_cu_summary(unit.read_unit));
278 
279     if let Some(convert_program) = unit.read_line_program(Some(unit.unit.encoding()), None)? {
280         let (program, files) = clone_line_program(convert_program, addr_tr)?;
281         unit.set_line_program(program, files);
282     }
283 
284     if root_entry.tag == gimli::DW_TAG_compile_unit {
285         log_begin_input_die(root_entry);
286 
287         let out_root_id = unit.unit.root();
288         clone_die_attributes(
289             unit,
290             root_entry,
291             addr_tr,
292             None,
293             out_root_id,
294             None,
295             None,
296             EntryAttributesContext {
297                 subprograms: &mut current_subprogram,
298                 frame_base: None,
299             },
300             isa,
301         )?;
302 
303         if let Some(skeleton_root_entry) = skeleton_root_entry {
304             clone_die_attributes(
305                 unit,
306                 skeleton_root_entry,
307                 addr_tr,
308                 None,
309                 out_root_id,
310                 None,
311                 None,
312                 EntryAttributesContext {
313                     subprograms: &mut current_subprogram,
314                     frame_base: None,
315                 },
316                 isa,
317             )?;
318         }
319 
320         log_end_output_die(out_root_id, root_entry, unit);
321     } else {
322         // Can happen when the DWARF is split and we dont have the package/dwo files.
323         // This is a better user experience than errorring.
324         dbi_log!("... skipped: split DW_TAG_compile_unit entry missing");
325         return Ok(()); // empty:
326     }
327 
328     let mut entry = write::ConvertUnitEntry::null(unit.read_unit);
329     while let Some(entry_id) = unit.read_entry(&mut entry)? {
330         log_begin_input_die(&entry);
331         let (Some(out_die_id), Some(parent)) = (entry_id, entry.parent) else {
332             log_end_output_die_skipped(&entry, "unreachable");
333             continue;
334         };
335 
336         let new_stack_len = entry.depth;
337         current_frame_base.update(new_stack_len);
338         current_scope_ranges.update(new_stack_len);
339         current_value_range.update(new_stack_len);
340         current_subprogram.update(new_stack_len);
341         let range_builder = if entry.tag == gimli::DW_TAG_subprogram {
342             let range_builder = RangeInfoBuilder::from_subprogram_die(&entry, addr_tr)?;
343             if let RangeInfoBuilder::Function(func) = range_builder {
344                 let frame_info = compilation.function_frame_info(module, func);
345                 current_value_range.push(new_stack_len, frame_info);
346                 let (symbol, _) = compilation.function(module, func);
347                 translated.insert(symbol);
348                 current_scope_ranges.push(new_stack_len, range_builder.get_ranges(addr_tr));
349                 Some(range_builder)
350             } else {
351                 // FIXME current_scope_ranges.push()
352                 None
353             }
354         } else {
355             if entry.has_attr(gimli::DW_AT_high_pc) || entry.has_attr(gimli::DW_AT_ranges) {
356                 let range_builder = RangeInfoBuilder::from(&entry)?;
357                 current_scope_ranges.push(new_stack_len, range_builder.get_ranges(addr_tr));
358                 Some(range_builder)
359             } else {
360                 None
361             }
362         };
363 
364         if let Some(AttributeValue::Exprloc(expr)) = entry.attr_value(gimli::DW_AT_frame_base) {
365             if let Some(expr) = compile_expression(&expr, unit.unit.encoding(), None)? {
366                 current_frame_base.push(new_stack_len, expr);
367             }
368         }
369 
370         if entry.tag == gimli::DW_TAG_pointer_type || entry.tag == gimli::DW_TAG_reference_type {
371             // Wrap pointer types.
372             let pointer_kind = match entry.tag {
373                 gimli::DW_TAG_pointer_type => WebAssemblyPtrKind::Pointer,
374                 gimli::DW_TAG_reference_type => WebAssemblyPtrKind::Reference,
375                 _ => panic!(),
376             };
377             replace_pointer_type(
378                 out_die_id,
379                 parent,
380                 pointer_kind,
381                 out_module_synthetic_unit.wasm_ptr_die_ref(),
382                 &entry,
383                 unit,
384             )?;
385             log_end_output_die(out_die_id, &entry, unit);
386             continue;
387         }
388 
389         unit.unit.add_reserved(out_die_id, parent, entry.tag);
390 
391         clone_die_attributes(
392             unit,
393             &entry,
394             addr_tr,
395             current_value_range.top(),
396             out_die_id,
397             range_builder,
398             current_scope_ranges.top(),
399             EntryAttributesContext {
400                 subprograms: &mut current_subprogram,
401                 frame_base: current_frame_base.top(),
402             },
403             isa,
404         )?;
405 
406         // Data in WebAssembly memory always uses little-endian byte order.
407         // If the native architecture is big-endian, we need to mark all
408         // base types used to refer to WebAssembly memory as little-endian
409         // using the DW_AT_endianity attribute, so that the debugger will
410         // be able to correctly access them.
411         if entry.tag == gimli::DW_TAG_base_type && isa.endianness() == Endianness::Big {
412             let current_scope = unit.unit.get_mut(out_die_id);
413             current_scope.set(
414                 gimli::DW_AT_endianity,
415                 write::AttributeValue::Endianity(gimli::DW_END_little),
416             );
417         }
418 
419         if entry.tag == gimli::DW_TAG_subprogram && !current_scope_ranges.is_empty() {
420             append_vmctx_info(
421                 unit.unit,
422                 out_die_id,
423                 out_module_synthetic_unit.vmctx_ptr_die_ref(),
424                 addr_tr,
425                 current_value_range.top(),
426                 current_scope_ranges.top().context("range")?,
427                 unit.strings,
428                 isa,
429             )?;
430         }
431 
432         log_end_output_die(out_die_id, &entry, unit);
433     }
434     Ok(())
435 }
436