github.com/machinefi/w3bstream@v1.6.5-rc9.0.20240426031326-b8c7c4876e72/pkg/modules/wasm/runtime/wasmtime/debug.go (about) 1 package wasmtime 2 3 import ( 4 "bytes" 5 "debug/dwarf" 6 "fmt" 7 "log" 8 9 "github.com/pkg/errors" 10 ) 11 12 const ( 13 SecCustom byte = 0 14 SecType byte = 1 15 SecImport byte = 2 16 SecFunction byte = 3 17 SecTable byte = 4 18 SecMemory byte = 5 19 SecGlobal byte = 6 20 SecExport byte = 7 21 SecStart byte = 8 22 SecElement byte = 9 23 SecCode byte = 10 24 SecData byte = 11 25 ) 26 27 var secTypeNames = map[byte]string{ 28 SecCustom: "CUSTOM", 29 SecType: "TYPE", 30 SecImport: "IMPORT", 31 SecFunction: "FUNCTION", 32 SecTable: "TABLE", 33 SecMemory: "MEMORY", 34 SecGlobal: "GLOBAL", 35 SecExport: "EXPORT", 36 SecStart: "START", 37 SecElement: "ELEMENT", 38 SecCode: "CODE", 39 SecData: "DATA", 40 } 41 42 type DwarfInfo struct { 43 data *dwarf.Data 44 codeSectionOffset int 45 lineReader *dwarf.LineReader 46 } 47 48 func (d *DwarfInfo) GetLineReader() *dwarf.LineReader { 49 if d.lineReader != nil { 50 d.lineReader.Reset() 51 return d.lineReader 52 } 53 54 // Get the line table for the first CU. 55 cu, err := d.data.Reader().Next() 56 if err != nil { 57 log.Printf("[wasmtime][debug] getLineReader fail to do d.Reader().Next(), err: %v", err) 58 } 59 60 lineReader, err := d.data.LineReader(cu) 61 if err != nil { 62 log.Printf("[wasmtime][debug] getLineReader fail to do d.LineReader(), err: %v", err) 63 } 64 65 d.lineReader = lineReader 66 67 return d.lineReader 68 } 69 70 func (d *DwarfInfo) SeekPC(pc uint64) *dwarf.LineEntry { 71 var line dwarf.LineEntry 72 73 lr := d.GetLineReader() 74 if lr == nil { 75 return nil 76 } 77 78 // https://yurydelendik.github.io/webassembly-dwarf/#pc 79 pc -= uint64(d.codeSectionOffset) 80 81 if err := lr.SeekPC(pc, &line); err != nil { 82 return nil 83 } 84 85 return &line 86 } 87 88 func ParseDwarf(data []byte) *DwarfInfo { 89 customSections, codeSectionOffset, err := LoadCustomSections(data) 90 if err != nil { 91 return nil 92 } 93 94 dwarfData, err := dwarf.New( 95 customSections[".debug_abbrev"].data, 96 customSections[".debug_aranges"].data, 97 customSections[".debug_frame"].data, 98 customSections[".debug_info"].data, 99 customSections[".debug_line"].data, 100 customSections[".debug_pubnames"].data, 101 customSections[".debug_ranges"].data, 102 customSections[".debug_str"].data) 103 if err != nil { 104 log.Printf("[wasmtime][debug] dwarf.New failed: %v", err) 105 return nil 106 } 107 108 return &DwarfInfo{ 109 data: dwarfData, 110 codeSectionOffset: codeSectionOffset, 111 } 112 } 113 114 type CustomSection struct { 115 name string 116 data []byte 117 } 118 119 func LoadCustomSections(data []byte) (map[string]CustomSection, int, error) { 120 // Index of first section 121 i := 8 122 codeSectionOffset := 0 123 124 // Skip preamble 125 if !bytes.Equal(data[0:i], []byte{0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00}) { 126 return nil, 0, errors.New("invalid preamble. Only Wasm v1 files are supported") 127 } 128 129 customSections := map[string]CustomSection{} 130 // Read all sections 131 for i < len(data) { 132 sectionType := data[i] 133 if _, ok := secTypeNames[sectionType]; !ok { 134 return nil, 0, fmt.Errorf("section type 0x%02X not supported", sectionType) 135 } 136 i++ 137 138 sectionDataSize, n := ReadULEB128(data[i:]) 139 i += n 140 141 sectionName := "" 142 143 if sectionType == SecCustom { 144 customSection := NewCustomSection(data[i : i+sectionDataSize]) 145 sectionName = customSection.name 146 customSections[customSection.name] = customSection 147 } 148 149 if sectionType == SecCode { 150 codeSectionOffset = i 151 } 152 153 if true /* debug mode */ { 154 if len(sectionName) > 0 { 155 log.Printf("[wasmtime][debug] LoadCustomSections %v(%X) %v: data start at 0x%X, data size 0x%X", secTypeNames[sectionType], sectionType, sectionName, i, sectionDataSize) 156 } else { 157 log.Printf("[wasmtime][debug] LoadCustomSections %v(%X): data start at 0x%X, data size 0x%X", secTypeNames[sectionType], sectionType, i, sectionDataSize) 158 } 159 } 160 161 i += sectionDataSize 162 } 163 164 return customSections, codeSectionOffset, nil 165 } 166 167 // NewCustomSection properly read name and data from a custom section. 168 func NewCustomSection(data []byte) CustomSection { 169 nameLength, n := ReadULEB128(data) 170 171 return CustomSection{ 172 name: string(data[n : n+nameLength]), 173 data: data[nameLength+n:], 174 } 175 } 176 177 // ReadULEB128 read a uLEB128 at the starting position. Returns (number read, number of bytes read). 178 func ReadULEB128(data []byte) (int, int) { 179 length := 1 180 for data[length-1]&0x80 > 0 { 181 length++ 182 } 183 184 n := int(DecodeULeb128(data[:length+1])) 185 186 return n, length 187 } 188 189 // DecodeULeb128 decodes an unsigned LEB128 value to an unsigned int32 value. Returns the result as a uint32. 190 func DecodeULeb128(value []byte) uint32 { 191 var result uint32 192 var ctr uint 193 var cur byte = 0x80 194 195 for (cur&0x80 == 0x80) && ctr < 5 { 196 cur = value[ctr] & 0xff 197 result += uint32(cur&0x7f) << (ctr * 7) 198 ctr++ 199 } 200 201 return result 202 }