wasmtime_internal_cranelift/debug/transform/
synthetic.rs

1use gimli::write::{
2    AttributeValue, LineProgram, Reference, StringTable, Unit, UnitEntryId, UnitId, UnitTable,
3};
4use wasmtime_environ::StaticModuleIndex;
5use wasmtime_versioned_export_macros::versioned_stringify_ident;
6
7use 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).
18pub struct ModuleSyntheticUnit {
19    unit_id: UnitId,
20    vmctx_ptr_die_id: UnitEntryId,
21    wasm_ptr_die_id: UnitEntryId,
22}
23
24macro_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
32impl 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}