1 use super::address_transform::AddressTransform;
2 use super::expression::{CompiledExpression, FunctionFrameInfo};
3 use crate::debug::ModuleMemoryOffset;
4 use crate::CompiledFunctionsMetadata;
5 use anyhow::Error;
6 use cranelift_codegen::isa::TargetIsa;
7 use gimli::write;
8 use wasmtime_environ::DefinedFuncIndex;
9 use wasmtime_versioned_export_macros::versioned_stringify_ident;
10 ///Adds internal Wasm utility types DIEs such as WebAssemblyPtr and
11 /// WasmtimeVMContext.
12 ///
13 /// For unwrapping Wasm pointer, the WasmtimeVMContext has the `set()` method
14 /// that allows to control current Wasm memory to inspect.
15 /// Notice that "set_vmctx_memory" is an external/builtin subprogram that
16 /// is not part of Wasm code.
17 pub(crate) fn add_internal_types(
18     comp_unit: &mut write::Unit,
19     root_id: write::UnitEntryId,
20     out_strings: &mut write::StringTable,
21     memory_offset: &ModuleMemoryOffset,
22 ) -> (write::UnitEntryId, write::UnitEntryId) {
23     const WASM_PTR_LEN: u8 = 4;
24 
25     macro_rules! add_tag {
26         ($parent_id:ident, $tag:expr => $die:ident as $die_id:ident { $($a:path = $v:expr),* }) => {
27             let $die_id = comp_unit.add($parent_id, $tag);
28             let $die = comp_unit.get_mut($die_id);
29             $( $die.set($a, $v); )*
30         };
31     }
32 
33     // Build DW_TAG_base_type for generic `WebAssemblyPtr`.
34     //  .. DW_AT_name = "WebAssemblyPtr"
35     //  .. DW_AT_byte_size = 4
36     //  .. DW_AT_encoding = DW_ATE_unsigned
37     // let wp_die_id = comp_unit.add(root_id, gimli::DW_TAG_base_type);
38     // let wp_die = comp_unit.get_mut(wp_die_id);
39     add_tag!(root_id, gimli::DW_TAG_base_type => wp_die as wp_die_id {
40         gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("WebAssemblyPtr")),
41         gimli::DW_AT_byte_size = write::AttributeValue::Data1(WASM_PTR_LEN),
42         gimli::DW_AT_encoding = write::AttributeValue::Encoding(gimli::DW_ATE_unsigned)
43     });
44 
45     // Build DW_TAG_base_type for Wasm byte:
46     //  .. DW_AT_name = u8
47     //  .. DW_AT_encoding = DW_ATE_unsigned
48     //  .. DW_AT_byte_size = 1
49     add_tag!(root_id, gimli::DW_TAG_base_type => memory_byte_die as memory_byte_die_id {
50         gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("u8")),
51         gimli::DW_AT_encoding = write::AttributeValue::Encoding(gimli::DW_ATE_unsigned),
52         gimli::DW_AT_byte_size = write::AttributeValue::Data1(1)
53     });
54 
55     // Build DW_TAG_pointer_type that references Wasm bytes:
56     //  .. DW_AT_name = "u8*"
57     //  .. DW_AT_type = <memory_byte_die>
58     add_tag!(root_id, gimli::DW_TAG_pointer_type => memory_bytes_die as memory_bytes_die_id {
59         gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("u8*")),
60         gimli::DW_AT_type = write::AttributeValue::UnitRef(memory_byte_die_id)
61     });
62 
63     // Create artificial VMContext type and its reference for convinience viewing
64     // its fields (such as memory ref) in a debugger. Build DW_TAG_structure_type:
65     //   .. DW_AT_name = "WasmtimeVMContext"
66     let vmctx_die_id = comp_unit.add(root_id, gimli::DW_TAG_structure_type);
67     let vmctx_die = comp_unit.get_mut(vmctx_die_id);
68     vmctx_die.set(
69         gimli::DW_AT_name,
70         write::AttributeValue::StringRef(out_strings.add("WasmtimeVMContext")),
71     );
72 
73     // TODO multiple memories
74     match *memory_offset {
75         ModuleMemoryOffset::Defined(memory_offset) => {
76             // The context has defined memory: extend the WasmtimeVMContext size
77             // past the "memory" field.
78             const MEMORY_FIELD_SIZE_PLUS_PADDING: u32 = 8;
79             vmctx_die.set(
80                 gimli::DW_AT_byte_size,
81                 write::AttributeValue::Data4(memory_offset + MEMORY_FIELD_SIZE_PLUS_PADDING),
82             );
83 
84             // Define the "memory" field which is a direct pointer to allocated Wasm memory.
85             // Build DW_TAG_member:
86             //  .. DW_AT_name = "memory"
87             //  .. DW_AT_type = <memory_bytes_die>
88             //  .. DW_AT_data_member_location = `memory_offset`
89             add_tag!(vmctx_die_id, gimli::DW_TAG_member => m_die as m_die_id {
90                 gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("memory")),
91                 gimli::DW_AT_type = write::AttributeValue::UnitRef(memory_bytes_die_id),
92                 gimli::DW_AT_data_member_location = write::AttributeValue::Udata(memory_offset as u64)
93             });
94         }
95         ModuleMemoryOffset::Imported(_) => {
96             // TODO implement convinience pointer to and additional types for VMMemoryImport.
97         }
98         ModuleMemoryOffset::None => (),
99     }
100 
101     // Build DW_TAG_pointer_type for `WasmtimeVMContext*`:
102     //  .. DW_AT_name = "WasmtimeVMContext*"
103     //  .. DW_AT_type = <vmctx_die>
104     add_tag!(root_id, gimli::DW_TAG_pointer_type => vmctx_ptr_die as vmctx_ptr_die_id {
105         gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("WasmtimeVMContext*")),
106         gimli::DW_AT_type = write::AttributeValue::UnitRef(vmctx_die_id)
107     });
108 
109     // Build vmctx_die's DW_TAG_subprogram for `set` method:
110     //  .. DW_AT_linkage_name = "set_vmctx_memory"
111     //  .. DW_AT_name = "set"
112     //  .. DW_TAG_formal_parameter
113     //  ..  .. DW_AT_type = <vmctx_ptr_die>
114     //  ..  .. DW_AT_artificial = 1
115     add_tag!(vmctx_die_id, gimli::DW_TAG_subprogram => vmctx_set as vmctx_set_id {
116         gimli::DW_AT_linkage_name = write::AttributeValue::StringRef(out_strings.add(versioned_stringify_ident!(set_vmctx_memory))),
117         gimli::DW_AT_name = write::AttributeValue::StringRef(out_strings.add("set"))
118     });
119     add_tag!(vmctx_set_id, gimli::DW_TAG_formal_parameter => vmctx_set_this_param as vmctx_set_this_param_id {
120         gimli::DW_AT_type = write::AttributeValue::UnitRef(vmctx_ptr_die_id),
121         gimli::DW_AT_artificial = write::AttributeValue::Flag(true)
122     });
123 
124     (wp_die_id, vmctx_ptr_die_id)
125 }
126 
127 pub(crate) fn append_vmctx_info(
128     comp_unit: &mut write::Unit,
129     parent_id: write::UnitEntryId,
130     vmctx_die_id: write::UnitEntryId,
131     addr_tr: &AddressTransform,
132     frame_info: Option<&FunctionFrameInfo>,
133     scope_ranges: &[(u64, u64)],
134     out_strings: &mut write::StringTable,
135     isa: &dyn TargetIsa,
136 ) -> Result<(), Error> {
137     let loc = {
138         let expr = CompiledExpression::vmctx();
139         let locs = expr
140             .build_with_locals(scope_ranges, addr_tr, frame_info, isa)
141             .map(|i| {
142                 i.map(|(begin, length, data)| write::Location::StartLength {
143                     begin,
144                     length,
145                     data,
146                 })
147             })
148             .collect::<Result<Vec<_>, _>>()?;
149         let list_id = comp_unit.locations.add(write::LocationList(locs));
150         write::AttributeValue::LocationListRef(list_id)
151     };
152 
153     let var_die_id = comp_unit.add(parent_id, gimli::DW_TAG_variable);
154     let var_die = comp_unit.get_mut(var_die_id);
155     var_die.set(
156         gimli::DW_AT_name,
157         write::AttributeValue::StringRef(out_strings.add("__vmctx")),
158     );
159     var_die.set(
160         gimli::DW_AT_type,
161         write::AttributeValue::UnitRef(vmctx_die_id),
162     );
163     var_die.set(gimli::DW_AT_location, loc);
164 
165     Ok(())
166 }
167 
168 pub(crate) fn get_function_frame_info<'a, 'b, 'c>(
169     memory_offset: &ModuleMemoryOffset,
170     funcs: &'b CompiledFunctionsMetadata,
171     func_index: DefinedFuncIndex,
172 ) -> Option<FunctionFrameInfo<'a>>
173 where
174     'b: 'a,
175     'c: 'a,
176 {
177     if let Some(func) = funcs.get(func_index) {
178         let frame_info = FunctionFrameInfo {
179             value_ranges: &func.value_labels_ranges,
180             memory_offset: memory_offset.clone(),
181             sized_stack_slots: &func.sized_stack_slots,
182         };
183         Some(frame_info)
184     } else {
185         None
186     }
187 }
188