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