1 package binary
2
3 import (
4 "bytes"
5 "debug/dwarf"
6 "errors"
7 "fmt"
8 "io"
9
10 "github.com/tetratelabs/wazero/api"
11 "github.com/tetratelabs/wazero/internal/leb128"
12 "github.com/tetratelabs/wazero/internal/wasm"
13 "github.com/tetratelabs/wazero/internal/wasmdebug"
14 )
15
16
17
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
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
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
42 for {
43
44
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
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
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
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
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
168 type memorySizer func(minPages uint32, maxPages *uint32) (min uint32, capacity uint32, max uint32)
169
170
171
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
179 if *maxPages > wasm.MemoryLimitPages {
180 return minPages, minPages, *maxPages
181 }
182
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 }
194
View as plain text