1 use super::address_transform::AddressTransform; 2 use super::expression::{CompiledExpression, FunctionFrameInfo}; 3 use crate::debug::ModuleMemoryOffset; 4 use anyhow::Error; 5 use cranelift_codegen::isa::TargetIsa; 6 use gimli::write; 7 use wasmtime_versioned_export_macros::versioned_stringify_ident; 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 control current Wasm memory to inspect. 14 /// Notice that "set_vmctx_memory" is an external/builtin subprogram that 15 /// is not part of Wasm code. 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 memory_offset: &ModuleMemoryOffset, 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::UnitRef(memory_byte_die_id) 60 }); 61 62 // Create artificial VMContext type and its reference for convenience 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 *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::UnitRef(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 convenience 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::UnitRef(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(versioned_stringify_ident!(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::UnitRef(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 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::UnitRef(vmctx_die_id), 161 ); 162 var_die.set(gimli::DW_AT_location, loc); 163 164 Ok(()) 165 } 166