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