1 use gimli::write::{
2     AttributeValue, LineProgram, Reference, StringTable, Unit, UnitEntryId, UnitId, UnitTable,
3 };
4 use wasmtime_environ::StaticModuleIndex;
5 use wasmtime_versioned_export_macros::versioned_stringify_ident;
6 
7 use crate::debug::{Compilation, ModuleMemoryOffset};
8 
9 /// Internal Wasm utility types DIEs such as WebAssemblyPtr and WasmtimeVMContext.
10 ///
11 /// For unwrapping Wasm pointer, the WasmtimeVMContext has the `set()` method
12 /// that allows to control current Wasm memory to inspect.
13 /// Notice that "wasmtime_set_vmctx_memory" is an external/builtin subprogram
14 /// that is not part of Wasm code.
15 ///
16 /// This CU is currently per-module since VMContext memory structure is per-module;
17 /// some of the contained types could be made global (per-Compilation).
18 pub struct ModuleSyntheticUnit {
19     unit_id: UnitId,
20     vmctx_ptr_die_id: UnitEntryId,
21     wasm_ptr_die_id: UnitEntryId,
22 }
23 
24 macro_rules! add_tag {
25     ($unit:ident, $parent_id:ident, $tag:expr => $die:ident as $die_id:ident { $($a:path = $v:expr),* }) => {
26         let $die_id = $unit.add($parent_id, $tag);
27         let $die = $unit.get_mut($die_id);
28         $( $die.set($a, $v); )*
29     };
30 }
31 
32 impl ModuleSyntheticUnit {
33     pub fn new(
34         module: StaticModuleIndex,
35         compilation: &Compilation<'_>,
36         encoding: gimli::Encoding,
37         out_units: &mut UnitTable,
38         out_strings: &mut StringTable,
39     ) -> Self {
40         let unit_id = Self::create_unit(encoding, out_units, out_strings);
41         let unit = out_units.get_mut(unit_id);
42         let vmctx_ptr_die_id = Self::create_vmctx_ptr_die(module, compilation, unit, out_strings);
43         let wasm_ptr_die_id = Self::create_wasm_ptr_die(unit, out_strings);
44 
45         Self {
46             unit_id,
47             vmctx_ptr_die_id,
48             wasm_ptr_die_id,
49         }
50     }
51 
52     pub fn vmctx_ptr_die_ref(&self) -> Reference {
53         Reference::Entry(self.unit_id, self.vmctx_ptr_die_id)
54     }
55 
56     pub fn wasm_ptr_die_ref(&self) -> Reference {
57         Reference::Entry(self.unit_id, self.wasm_ptr_die_id)
58     }
59 
60     fn create_unit(
61         encoding: gimli::Encoding,
62         out_units: &mut UnitTable,
63         out_strings: &mut StringTable,
64     ) -> UnitId {
65         let unit_id = out_units.add(Unit::new(encoding, LineProgram::none()));
66         let unit = out_units.get_mut(unit_id);
67         let unit_die = unit.get_mut(unit.root());
68         unit_die.set(
69             gimli::DW_AT_name,
70             AttributeValue::StringRef(out_strings.add("WasmtimeModuleSyntheticUnit")),
71         );
72         unit_die.set(
73             gimli::DW_AT_producer,
74             AttributeValue::StringRef(out_strings.add("wasmtime")),
75         );
76         unit_die.set(
77             gimli::DW_AT_language,
78             AttributeValue::Language(gimli::DW_LANG_C11),
79         );
80         unit_id
81     }
82 
83     fn create_vmctx_ptr_die(
84         module: StaticModuleIndex,
85         compilation: &Compilation<'_>,
86         unit: &mut Unit,
87         out_strings: &mut StringTable,
88     ) -> UnitEntryId {
89         // Build DW_TAG_base_type for Wasm byte:
90         //  .. DW_AT_name = u8
91         //  .. DW_AT_encoding = DW_ATE_unsigned
92         //  .. DW_AT_byte_size = 1
93         let root_id = unit.root();
94         add_tag!(unit, root_id, gimli::DW_TAG_base_type => memory_byte_die as memory_byte_die_id {
95             gimli::DW_AT_name = AttributeValue::StringRef(out_strings.add("u8")),
96             gimli::DW_AT_encoding = AttributeValue::Encoding(gimli::DW_ATE_unsigned),
97             gimli::DW_AT_byte_size = AttributeValue::Data1(1)
98         });
99 
100         // Build DW_TAG_pointer_type that references Wasm bytes:
101         //  .. DW_AT_name = "u8*"
102         //  .. DW_AT_type = <memory_byte_die>
103         add_tag!(unit, root_id, gimli::DW_TAG_pointer_type => memory_bytes_die as memory_bytes_die_id {
104             gimli::DW_AT_name = AttributeValue::StringRef(out_strings.add("u8*")),
105             gimli::DW_AT_type = AttributeValue::UnitRef(memory_byte_die_id)
106         });
107 
108         // Create artificial VMContext type and its reference for convenience viewing
109         // its fields (such as memory ref) in a debugger. Build DW_TAG_structure_type:
110         //   .. DW_AT_name = "WasmtimeVMContext"
111         let vmctx_die_id = unit.add(root_id, gimli::DW_TAG_structure_type);
112         let vmctx_die = unit.get_mut(vmctx_die_id);
113         vmctx_die.set(
114             gimli::DW_AT_name,
115             AttributeValue::StringRef(out_strings.add("WasmtimeVMContext")),
116         );
117 
118         // TODO multiple memories
119         match compilation.module_memory_offsets[module] {
120             ModuleMemoryOffset::Defined(memory_offset) => {
121                 // The context has defined memory: extend the WasmtimeVMContext size
122                 // past the "memory" field.
123                 const MEMORY_FIELD_SIZE_PLUS_PADDING: u32 = 8;
124                 vmctx_die.set(
125                     gimli::DW_AT_byte_size,
126                     AttributeValue::Data4(memory_offset + MEMORY_FIELD_SIZE_PLUS_PADDING),
127                 );
128 
129                 // Define the "memory" field which is a direct pointer to allocated Wasm memory.
130                 // Build DW_TAG_member:
131                 //  .. DW_AT_name = "memory"
132                 //  .. DW_AT_type = <memory_bytes_die>
133                 //  .. DW_AT_data_member_location = `memory_offset`
134                 add_tag!(unit, vmctx_die_id, gimli::DW_TAG_member => m_die as m_die_id {
135                     gimli::DW_AT_name = AttributeValue::StringRef(out_strings.add("memory")),
136                     gimli::DW_AT_type = AttributeValue::UnitRef(memory_bytes_die_id),
137                     gimli::DW_AT_data_member_location = AttributeValue::Udata(memory_offset as u64)
138                 });
139             }
140             ModuleMemoryOffset::Imported { .. } => {
141                 // TODO implement convenience pointer to and additional types for VMMemoryImport.
142             }
143             ModuleMemoryOffset::None => (),
144         }
145 
146         // Build DW_TAG_pointer_type for `WasmtimeVMContext*`:
147         //  .. DW_AT_name = "WasmtimeVMContext*"
148         //  .. DW_AT_type = <vmctx_die>
149         add_tag!(unit, root_id, gimli::DW_TAG_pointer_type => vmctx_ptr_die as vmctx_ptr_die_id {
150             gimli::DW_AT_name = AttributeValue::StringRef(out_strings.add("WasmtimeVMContext*")),
151             gimli::DW_AT_type = AttributeValue::UnitRef(vmctx_die_id)
152         });
153 
154         // Build vmctx_die's DW_TAG_subprogram for `set` method:
155         //  .. DW_AT_linkage_name = "wasmtime_set_vmctx_memory"
156         //  .. DW_AT_name = "set"
157         //  .. DW_TAG_formal_parameter
158         //  ..  .. DW_AT_type = <vmctx_ptr_die>
159         //  ..  .. DW_AT_artificial = 1
160         add_tag!(unit, vmctx_die_id, gimli::DW_TAG_subprogram => vmctx_set as vmctx_set_id {
161             gimli::DW_AT_linkage_name = AttributeValue::StringRef(out_strings.add(versioned_stringify_ident!(wasmtime_set_vmctx_memory))),
162             gimli::DW_AT_name = AttributeValue::StringRef(out_strings.add("set"))
163         });
164         add_tag!(unit, vmctx_set_id, gimli::DW_TAG_formal_parameter => vmctx_set_this_param as vmctx_set_this_param_id {
165             gimli::DW_AT_type = AttributeValue::UnitRef(vmctx_ptr_die_id),
166             gimli::DW_AT_artificial = AttributeValue::Flag(true)
167         });
168 
169         vmctx_ptr_die_id
170     }
171 
172     fn create_wasm_ptr_die(unit: &mut Unit, out_strings: &mut StringTable) -> UnitEntryId {
173         // Build DW_TAG_base_type for generic `WebAssemblyPtr`.
174         //  .. DW_AT_name = "WebAssemblyPtr"
175         //  .. DW_AT_byte_size = 4
176         //  .. DW_AT_encoding = DW_ATE_unsigned
177         const WASM_PTR_LEN: u8 = 4;
178         let root_id = unit.root();
179         add_tag!(unit, root_id, gimli::DW_TAG_base_type => wp_die as wp_die_id {
180             gimli::DW_AT_name = AttributeValue::StringRef(out_strings.add("WebAssemblyPtr")),
181             gimli::DW_AT_byte_size = AttributeValue::Data1(WASM_PTR_LEN),
182             gimli::DW_AT_encoding = AttributeValue::Encoding(gimli::DW_ATE_unsigned)
183         });
184 
185         wp_die_id
186     }
187 }
188