1package wasm
2
3import (
4 "bytes"
5 "crypto/sha256"
6 "encoding/binary"
7 "errors"
8 "fmt"
9 "io"
10 "sort"
11 "strings"
12 "sync"
13
14 "github.com/tetratelabs/wazero/api"
15 "github.com/tetratelabs/wazero/experimental"
16 "github.com/tetratelabs/wazero/internal/ieee754"
17 "github.com/tetratelabs/wazero/internal/leb128"
18 "github.com/tetratelabs/wazero/internal/wasmdebug"
19)
20
21// Module is a WebAssembly binary representation.
22// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#modules%E2%91%A8
23//
24// Differences from the specification:
25// * NameSection is the only key ("name") decoded from the SectionIDCustom.
26// * ExportSection is represented as a map for lookup convenience.
27// * Code.GoFunc is contains any go `func`. It may be present when Code.Body is not.
28type Module struct {
29 // TypeSection contains the unique FunctionType of functions imported or defined in this module.
30 //
31 // Note: Currently, there is no type ambiguity in the index as WebAssembly 1.0 only defines function type.
32 // In the future, other types may be introduced to support CoreFeatures such as module linking.
33 //
34 // Note: In the Binary Format, this is SectionIDType.
35 //
36 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#types%E2%91%A0%E2%91%A0
37 TypeSection []FunctionType
38
39 // ImportSection contains imported functions, tables, memories or globals required for instantiation
40 // (Store.Instantiate).
41 //
42 // Note: there are no unique constraints relating to the two-level namespace of Import.Module and Import.Name.
43 //
44 // Note: In the Binary Format, this is SectionIDImport.
45 //
46 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#import-section%E2%91%A0
47 ImportSection []Import
48 // ImportFunctionCount ImportGlobalCount ImportMemoryCount, and ImportTableCount are
49 // the cached import count per ExternType set during decoding.
50 ImportFunctionCount,
51 ImportGlobalCount,
52 ImportMemoryCount,
53 ImportTableCount Index
54 // ImportPerModule maps a module name to the list of Import to be imported from the module.
55 // This is used to do fast import resolution during instantiation.
56 ImportPerModule map[string][]*Import
57
58 // FunctionSection contains the index in TypeSection of each function defined in this module.
59 //
60 // Note: The function Index space begins with imported functions and ends with those defined in this module.
61 // For example, if there are two imported functions and one defined in this module, the function Index 3 is defined
62 // in this module at FunctionSection[0].
63 //
64 // Note: FunctionSection is index correlated with the CodeSection. If given the same position, e.g. 2, a function
65 // type is at TypeSection[FunctionSection[2]], while its locals and body are at CodeSection[2].
66 //
67 // Note: In the Binary Format, this is SectionIDFunction.
68 //
69 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#function-section%E2%91%A0
70 FunctionSection []Index
71
72 // TableSection contains each table defined in this module.
73 //
74 // Note: The table Index space begins with imported tables and ends with those defined in this module.
75 // For example, if there are two imported tables and one defined in this module, the table Index 3 is defined in
76 // this module at TableSection[0].
77 //
78 // Note: Version 1.0 (20191205) of the WebAssembly spec allows at most one table definition per module, so the
79 // length of the TableSection can be zero or one, and can only be one if there is no imported table.
80 //
81 // Note: In the Binary Format, this is SectionIDTable.
82 //
83 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#table-section%E2%91%A0
84 TableSection []Table
85
86 // MemorySection contains each memory defined in this module.
87 //
88 // Note: The memory Index space begins with imported memories and ends with those defined in this module.
89 // For example, if there are two imported memories and one defined in this module, the memory Index 3 is defined in
90 // this module at TableSection[0].
91 //
92 // Note: Version 1.0 (20191205) of the WebAssembly spec allows at most one memory definition per module, so the
93 // length of the MemorySection can be zero or one, and can only be one if there is no imported memory.
94 //
95 // Note: In the Binary Format, this is SectionIDMemory.
96 //
97 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#memory-section%E2%91%A0
98 MemorySection *Memory
99
100 // GlobalSection contains each global defined in this module.
101 //
102 // Global indexes are offset by any imported globals because the global index begins with imports, followed by
103 // ones defined in this module. For example, if there are two imported globals and three defined in this module, the
104 // global at index 3 is defined in this module at GlobalSection[0].
105 //
106 // Note: In the Binary Format, this is SectionIDGlobal.
107 //
108 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#global-section%E2%91%A0
109 GlobalSection []Global
110
111 // ExportSection contains each export defined in this module.
112 //
113 // Note: In the Binary Format, this is SectionIDExport.
114 //
115 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#exports%E2%91%A0
116 ExportSection []Export
117 // Exports maps a name to Export, and is convenient for fast look up of exported instances at runtime.
118 // Each item of this map points to an element of ExportSection.
119 Exports map[string]*Export
120
121 // StartSection is the index of a function to call before returning from Store.Instantiate.
122 //
123 // Note: The index here is not the position in the FunctionSection, rather in the function index, which
124 // begins with imported functions.
125 //
126 // Note: In the Binary Format, this is SectionIDStart.
127 //
128 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#start-section%E2%91%A0
129 StartSection *Index
130
131 // Note: In the Binary Format, this is SectionIDElement.
132 ElementSection []ElementSegment
133
134 // CodeSection is index-correlated with FunctionSection and contains each
135 // function's locals and body.
136 //
137 // When present, the HostFunctionSection of the same index must be nil.
138 //
139 // Note: In the Binary Format, this is SectionIDCode.
140 //
141 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#code-section%E2%91%A0
142 CodeSection []Code
143
144 // Note: In the Binary Format, this is SectionIDData.
145 DataSection []DataSegment
146
147 // NameSection is set when the SectionIDCustom "name" was successfully decoded from the binary format.
148 //
149 // Note: This is the only SectionIDCustom defined in the WebAssembly 1.0 (20191205) Binary Format.
150 // Others are skipped as they are not used in wazero.
151 //
152 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#name-section%E2%91%A0
153 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#custom-section%E2%91%A0
154 NameSection *NameSection
155
156 // CustomSections are set when the SectionIDCustom other than "name" were successfully decoded from the binary format.
157 //
158 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#custom-section%E2%91%A0
159 CustomSections []*CustomSection
160
161 // DataCountSection is the optional section and holds the number of data segments in the data section.
162 //
163 // Note: This may exist in WebAssembly 2.0 or WebAssembly 1.0 with CoreFeatureBulkMemoryOperations.
164 // See https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/binary/modules.html#data-count-section
165 // See https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/appendix/changes.html#bulk-memory-and-table-instructions
166 DataCountSection *uint32
167
168 // ID is the sha256 value of the source wasm plus the configurations which affect the runtime representation of
169 // Wasm binary. This is only used for caching.
170 ID ModuleID
171
172 // IsHostModule true if this is the host module, false otherwise.
173 IsHostModule bool
174
175 // functionDefinitionSectionInitOnce guards FunctionDefinitionSection so that it is initialized exactly once.
176 functionDefinitionSectionInitOnce sync.Once
177
178 // FunctionDefinitionSection is a wazero-specific section.
179 FunctionDefinitionSection []FunctionDefinition
180
181 // MemoryDefinitionSection is a wazero-specific section.
182 MemoryDefinitionSection []MemoryDefinition
183
184 // DWARFLines is used to emit DWARF based stack trace. This is created from the multiple custom sections
185 // as described in https://yurydelendik.github.io/webassembly-dwarf/, though it is not specified in the Wasm
186 // specification: https://github.com/WebAssembly/debugging/issues/1
187 DWARFLines *wasmdebug.DWARFLines
188}
189
190// ModuleID represents sha256 hash value uniquely assigned to Module.
191type ModuleID = [sha256.Size]byte
192
193// The wazero specific limitation described at RATIONALE.md.
194// TL;DR; We multiply by 8 (to get offsets in bytes) and the multiplication result must be less than 32bit max
195const (
196 MaximumGlobals = uint32(1 << 27)
197 MaximumFunctionIndex = uint32(1 << 27)
198 MaximumTableIndex = uint32(1 << 27)
199)
200
201// AssignModuleID calculates a sha256 checksum on `wasm` and other args, and set Module.ID to the result.
202// See the doc on Module.ID on what it's used for.
203func (m *Module) AssignModuleID(wasm []byte, listeners []experimental.FunctionListener, withEnsureTermination bool) {
204 h := sha256.New()
205 h.Write(wasm)
206 // Use the pre-allocated space backed by m.ID below.
207
208 // Write the existence of listeners to the checksum per function.
209 for i, l := range listeners {
210 binary.LittleEndian.PutUint32(m.ID[:], uint32(i))
211 m.ID[4] = boolToByte(l != nil)
212 h.Write(m.ID[:5])
213 }
214 // Write the flag of ensureTermination to the checksum.
215 m.ID[0] = boolToByte(withEnsureTermination)
216 h.Write(m.ID[:1])
217 // Get checksum by passing the slice underlying m.ID.
218 h.Sum(m.ID[:0])
219}
220
221func boolToByte(b bool) (ret byte) {
222 if b {
223 ret = 1
224 }
225 return
226}
227
228// typeOfFunction returns the wasm.FunctionType for the given function space index or nil.
229func (m *Module) typeOfFunction(funcIdx Index) *FunctionType {
230 typeSectionLength, importedFunctionCount := uint32(len(m.TypeSection)), m.ImportFunctionCount
231 if funcIdx < importedFunctionCount {
232 // Imports are not exclusively functions. This is the current function index in the loop.
233 cur := Index(0)
234 for i := range m.ImportSection {
235 imp := &m.ImportSection[i]
236 if imp.Type != ExternTypeFunc {
237 continue
238 }
239 if funcIdx == cur {
240 if imp.DescFunc >= typeSectionLength {
241 return nil
242 }
243 return &m.TypeSection[imp.DescFunc]
244 }
245 cur++
246 }
247 }
248
249 funcSectionIdx := funcIdx - m.ImportFunctionCount
250 if funcSectionIdx >= uint32(len(m.FunctionSection)) {
251 return nil
252 }
253 typeIdx := m.FunctionSection[funcSectionIdx]
254 if typeIdx >= typeSectionLength {
255 return nil
256 }
257 return &m.TypeSection[typeIdx]
258}
259
260func (m *Module) Validate(enabledFeatures api.CoreFeatures) error {
261 for i := range m.TypeSection {
262 tp := &m.TypeSection[i]
263 tp.CacheNumInUint64()
264 }
265
266 if err := m.validateStartSection(); err != nil {
267 return err
268 }
269
270 functions, globals, memory, tables, err := m.AllDeclarations()
271 if err != nil {
272 return err
273 }
274
275 if err = m.validateImports(enabledFeatures); err != nil {
276 return err
277 }
278
279 if err = m.validateGlobals(globals, uint32(len(functions)), MaximumGlobals); err != nil {
280 return err
281 }
282
283 if err = m.validateMemory(memory, globals, enabledFeatures); err != nil {
284 return err
285 }
286
287 if err = m.validateExports(enabledFeatures, functions, globals, memory, tables); err != nil {
288 return err
289 }
290
291 if m.CodeSection != nil {
292 if err = m.validateFunctions(enabledFeatures, functions, globals, memory, tables, MaximumFunctionIndex); err != nil {
293 return err
294 }
295 } // No need to validate host functions as NewHostModule validates
296
297 if err = m.validateTable(enabledFeatures, tables, MaximumTableIndex); err != nil {
298 return err
299 }
300
301 if err = m.validateDataCountSection(); err != nil {
302 return err
303 }
304 return nil
305}
306
307func (m *Module) validateStartSection() error {
308 // Check the start function is valid.
309 // TODO: this should be verified during decode so that errors have the correct source positions
310 if m.StartSection != nil {
311 startIndex := *m.StartSection
312 ft := m.typeOfFunction(startIndex)
313 if ft == nil { // TODO: move this check to decoder so that a module can never be decoded invalidly
314 return fmt.Errorf("invalid start function: func[%d] has an invalid type", startIndex)
315 }
316 if len(ft.Params) > 0 || len(ft.Results) > 0 {
317 return fmt.Errorf("invalid start function: func[%d] must have an empty (nullary) signature: %s", startIndex, ft)
318 }
319 }
320 return nil
321}
322
323func (m *Module) validateGlobals(globals []GlobalType, numFuncts, maxGlobals uint32) error {
324 if uint32(len(globals)) > maxGlobals {
325 return fmt.Errorf("too many globals in a module")
326 }
327
328 // Global initialization constant expression can only reference the imported globals.
329 // See the note on https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#constant-expressions%E2%91%A0
330 importedGlobals := globals[:m.ImportGlobalCount]
331 for i := range m.GlobalSection {
332 g := &m.GlobalSection[i]
333 if err := validateConstExpression(importedGlobals, numFuncts, &g.Init, g.Type.ValType); err != nil {
334 return err
335 }
336 }
337 return nil
338}
339
340func (m *Module) validateFunctions(enabledFeatures api.CoreFeatures, functions []Index, globals []GlobalType, memory *Memory, tables []Table, maximumFunctionIndex uint32) error {
341 if uint32(len(functions)) > maximumFunctionIndex {
342 return fmt.Errorf("too many functions (%d) in a module", len(functions))
343 }
344
345 functionCount := m.SectionElementCount(SectionIDFunction)
346 codeCount := m.SectionElementCount(SectionIDCode)
347 if functionCount == 0 && codeCount == 0 {
348 return nil
349 }
350
351 typeCount := m.SectionElementCount(SectionIDType)
352 if codeCount != functionCount {
353 return fmt.Errorf("code count (%d) != function count (%d)", codeCount, functionCount)
354 }
355
356 declaredFuncIndexes, err := m.declaredFunctionIndexes()
357 if err != nil {
358 return err
359 }
360
361 // Create bytes.Reader once as it causes allocation, and
362 // we frequently need it (e.g. on every If instruction).
363 br := bytes.NewReader(nil)
364 // Also, we reuse the stacks across multiple function validations to reduce allocations.
365 vs := &stacks{}
366 for idx, typeIndex := range m.FunctionSection {
367 if typeIndex >= typeCount {
368 return fmt.Errorf("invalid %s: type section index %d out of range", m.funcDesc(SectionIDFunction, Index(idx)), typeIndex)
369 }
370 c := &m.CodeSection[idx]
371 if c.GoFunc != nil {
372 continue
373 }
374 if err = m.validateFunction(vs, enabledFeatures, Index(idx), functions, globals, memory, tables, declaredFuncIndexes, br); err != nil {
375 return fmt.Errorf("invalid %s: %w", m.funcDesc(SectionIDFunction, Index(idx)), err)
376 }
377 }
378 return nil
379}
380
381// declaredFunctionIndexes returns a set of function indexes that can be used as an immediate for OpcodeRefFunc instruction.
382//
383// The criteria for which function indexes can be available for that instruction is vague in the spec:
384//
385// - "References: the list of function indices that occur in the module outside functions and can hence be used to form references inside them."
386// - https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/valid/conventions.html#contexts
387// - "Ref is the set funcidx(module with functions=ε, start=ε) , i.e., the set of function indices occurring in the module, except in its functions or start function."
388// - https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/valid/modules.html#valid-module
389//
390// To clarify, we reverse-engineer logic required to pass the WebAssembly Core specification 2.0 test suite:
391// https://github.com/WebAssembly/spec/blob/d39195773112a22b245ffbe864bab6d1182ccb06/test/core/ref_func.wast#L78-L115
392//
393// To summarize, the function indexes OpcodeRefFunc can refer include:
394// - existing in an element section regardless of its mode (active, passive, declarative).
395// - defined as globals whose value type is ValueRefFunc.
396// - used as an exported function.
397//
398// See https://github.com/WebAssembly/reference-types/issues/31
399// See https://github.com/WebAssembly/reference-types/issues/76
400func (m *Module) declaredFunctionIndexes() (ret map[Index]struct{}, err error) {
401 ret = map[uint32]struct{}{}
402
403 for i := range m.ExportSection {
404 exp := &m.ExportSection[i]
405 if exp.Type == ExternTypeFunc {
406 ret[exp.Index] = struct{}{}
407 }
408 }
409
410 for i := range m.GlobalSection {
411 g := &m.GlobalSection[i]
412 if g.Init.Opcode == OpcodeRefFunc {
413 var index uint32
414 index, _, err = leb128.LoadUint32(g.Init.Data)
415 if err != nil {
416 err = fmt.Errorf("%s[%d] failed to initialize: %w", SectionIDName(SectionIDGlobal), i, err)
417 return
418 }
419 ret[index] = struct{}{}
420 }
421 }
422
423 for i := range m.ElementSection {
424 elem := &m.ElementSection[i]
425 for _, index := range elem.Init {
426 if index != ElementInitNullReference {
427 ret[index] = struct{}{}
428 }
429 }
430 }
431 return
432}
433
434func (m *Module) funcDesc(sectionID SectionID, sectionIndex Index) string {
435 // Try to improve the error message by collecting any exports:
436 var exportNames []string
437 funcIdx := sectionIndex + m.ImportFunctionCount
438 for i := range m.ExportSection {
439 exp := &m.ExportSection[i]
440 if exp.Index == funcIdx && exp.Type == ExternTypeFunc {
441 exportNames = append(exportNames, fmt.Sprintf("%q", exp.Name))
442 }
443 }
444 sectionIDName := SectionIDName(sectionID)
445 if exportNames == nil {
446 return fmt.Sprintf("%s[%d]", sectionIDName, sectionIndex)
447 }
448 sort.Strings(exportNames) // go map keys do not iterate consistently
449 return fmt.Sprintf("%s[%d] export[%s]", sectionIDName, sectionIndex, strings.Join(exportNames, ","))
450}
451
452func (m *Module) validateMemory(memory *Memory, globals []GlobalType, _ api.CoreFeatures) error {
453 var activeElementCount int
454 for i := range m.DataSection {
455 d := &m.DataSection[i]
456 if !d.IsPassive() {
457 activeElementCount++
458 }
459 }
460 if activeElementCount > 0 && memory == nil {
461 return fmt.Errorf("unknown memory")
462 }
463
464 // Constant expression can only reference imported globals.
465 // https://github.com/WebAssembly/spec/blob/5900d839f38641989a9d8df2df4aee0513365d39/test/core/data.wast#L84-L91
466 importedGlobals := globals[:m.ImportGlobalCount]
467 for i := range m.DataSection {
468 d := &m.DataSection[i]
469 if !d.IsPassive() {
470 if err := validateConstExpression(importedGlobals, 0, &d.OffsetExpression, ValueTypeI32); err != nil {
471 return fmt.Errorf("calculate offset: %w", err)
472 }
473 }
474 }
475 return nil
476}
477
478func (m *Module) validateImports(enabledFeatures api.CoreFeatures) error {
479 for i := range m.ImportSection {
480 imp := &m.ImportSection[i]
481 if imp.Module == "" {
482 return fmt.Errorf("import[%d] has an empty module name", i)
483 }
484 switch imp.Type {
485 case ExternTypeFunc:
486 if int(imp.DescFunc) >= len(m.TypeSection) {
487 return fmt.Errorf("invalid import[%q.%q] function: type index out of range", imp.Module, imp.Name)
488 }
489 case ExternTypeGlobal:
490 if !imp.DescGlobal.Mutable {
491 continue
492 }
493 if err := enabledFeatures.RequireEnabled(api.CoreFeatureMutableGlobal); err != nil {
494 return fmt.Errorf("invalid import[%q.%q] global: %w", imp.Module, imp.Name, err)
495 }
496 }
497 }
498 return nil
499}
500
501func (m *Module) validateExports(enabledFeatures api.CoreFeatures, functions []Index, globals []GlobalType, memory *Memory, tables []Table) error {
502 for i := range m.ExportSection {
503 exp := &m.ExportSection[i]
504 index := exp.Index
505 switch exp.Type {
506 case ExternTypeFunc:
507 if index >= uint32(len(functions)) {
508 return fmt.Errorf("unknown function for export[%q]", exp.Name)
509 }
510 case ExternTypeGlobal:
511 if index >= uint32(len(globals)) {
512 return fmt.Errorf("unknown global for export[%q]", exp.Name)
513 }
514 if !globals[index].Mutable {
515 continue
516 }
517 if err := enabledFeatures.RequireEnabled(api.CoreFeatureMutableGlobal); err != nil {
518 return fmt.Errorf("invalid export[%q] global[%d]: %w", exp.Name, index, err)
519 }
520 case ExternTypeMemory:
521 if index > 0 || memory == nil {
522 return fmt.Errorf("memory for export[%q] out of range", exp.Name)
523 }
524 case ExternTypeTable:
525 if index >= uint32(len(tables)) {
526 return fmt.Errorf("table for export[%q] out of range", exp.Name)
527 }
528 }
529 }
530 return nil
531}
532
533func validateConstExpression(globals []GlobalType, numFuncs uint32, expr *ConstantExpression, expectedType ValueType) (err error) {
534 var actualType ValueType
535 switch expr.Opcode {
536 case OpcodeI32Const:
537 // Treat constants as signed as their interpretation is not yet known per /RATIONALE.md
538 _, _, err = leb128.LoadInt32(expr.Data)
539 if err != nil {
540 return fmt.Errorf("read i32: %w", err)
541 }
542 actualType = ValueTypeI32
543 case OpcodeI64Const:
544 // Treat constants as signed as their interpretation is not yet known per /RATIONALE.md
545 _, _, err = leb128.LoadInt64(expr.Data)
546 if err != nil {
547 return fmt.Errorf("read i64: %w", err)
548 }
549 actualType = ValueTypeI64
550 case OpcodeF32Const:
551 _, err = ieee754.DecodeFloat32(expr.Data)
552 if err != nil {
553 return fmt.Errorf("read f32: %w", err)
554 }
555 actualType = ValueTypeF32
556 case OpcodeF64Const:
557 _, err = ieee754.DecodeFloat64(expr.Data)
558 if err != nil {
559 return fmt.Errorf("read f64: %w", err)
560 }
561 actualType = ValueTypeF64
562 case OpcodeGlobalGet:
563 id, _, err := leb128.LoadUint32(expr.Data)
564 if err != nil {
565 return fmt.Errorf("read index of global: %w", err)
566 }
567 if uint32(len(globals)) <= id {
568 return fmt.Errorf("global index out of range")
569 }
570 actualType = globals[id].ValType
571 case OpcodeRefNull:
572 if len(expr.Data) == 0 {
573 return fmt.Errorf("read reference type for ref.null: %w", io.ErrShortBuffer)
574 }
575 reftype := expr.Data[0]
576 if reftype != RefTypeFuncref && reftype != RefTypeExternref {
577 return fmt.Errorf("invalid type for ref.null: 0x%x", reftype)
578 }
579 actualType = reftype
580 case OpcodeRefFunc:
581 index, _, err := leb128.LoadUint32(expr.Data)
582 if err != nil {
583 return fmt.Errorf("read i32: %w", err)
584 } else if index >= numFuncs {
585 return fmt.Errorf("ref.func index out of range [%d] with length %d", index, numFuncs-1)
586 }
587 actualType = ValueTypeFuncref
588 case OpcodeVecV128Const:
589 if len(expr.Data) != 16 {
590 return fmt.Errorf("%s needs 16 bytes but was %d bytes", OpcodeVecV128ConstName, len(expr.Data))
591 }
592 actualType = ValueTypeV128
593 default:
594 return fmt.Errorf("invalid opcode for const expression: 0x%x", expr.Opcode)
595 }
596
597 if actualType != expectedType {
598 return fmt.Errorf("const expression type mismatch expected %s but got %s",
599 ValueTypeName(expectedType), ValueTypeName(actualType))
600 }
601 return nil
602}
603
604func (m *Module) validateDataCountSection() (err error) {
605 if m.DataCountSection != nil && int(*m.DataCountSection) != len(m.DataSection) {
606 err = fmt.Errorf("data count section (%d) doesn't match the length of data section (%d)",
607 *m.DataCountSection, len(m.DataSection))
608 }
609 return
610}
611
612func (m *ModuleInstance) buildGlobals(module *Module, funcRefResolver func(funcIndex Index) Reference) {
613 importedGlobals := m.Globals[:module.ImportGlobalCount]
614
615 me := m.Engine
616 engineOwnGlobal := me.OwnsGlobals()
617 for i := Index(0); i < Index(len(module.GlobalSection)); i++ {
618 gs := &module.GlobalSection[i]
619 g := &GlobalInstance{}
620 if engineOwnGlobal {
621 g.Me = me
622 g.Index = i + module.ImportGlobalCount
623 }
624 m.Globals[i+module.ImportGlobalCount] = g
625 g.Type = gs.Type
626 g.initialize(importedGlobals, &gs.Init, funcRefResolver)
627 }
628}
629
630func paramNames(localNames IndirectNameMap, funcIdx uint32, paramLen int) []string {
631 for i := range localNames {
632 nm := &localNames[i]
633 // Only build parameter names if we have one for each.
634 if nm.Index != funcIdx || len(nm.NameMap) < paramLen {
635 continue
636 }
637
638 ret := make([]string, paramLen)
639 for j := range nm.NameMap {
640 p := &nm.NameMap[j]
641 if int(p.Index) < paramLen {
642 ret[p.Index] = p.Name
643 }
644 }
645 return ret
646 }
647 return nil
648}
649
650func (m *ModuleInstance) buildMemory(module *Module, allocator experimental.MemoryAllocator) {
651 memSec := module.MemorySection
652 if memSec != nil {
653 m.MemoryInstance = NewMemoryInstance(memSec, allocator, m.Engine)
654 m.MemoryInstance.definition = &module.MemoryDefinitionSection[0]
655 }
656}
657
658// Index is the offset in an index, not necessarily an absolute position in a Module section. This is because
659// indexs are often preceded by a corresponding type in the Module.ImportSection.
660//
661// For example, the function index starts with any ExternTypeFunc in the Module.ImportSection followed by
662// the Module.FunctionSection
663//
664// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-index
665type Index = uint32
666
667// FunctionType is a possibly empty function signature.
668//
669// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#function-types%E2%91%A0
670type FunctionType struct {
671 // Params are the possibly empty sequence of value types accepted by a function with this signature.
672 Params []ValueType
673
674 // Results are the possibly empty sequence of value types returned by a function with this signature.
675 //
676 // Note: In WebAssembly 1.0 (20191205), there can be at most one result.
677 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#result-types%E2%91%A0
678 Results []ValueType
679
680 // string is cached as it is used both for String and key
681 string string
682
683 // ParamNumInUint64 is the number of uint64 values requires to represent the Wasm param type.
684 ParamNumInUint64 int
685
686 // ResultsNumInUint64 is the number of uint64 values requires to represent the Wasm result type.
687 ResultNumInUint64 int
688}
689
690func (f *FunctionType) CacheNumInUint64() {
691 if f.ParamNumInUint64 == 0 {
692 for _, tp := range f.Params {
693 f.ParamNumInUint64++
694 if tp == ValueTypeV128 {
695 f.ParamNumInUint64++
696 }
697 }
698 }
699
700 if f.ResultNumInUint64 == 0 {
701 for _, tp := range f.Results {
702 f.ResultNumInUint64++
703 if tp == ValueTypeV128 {
704 f.ResultNumInUint64++
705 }
706 }
707 }
708}
709
710// EqualsSignature returns true if the function type has the same parameters and results.
711func (f *FunctionType) EqualsSignature(params []ValueType, results []ValueType) bool {
712 return bytes.Equal(f.Params, params) && bytes.Equal(f.Results, results)
713}
714
715// key gets or generates the key for Store.typeIDs. e.g. "i32_v" for one i32 parameter and no (void) result.
716func (f *FunctionType) key() string {
717 if f.string != "" {
718 return f.string
719 }
720 var ret string
721 for _, b := range f.Params {
722 ret += ValueTypeName(b)
723 }
724 if len(f.Params) == 0 {
725 ret += "v_"
726 } else {
727 ret += "_"
728 }
729 for _, b := range f.Results {
730 ret += ValueTypeName(b)
731 }
732 if len(f.Results) == 0 {
733 ret += "v"
734 }
735 f.string = ret
736 return ret
737}
738
739// String implements fmt.Stringer.
740func (f *FunctionType) String() string {
741 return f.key()
742}
743
744// Import is the binary representation of an import indicated by Type
745// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-import
746type Import struct {
747 Type ExternType
748 // Module is the possibly empty primary namespace of this import
749 Module string
750 // Module is the possibly empty secondary namespace of this import
751 Name string
752 // DescFunc is the index in Module.TypeSection when Type equals ExternTypeFunc
753 DescFunc Index
754 // DescTable is the inlined Table when Type equals ExternTypeTable
755 DescTable Table
756 // DescMem is the inlined Memory when Type equals ExternTypeMemory
757 DescMem *Memory
758 // DescGlobal is the inlined GlobalType when Type equals ExternTypeGlobal
759 DescGlobal GlobalType
760 // IndexPerType has the index of this import per ExternType.
761 IndexPerType Index
762}
763
764// Memory describes the limits of pages (64KB) in a memory.
765type Memory struct {
766 Min, Cap, Max uint32
767 // IsMaxEncoded true if the Max is encoded in the original binary.
768 IsMaxEncoded bool
769 // IsShared true if the memory is shared for access from multiple agents.
770 IsShared bool
771}
772
773// Validate ensures values assigned to Min, Cap and Max are within valid thresholds.
774func (m *Memory) Validate(memoryLimitPages uint32) error {
775 min, capacity, max := m.Min, m.Cap, m.Max
776
777 if max > memoryLimitPages {
778 return fmt.Errorf("max %d pages (%s) over limit of %d pages (%s)",
779 max, PagesToUnitOfBytes(max), memoryLimitPages, PagesToUnitOfBytes(memoryLimitPages))
780 } else if min > memoryLimitPages {
781 return fmt.Errorf("min %d pages (%s) over limit of %d pages (%s)",
782 min, PagesToUnitOfBytes(min), memoryLimitPages, PagesToUnitOfBytes(memoryLimitPages))
783 } else if min > max {
784 return fmt.Errorf("min %d pages (%s) > max %d pages (%s)",
785 min, PagesToUnitOfBytes(min), max, PagesToUnitOfBytes(max))
786 } else if capacity < min {
787 return fmt.Errorf("capacity %d pages (%s) less than minimum %d pages (%s)",
788 capacity, PagesToUnitOfBytes(capacity), min, PagesToUnitOfBytes(min))
789 } else if capacity > memoryLimitPages {
790 return fmt.Errorf("capacity %d pages (%s) over limit of %d pages (%s)",
791 capacity, PagesToUnitOfBytes(capacity), memoryLimitPages, PagesToUnitOfBytes(memoryLimitPages))
792 }
793 return nil
794}
795
796type GlobalType struct {
797 ValType ValueType
798 Mutable bool
799}
800
801type Global struct {
802 Type GlobalType
803 Init ConstantExpression
804}
805
806type ConstantExpression struct {
807 Opcode Opcode
808 Data []byte
809}
810
811// Export is the binary representation of an export indicated by Type
812// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-export
813type Export struct {
814 Type ExternType
815
816 // Name is what the host refers to this definition as.
817 Name string
818
819 // Index is the index of the definition to export, the index is by Type
820 // e.g. If ExternTypeFunc, this is a position in the function index.
821 Index Index
822}
823
824// Code is an entry in the Module.CodeSection containing the locals and body of the function.
825// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-code
826type Code struct {
827 // LocalTypes are any function-scoped variables in insertion order.
828 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-local
829 LocalTypes []ValueType
830
831 // Body is a sequence of expressions ending in OpcodeEnd
832 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-expr
833 Body []byte
834
835 // GoFunc is non-nil when IsHostFunction and defined in go, either
836 // api.GoFunction or api.GoModuleFunction. When present, LocalTypes and Body must
837 // be nil.
838 //
839 // Note: This has no serialization format, so is not encodable.
840 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#host-functions%E2%91%A2
841 GoFunc interface{}
842
843 // BodyOffsetInCodeSection is the offset of the beginning of the body in the code section.
844 // This is used for DWARF based stack trace where a program counter represents an offset in code section.
845 BodyOffsetInCodeSection uint64
846}
847
848type DataSegment struct {
849 OffsetExpression ConstantExpression
850 Init []byte
851 Passive bool
852}
853
854// IsPassive returns true if this data segment is "passive" in the sense that memory offset and
855// index is determined at runtime and used by OpcodeMemoryInitName instruction in the bulk memory
856// operations proposal.
857//
858// See https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/appendix/changes.html#bulk-memory-and-table-instructions
859func (d *DataSegment) IsPassive() bool {
860 return d.Passive
861}
862
863// NameSection represent the known custom name subsections defined in the WebAssembly Binary Format
864//
865// Note: This can be nil if no names were decoded for any reason including configuration.
866// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#name-section%E2%91%A0
867type NameSection struct {
868 // ModuleName is the symbolic identifier for a module. e.g. math
869 //
870 // Note: This can be empty for any reason including configuration.
871 ModuleName string
872
873 // FunctionNames is an association of a function index to its symbolic identifier. e.g. add
874 //
875 // * the key (idx) is in the function index, where module defined functions are preceded by imported ones.
876 // See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#functions%E2%91%A7
877 //
878 // For example, assuming the below text format is the second import, you would expect FunctionNames[1] = "mul"
879 // (import "Math" "Mul" (func $mul (param $x f32) (param $y f32) (result f32)))
880 //
881 // Note: FunctionNames are only used for debugging. At runtime, functions are called based on raw numeric index.
882 // Note: This can be nil for any reason including configuration.
883 FunctionNames NameMap
884
885 // LocalNames contains symbolic names for function parameters or locals that have one.
886 //
887 // Note: In the Text Format, function local names can inherit parameter
888 // names from their type. Here are some examples:
889 // * (module (import (func (param $x i32) (param i32))) (func (type 0))) = [{0, {x,0}}]
890 // * (module (import (func (param i32) (param $y i32))) (func (type 0) (local $z i32))) = [0, [{y,1},{z,2}]]
891 // * (module (func (param $x i32) (local $y i32) (local $z i32))) = [{x,0},{y,1},{z,2}]
892 //
893 // Note: LocalNames are only used for debugging. At runtime, locals are called based on raw numeric index.
894 // Note: This can be nil for any reason including configuration.
895 LocalNames IndirectNameMap
896
897 // ResultNames is a wazero-specific mechanism to store result names.
898 ResultNames IndirectNameMap
899}
900
901// CustomSection contains the name and raw data of a custom section.
902type CustomSection struct {
903 Name string
904 Data []byte
905}
906
907// NameMap associates an index with any associated names.
908//
909// Note: Often the index bridges multiple sections. For example, the function index starts with any
910// ExternTypeFunc in the Module.ImportSection followed by the Module.FunctionSection
911//
912// Note: NameMap is unique by NameAssoc.Index, but NameAssoc.Name needn't be unique.
913// Note: When encoding in the Binary format, this must be ordered by NameAssoc.Index
914// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-namemap
915type NameMap []NameAssoc
916
917type NameAssoc struct {
918 Index Index
919 Name string
920}
921
922// IndirectNameMap associates an index with an association of names.
923//
924// Note: IndirectNameMap is unique by NameMapAssoc.Index, but NameMapAssoc.NameMap needn't be unique.
925// Note: When encoding in the Binary format, this must be ordered by NameMapAssoc.Index
926// https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#binary-indirectnamemap
927type IndirectNameMap []NameMapAssoc
928
929type NameMapAssoc struct {
930 Index Index
931 NameMap NameMap
932}
933
934// AllDeclarations returns all declarations for functions, globals, memories and tables in a module including imported ones.
935func (m *Module) AllDeclarations() (functions []Index, globals []GlobalType, memory *Memory, tables []Table, err error) {
936 for i := range m.ImportSection {
937 imp := &m.ImportSection[i]
938 switch imp.Type {
939 case ExternTypeFunc:
940 functions = append(functions, imp.DescFunc)
941 case ExternTypeGlobal:
942 globals = append(globals, imp.DescGlobal)
943 case ExternTypeMemory:
944 memory = imp.DescMem
945 case ExternTypeTable:
946 tables = append(tables, imp.DescTable)
947 }
948 }
949
950 functions = append(functions, m.FunctionSection...)
951 for i := range m.GlobalSection {
952 g := &m.GlobalSection[i]
953 globals = append(globals, g.Type)
954 }
955 if m.MemorySection != nil {
956 if memory != nil { // shouldn't be possible due to Validate
957 err = errors.New("at most one table allowed in module")
958 return
959 }
960 memory = m.MemorySection
961 }
962 if m.TableSection != nil {
963 tables = append(tables, m.TableSection...)
964 }
965 return
966}
967
968// SectionID identifies the sections of a Module in the WebAssembly 1.0 (20191205) Binary Format.
969//
970// Note: these are defined in the wasm package, instead of the binary package, as a key per section is needed regardless
971// of format, and deferring to the binary type avoids confusion.
972//
973// See https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#sections%E2%91%A0
974type SectionID = byte
975
976const (
977 // SectionIDCustom includes the standard defined NameSection and possibly others not defined in the standard.
978 SectionIDCustom SectionID = iota // don't add anything not in https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#sections%E2%91%A0
979 SectionIDType
980 SectionIDImport
981 SectionIDFunction
982 SectionIDTable
983 SectionIDMemory
984 SectionIDGlobal
985 SectionIDExport
986 SectionIDStart
987 SectionIDElement
988 SectionIDCode
989 SectionIDData
990
991 // SectionIDDataCount may exist in WebAssembly 2.0 or WebAssembly 1.0 with CoreFeatureBulkMemoryOperations enabled.
992 //
993 // See https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/binary/modules.html#data-count-section
994 // See https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/appendix/changes.html#bulk-memory-and-table-instructions
995 SectionIDDataCount
996)
997
998// SectionIDName returns the canonical name of a module section.
999// https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/#sections%E2%91%A0
1000func SectionIDName(sectionID SectionID) string {
1001 switch sectionID {
1002 case SectionIDCustom:
1003 return "custom"
1004 case SectionIDType:
1005 return "type"
1006 case SectionIDImport:
1007 return "import"
1008 case SectionIDFunction:
1009 return "function"
1010 case SectionIDTable:
1011 return "table"
1012 case SectionIDMemory:
1013 return "memory"
1014 case SectionIDGlobal:
1015 return "global"
1016 case SectionIDExport:
1017 return "export"
1018 case SectionIDStart:
1019 return "start"
1020 case SectionIDElement:
1021 return "element"
1022 case SectionIDCode:
1023 return "code"
1024 case SectionIDData:
1025 return "data"
1026 case SectionIDDataCount:
1027 return "data_count"
1028 }
1029 return "unknown"
1030}
1031
1032// ValueType is an alias of api.ValueType defined to simplify imports.
1033type ValueType = api.ValueType
1034
1035const (
1036 ValueTypeI32 = api.ValueTypeI32
1037 ValueTypeI64 = api.ValueTypeI64
1038 ValueTypeF32 = api.ValueTypeF32
1039 ValueTypeF64 = api.ValueTypeF64
1040 // TODO: ValueTypeV128 is not exposed in the api pkg yet.
1041 ValueTypeV128 ValueType = 0x7b
1042 // TODO: ValueTypeFuncref is not exposed in the api pkg yet.
1043 ValueTypeFuncref ValueType = 0x70
1044 ValueTypeExternref = api.ValueTypeExternref
1045)
1046
1047// ValueTypeName is an alias of api.ValueTypeName defined to simplify imports.
1048func ValueTypeName(t ValueType) string {
1049 if t == ValueTypeFuncref {
1050 return "funcref"
1051 } else if t == ValueTypeV128 {
1052 return "v128"
1053 }
1054 return api.ValueTypeName(t)
1055}
1056
1057func isReferenceValueType(vt ValueType) bool {
1058 return vt == ValueTypeExternref || vt == ValueTypeFuncref
1059}
1060
1061// ExternType is an alias of api.ExternType defined to simplify imports.
1062type ExternType = api.ExternType
1063
1064const (
1065 ExternTypeFunc = api.ExternTypeFunc
1066 ExternTypeFuncName = api.ExternTypeFuncName
1067 ExternTypeTable = api.ExternTypeTable
1068 ExternTypeTableName = api.ExternTypeTableName
1069 ExternTypeMemory = api.ExternTypeMemory
1070 ExternTypeMemoryName = api.ExternTypeMemoryName
1071 ExternTypeGlobal = api.ExternTypeGlobal
1072 ExternTypeGlobalName = api.ExternTypeGlobalName
1073)
1074
1075// ExternTypeName is an alias of api.ExternTypeName defined to simplify imports.
1076func ExternTypeName(t ValueType) string {
1077 return api.ExternTypeName(t)
1078}