github.com/bananabytelabs/wazero@v0.0.0-20240105073314-54b22a776da8/internal/wasm/binary/decoder.go (about) 1 package binary 2 3 import ( 4 "bytes" 5 "debug/dwarf" 6 "errors" 7 "fmt" 8 "io" 9 10 "github.com/bananabytelabs/wazero/api" 11 "github.com/bananabytelabs/wazero/internal/leb128" 12 "github.com/bananabytelabs/wazero/internal/wasm" 13 "github.com/bananabytelabs/wazero/internal/wasmdebug" 14 ) 15 16 // DecodeModule implements wasm.DecodeModule for the WebAssembly 1.0 (20191205) Binary Format 17 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-format%E2%91%A0 18 func DecodeModule( 19 binary []byte, 20 enabledFeatures api.CoreFeatures, 21 memoryLimitPages uint32, 22 memoryCapacityFromMax, 23 dwarfEnabled, storeCustomSections bool, 24 ) (*wasm.Module, error) { 25 r := bytes.NewReader(binary) 26 27 // Magic number. 28 buf := make([]byte, 4) 29 if _, err := io.ReadFull(r, buf); err != nil || !bytes.Equal(buf, Magic) { 30 return nil, ErrInvalidMagicNumber 31 } 32 33 // Version. 34 if _, err := io.ReadFull(r, buf); err != nil || !bytes.Equal(buf, version) { 35 return nil, ErrInvalidVersion 36 } 37 38 memSizer := newMemorySizer(memoryLimitPages, memoryCapacityFromMax) 39 40 m := &wasm.Module{} 41 var info, line, str, abbrev, ranges []byte // For DWARF Data. 42 for { 43 // TODO: except custom sections, all others are required to be in order, but we aren't checking yet. 44 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#modules%E2%91%A0%E2%93%AA 45 sectionID, err := r.ReadByte() 46 if err == io.EOF { 47 break 48 } else if err != nil { 49 return nil, fmt.Errorf("read section id: %w", err) 50 } 51 52 sectionSize, _, err := leb128.DecodeUint32(r) 53 if err != nil { 54 return nil, fmt.Errorf("get size of section %s: %v", wasm.SectionIDName(sectionID), err) 55 } 56 57 sectionContentStart := r.Len() 58 switch sectionID { 59 case wasm.SectionIDCustom: 60 // First, validate the section and determine if the section for this name has already been set 61 name, nameSize, decodeErr := decodeUTF8(r, "custom section name") 62 if decodeErr != nil { 63 err = decodeErr 64 break 65 } else if sectionSize < nameSize { 66 err = fmt.Errorf("malformed custom section %s", name) 67 break 68 } else if name == "name" && m.NameSection != nil { 69 err = fmt.Errorf("redundant custom section %s", name) 70 break 71 } 72 73 // Now, either decode the NameSection or CustomSection 74 limit := sectionSize - nameSize 75 76 var c *wasm.CustomSection 77 if name != "name" { 78 if storeCustomSections || dwarfEnabled { 79 c, err = decodeCustomSection(r, name, uint64(limit)) 80 if err != nil { 81 return nil, fmt.Errorf("failed to read custom section name[%s]: %w", name, err) 82 } 83 m.CustomSections = append(m.CustomSections, c) 84 if dwarfEnabled { 85 switch name { 86 case ".debug_info": 87 info = c.Data 88 case ".debug_line": 89 line = c.Data 90 case ".debug_str": 91 str = c.Data 92 case ".debug_abbrev": 93 abbrev = c.Data 94 case ".debug_ranges": 95 ranges = c.Data 96 } 97 } 98 } else { 99 if _, err = io.CopyN(io.Discard, r, int64(limit)); err != nil { 100 return nil, fmt.Errorf("failed to skip name[%s]: %w", name, err) 101 } 102 } 103 } else { 104 m.NameSection, err = decodeNameSection(r, uint64(limit)) 105 } 106 case wasm.SectionIDType: 107 m.TypeSection, err = decodeTypeSection(enabledFeatures, r) 108 case wasm.SectionIDImport: 109 m.ImportSection, m.ImportPerModule, m.ImportFunctionCount, m.ImportGlobalCount, m.ImportMemoryCount, m.ImportTableCount, err = decodeImportSection(r, memSizer, memoryLimitPages, enabledFeatures) 110 if err != nil { 111 return nil, err // avoid re-wrapping the error. 112 } 113 case wasm.SectionIDFunction: 114 m.FunctionSection, err = decodeFunctionSection(r) 115 case wasm.SectionIDTable: 116 m.TableSection, err = decodeTableSection(r, enabledFeatures) 117 case wasm.SectionIDMemory: 118 m.MemorySection, err = decodeMemorySection(r, memSizer, memoryLimitPages) 119 case wasm.SectionIDGlobal: 120 if m.GlobalSection, err = decodeGlobalSection(r, enabledFeatures); err != nil { 121 return nil, err // avoid re-wrapping the error. 122 } 123 case wasm.SectionIDExport: 124 m.ExportSection, m.Exports, err = decodeExportSection(r) 125 case wasm.SectionIDStart: 126 if m.StartSection != nil { 127 return nil, errors.New("multiple start sections are invalid") 128 } 129 m.StartSection, err = decodeStartSection(r) 130 case wasm.SectionIDElement: 131 m.ElementSection, err = decodeElementSection(r, enabledFeatures) 132 case wasm.SectionIDCode: 133 m.CodeSection, err = decodeCodeSection(r) 134 case wasm.SectionIDData: 135 m.DataSection, err = decodeDataSection(r, enabledFeatures) 136 case wasm.SectionIDDataCount: 137 if err := enabledFeatures.RequireEnabled(api.CoreFeatureBulkMemoryOperations); err != nil { 138 return nil, fmt.Errorf("data count section not supported as %v", err) 139 } 140 m.DataCountSection, err = decodeDataCountSection(r) 141 default: 142 err = ErrInvalidSectionID 143 } 144 145 readBytes := sectionContentStart - r.Len() 146 if err == nil && int(sectionSize) != readBytes { 147 err = fmt.Errorf("invalid section length: expected to be %d but got %d", sectionSize, readBytes) 148 } 149 150 if err != nil { 151 return nil, fmt.Errorf("section %s: %v", wasm.SectionIDName(sectionID), err) 152 } 153 } 154 155 if dwarfEnabled { 156 d, _ := dwarf.New(abbrev, nil, nil, info, line, nil, ranges, str) 157 m.DWARFLines = wasmdebug.NewDWARFLines(d) 158 } 159 160 functionCount, codeCount := m.SectionElementCount(wasm.SectionIDFunction), m.SectionElementCount(wasm.SectionIDCode) 161 if functionCount != codeCount { 162 return nil, fmt.Errorf("function and code section have inconsistent lengths: %d != %d", functionCount, codeCount) 163 } 164 return m, nil 165 } 166 167 // memorySizer derives min, capacity and max pages from decoded wasm. 168 type memorySizer func(minPages uint32, maxPages *uint32) (min uint32, capacity uint32, max uint32) 169 170 // newMemorySizer sets capacity to minPages unless max is defined and 171 // memoryCapacityFromMax is true. 172 func newMemorySizer(memoryLimitPages uint32, memoryCapacityFromMax bool) memorySizer { 173 return func(minPages uint32, maxPages *uint32) (min, capacity, max uint32) { 174 if maxPages != nil { 175 if memoryCapacityFromMax { 176 return minPages, *maxPages, *maxPages 177 } 178 // This is an invalid value: let it propagate, we will fail later. 179 if *maxPages > wasm.MemoryLimitPages { 180 return minPages, minPages, *maxPages 181 } 182 // This is a valid value, but it goes over the run-time limit: return the limit. 183 if *maxPages > memoryLimitPages { 184 return minPages, minPages, memoryLimitPages 185 } 186 return minPages, minPages, *maxPages 187 } 188 if memoryCapacityFromMax { 189 return minPages, memoryLimitPages, memoryLimitPages 190 } 191 return minPages, minPages, memoryLimitPages 192 } 193 }