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