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