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