diff --git a/compiler/module.go b/compiler/module.go index e85e8d7..ec65de6 100644 --- a/compiler/module.go +++ b/compiler/module.go @@ -3,14 +3,14 @@ package compiler import ( "bytes" "encoding/binary" - //"fmt" + "strings" + "github.com/go-interpreter/wagon/disasm" "github.com/go-interpreter/wagon/wasm" - //"github.com/go-interpreter/wagon/validate" "github.com/go-interpreter/wagon/wasm/leb128" + "github.com/perlin-network/life/compiler/opcodes" "github.com/perlin-network/life/utils" - "strings" ) type Module struct { @@ -144,11 +144,15 @@ func (m *Module) CompileWithNGen(gp GasPolicy, numGlobals uint64) (out string, r for i, f := range m.Base.FunctionIndexSpace { //fmt.Printf("Compiling function %d (%+v) with %d locals\n", i, f.Sig, len(f.Body.Locals)) - d, err := disasm.Disassemble(f, m.Base) + instrs, err := disasm.Disassemble(f.Body.Code) if err != nil { panic(err) } - compiler := NewSSAFunctionCompiler(m.Base, d) + d := disasm.Disassembly{ + Code: instrs, + MaxDepth: 512, + } + compiler := NewSSAFunctionCompiler(m.Base, &d) compiler.CallIndexOffset = numFuncImports compiler.Compile(importTypeIDs) if m.DisableFloatingPoint { @@ -220,11 +224,15 @@ func (m *Module) CompileForInterpreter(gp GasPolicy) (_retCode []InterpreterCode for i, f := range m.Base.FunctionIndexSpace { //fmt.Printf("Compiling function %d (%+v) with %d locals\n", i, f.Sig, len(f.Body.Locals)) - d, err := disasm.Disassemble(f, m.Base) + instrs, err := disasm.Disassemble(f.Body.Code) if err != nil { panic(err) } - compiler := NewSSAFunctionCompiler(m.Base, d) + d := disasm.Disassembly{ + Code: instrs, + MaxDepth: 512, + } + compiler := NewSSAFunctionCompiler(m.Base, &d) compiler.CallIndexOffset = numFuncImports compiler.Compile(importTypeIDs) if m.DisableFloatingPoint { diff --git a/compiler/ngen.go b/compiler/ngen.go index f1603f4..9a8cba7 100644 --- a/compiler/ngen.go +++ b/compiler/ngen.go @@ -647,13 +647,13 @@ func (c *SSAFunctionCompiler) NGen(selfID uint64, numParams uint64, numLocals ui case "i64.store", "f64.store": writeMemStore(body, ins, "uint64_t") - case "current_memory": + case "memory.size": bSprintf(body, "%s%d.vu64 = vm->mem_size / 65536;", NGEN_VALUE_PREFIX, ins.Target, ) - case "grow_memory": + case "memory.grow": bSprintf(body, "%s%d.vu64 = vm->mem_size / 65536; vm->grow_memory(vm, %s%d.vu32 * 65536);", NGEN_VALUE_PREFIX, ins.Target, diff --git a/compiler/serialize.go b/compiler/serialize.go index 3994daa..4408ae7 100644 --- a/compiler/serialize.go +++ b/compiler/serialize.go @@ -722,10 +722,10 @@ func (c *SSAFunctionCompiler) Serialize() []byte { binary.Write(buf, binary.LittleEndian, uint32(v)) } - case "current_memory": + case "memory.size": binary.Write(buf, binary.LittleEndian, opcodes.CurrentMemory) - case "grow_memory": + case "memory.grow": binary.Write(buf, binary.LittleEndian, opcodes.GrowMemory) binary.Write(buf, binary.LittleEndian, uint32(ins.Values[0])) diff --git a/compiler/ssa.go b/compiler/ssa.go index 948df20..215bbc2 100644 --- a/compiler/ssa.go +++ b/compiler/ssa.go @@ -2,12 +2,11 @@ package compiler import ( "fmt" - "math" + "strings" "github.com/go-interpreter/wagon/disasm" "github.com/go-interpreter/wagon/wasm" - "strings" ) type TyValueID uint64 @@ -267,14 +266,14 @@ func (c *SSAFunctionCompiler) Compile(importTypeIDs []int) { c.Locations = append(c.Locations, &Location{ CodePos: len(c.Code), StackDepth: len(c.Stack), - PreserveTop: ins.Block.Signature != wasm.BlockTypeEmpty, + PreserveTop: ins.Block != nil && ins.Block.Signature != wasm.BlockTypeEmpty, }) case "loop": c.Locations = append(c.Locations, &Location{ CodePos: len(c.Code), StackDepth: len(c.Stack), - LoopPreserveTop: ins.Block.Signature != wasm.BlockTypeEmpty, + LoopPreserveTop: ins.Block != nil && ins.Block.Signature != wasm.BlockTypeEmpty, BrHead: true, }) @@ -284,7 +283,7 @@ func (c *SSAFunctionCompiler) Compile(importTypeIDs []int) { c.Locations = append(c.Locations, &Location{ CodePos: len(c.Code), StackDepth: len(c.Stack), - PreserveTop: ins.Block.Signature != wasm.BlockTypeEmpty, + PreserveTop: ins.Block != nil && ins.Block.Signature != wasm.BlockTypeEmpty, IfBlock: true, }) @@ -442,12 +441,12 @@ func (c *SSAFunctionCompiler) Compile(importTypeIDs []int) { c.PushStack(targetValueID) } - case "current_memory": + case "memory.size": retID := c.NextValueID() c.Code = append(c.Code, buildInstr(retID, ins.Op.Name, nil, nil)) c.PushStack(retID) - case "grow_memory": + case "memory.grow": retID := c.NextValueID() c.Code = append(c.Code, buildInstr(retID, ins.Op.Name, nil, c.PopStack(1))) c.PushStack(retID) diff --git a/exec/vm.go b/exec/vm.go index 1ac06e4..7928347 100644 --- a/exec/vm.go +++ b/exec/vm.go @@ -6,14 +6,13 @@ import ( "math" "math/bits" "runtime/debug" + "strings" + + "github.com/go-interpreter/wagon/wasm" "github.com/perlin-network/life/compiler" "github.com/perlin-network/life/compiler/opcodes" "github.com/perlin-network/life/utils" - - "strings" - - "github.com/go-interpreter/wagon/wasm" ) type FunctionImport func(vm *VirtualMachine) int64 @@ -44,6 +43,7 @@ type NCompileConfig struct { } type AOTService interface { + Initialize(vm *VirtualMachine) UnsafeInvokeFunction_0(vm *VirtualMachine, name string) uint64 UnsafeInvokeFunction_1(vm *VirtualMachine, name string, p0 uint64) uint64 UnsafeInvokeFunction_2(vm *VirtualMachine, name string, p0, p1 uint64) uint64 @@ -107,6 +107,313 @@ type ImportResolver interface { ResolveGlobal(module, field string) int64 } +type Module struct { + Config VMConfig + Module *compiler.Module + FunctionCode []compiler.InterpreterCode + FunctionImports []FunctionImportInfo + Table []uint32 + Globals []int64 + GasPolicy compiler.GasPolicy + ImportResolver ImportResolver +} + +var ( + emptyGlobals = []int64{} + emptyFuncImports = []FunctionImportInfo{} + emptyMemory = []byte{} + emptyTable = []uint32{} +) + +// NewModule instantiates a module for a given WebAssembly module, with +// specific execution options specified under a VMConfig, and a WebAssembly module import +// resolver. +func NewModule( + code []byte, + config VMConfig, + impResolver ImportResolver, + gasPolicy compiler.GasPolicy, +) (_retVM *Module, retErr error) { + if config.EnableJIT { + fmt.Println("Warning: JIT support is removed.") + } + + m, err := compiler.LoadModule(code) + if err != nil { + return nil, err + } + + m.DisableFloatingPoint = config.DisableFloatingPoint + + functionCode, err := m.CompileForInterpreter(gasPolicy) + if err != nil { + return nil, err + } + + defer utils.CatchPanic(&retErr) + + table := emptyTable + globals := emptyGlobals + funcImports := emptyFuncImports + + if m.Base.Import != nil && impResolver != nil { + for _, imp := range m.Base.Import.Entries { + switch imp.Type.Kind() { + case wasm.ExternalFunction: + funcImports = append(funcImports, FunctionImportInfo{ + ModuleName: imp.ModuleName, + FieldName: imp.FieldName, + F: nil, // deferred + }) + case wasm.ExternalGlobal: + globals = append(globals, impResolver.ResolveGlobal(imp.ModuleName, imp.FieldName)) + case wasm.ExternalMemory: + // TODO: Do we want a real import? + if m.Base.Memory != nil && len(m.Base.Memory.Entries) > 0 { + panic("cannot import another memory while we already have one") + } + m.Base.Memory = &wasm.SectionMemories{ + Entries: []wasm.Memory{ + wasm.Memory{ + Limits: wasm.ResizableLimits{ + Initial: uint32(config.DefaultMemoryPages), + }, + }, + }, + } + case wasm.ExternalTable: + // TODO: Do we want a real import? + if m.Base.Table != nil && len(m.Base.Table.Entries) > 0 { + panic("cannot import another table while we already have one") + } + m.Base.Table = &wasm.SectionTables{ + Entries: []wasm.Table{ + wasm.Table{ + Limits: wasm.ResizableLimits{ + Initial: uint32(config.DefaultTableSize), + }, + }, + }, + } + default: + panic(fmt.Errorf("import kind not supported: %d", imp.Type.Kind())) + } + } + } + + // Load global entries. + for _, entry := range m.Base.GlobalIndexSpace { + globals = append(globals, execInitExpr(entry.Init, globals)) + } + + // Populate table elements. + if m.Base.Table != nil && len(m.Base.Table.Entries) > 0 { + t := &m.Base.Table.Entries[0] + + if config.MaxTableSize != 0 && int(t.Limits.Initial) > config.MaxTableSize { + panic("max table size exceeded") + } + + table = make([]uint32, int(t.Limits.Initial)) + for i := 0; i < int(t.Limits.Initial); i++ { + table[i] = 0xffffffff + } + if m.Base.Elements != nil && len(m.Base.Elements.Entries) > 0 { + for _, e := range m.Base.Elements.Entries { + offset := int(execInitExpr(e.Offset, globals)) + copy(table[offset:], e.Elems) + } + } + } + + return &Module{ + Module: m, + Config: config, + FunctionCode: functionCode, + FunctionImports: funcImports, + Table: table, + Globals: globals, + GasPolicy: gasPolicy, + ImportResolver: impResolver, + }, nil +} + +func (m *Module) getExport(key string, kind wasm.External) (int, bool) { + if m.Module.Base.Export == nil { + return -1, false + } + + entry, ok := m.Module.Base.Export.Entries[key] + if !ok { + return -1, false + } + + if entry.Kind != kind { + return -1, false + } + + return int(entry.Index), true +} + +// GetGlobalExport returns the global export with the given name. +func (m *Module) GetGlobalExport(key string) (int, bool) { + return m.getExport(key, wasm.ExternalGlobal) +} + +// GetFunctionExport returns the function export with the given name. +func (m *Module) GetFunctionExport(key string) (int, bool) { + return m.getExport(key, wasm.ExternalFunction) +} + +func (m *Module) GenerateNEnv(config NCompileConfig) string { + builder := &strings.Builder{} + + bSprintf(builder, "#include \n\n") + + if config.DisableMemBoundCheck { + builder.WriteString("#define POLYMERASE_NO_MEM_BOUND_CHECK\n") + } + + builder.WriteString(compiler.NGEN_HEADER) + if !m.Config.DisableFloatingPoint { + builder.WriteString(compiler.NGEN_FP_HEADER) + } + + bSprintf(builder, "static uint64_t globals[] = {") + for _, v := range m.Globals { + bSprintf(builder, "%dull,", uint64(v)) + } + bSprintf(builder, "};\n") + + for i, code := range m.FunctionCode { + bSprintf(builder, "uint64_t %s%d(struct VirtualMachine *", compiler.NGEN_FUNCTION_PREFIX, i) + for j := 0; j < code.NumParams; j++ { + bSprintf(builder, ",uint64_t") + } + bSprintf(builder, ");\n") + } + + // call_indirect dispatcher. + bSprintf(builder, "struct TableEntry { uint64_t num_params; void *func; };\n") + bSprintf(builder, "static const uint64_t num_table_entries = %d;\n", len(m.Table)) + bSprintf(builder, "static struct TableEntry table[] = {\n") + for _, entry := range m.Table { + if entry == math.MaxUint32 { + bSprintf(builder, "{ .num_params = 0, .func = 0 },\n") + } else { + functionID := int(entry) + code := m.FunctionCode[functionID] + + bSprintf(builder, "{ .num_params = %d, .func = %s%d },\n", code.NumParams, compiler.NGEN_FUNCTION_PREFIX, functionID) + } + } + bSprintf(builder, "};\n") + bSprintf(builder, "static void * __attribute__((always_inline)) %sresolve_indirect(struct VirtualMachine *vm, uint64_t entry_id, uint64_t num_params) {\n", compiler.NGEN_ENV_API_PREFIX) + bSprintf(builder, "if(entry_id >= num_table_entries) { vm->throw_s(vm, \"%s\"); }\n", "table entry out of bounds") + bSprintf(builder, "if(table[entry_id].func == 0) { vm->throw_s(vm, \"%s\"); }\n", "table entry is null") + bSprintf(builder, "if(table[entry_id].num_params != num_params) { vm->throw_s(vm, \"%s\"); }\n", "argument count mismatch") + bSprintf(builder, "return table[entry_id].func;\n") + bSprintf(builder, "}\n") + + bSprintf(builder, "struct ImportEntry { const char *module_name; const char *field_name; ExternalFunction f; };\n") + bSprintf(builder, "static const uint64_t num_import_entries = %d;\n", len(m.FunctionImports)) + bSprintf(builder, "static struct ImportEntry imports[] = {\n") + for _, imp := range m.FunctionImports { + bSprintf(builder, "{ .module_name = \"%s\", .field_name = \"%s\", .f = 0 },\n", escapeName(imp.ModuleName), escapeName(imp.FieldName)) + } + bSprintf(builder, "};\n") + bSprintf(builder, + "static uint64_t __attribute__((always_inline)) %sinvoke_import(struct VirtualMachine *vm, uint64_t import_id, uint64_t num_params, uint64_t *params) {\n", + compiler.NGEN_ENV_API_PREFIX, + ) + + bSprintf(builder, "if(import_id >= num_import_entries) { vm->throw_s(vm, \"%s\"); }\n", "import entry out of bounds") + bSprintf(builder, "if(imports[import_id].f == 0) { imports[import_id].f = vm->resolve_import(vm, imports[import_id].module_name, imports[import_id].field_name); }\n") + bSprintf(builder, "if(imports[import_id].f == 0) { vm->throw_s(vm, \"%s\"); }\n", "cannot resolve import") + bSprintf(builder, "return imports[import_id].f(vm, import_id, num_params, params);\n") + bSprintf(builder, "}\n") + + return builder.String() +} + +func (m *Module) NBuildAliasDef() string { + builder := &strings.Builder{} + + builder.WriteString("// Aliases for exported functions\n") + + if m.Module.Base.Export != nil { + for name, exp := range m.Module.Base.Export.Entries { + if exp.Kind == wasm.ExternalFunction { + bSprintf(builder, "#define %sexport_%s %s%d\n", compiler.NGEN_FUNCTION_PREFIX, filterName(name), compiler.NGEN_FUNCTION_PREFIX, exp.Index) + } + } + } + + return builder.String() +} + +func (m *Module) NCompile(config NCompileConfig) string { + body, err := m.Module.CompileWithNGen(m.GasPolicy, uint64(len(m.Globals))) + if err != nil { + panic(err) + } + + out := m.GenerateNEnv(config) + "\n" + body + if config.AliasDef { + out += "\n" + out += m.NBuildAliasDef() + } + + return out +} + +// NewVirtualMachine instantiates a virtual machine for the module. +func (m *Module) NewVirtualMachine() *VirtualMachine { + globals := make([]int64, len(m.Globals)) + copy(globals, m.Globals) + table := make([]uint32, len(m.Table)) + copy(table, m.Table) + + // Load linear memory. + memory := emptyMemory + if m.Module.Base.Memory != nil && len(m.Module.Base.Memory.Entries) > 0 { + initialLimit := int(m.Module.Base.Memory.Entries[0].Limits.Initial) + if m.Config.MaxMemoryPages != 0 && initialLimit > m.Config.MaxMemoryPages { + panic("max memory exceeded") + } + + capacity := initialLimit * DefaultPageSize + + // Initialize empty memory. + memory = make([]byte, capacity) + for i := 0; i < capacity; i++ { + memory[i] = 0 + } + + if m.Module.Base.Data != nil && len(m.Module.Base.Data.Entries) > 0 { + for _, e := range m.Module.Base.Data.Entries { + offset := int(execInitExpr(e.Offset, globals)) + copy(memory[int(offset):], e.Data) + } + } + } + + return &VirtualMachine{ + Module: m.Module, + Config: m.Config, + FunctionCode: m.FunctionCode, + FunctionImports: m.FunctionImports, + CallStack: make([]Frame, DefaultCallStackSize), + CurrentFrame: -1, + Table: table, + Globals: globals, + Memory: memory, + Exited: true, + GasPolicy: m.GasPolicy, + ImportResolver: m.ImportResolver, + } +} + // NewVirtualMachine instantiates a virtual machine for a given WebAssembly module, with // specific execution options specified under a VMConfig, and a WebAssembly module import // resolver. @@ -134,9 +441,9 @@ func NewVirtualMachine( defer utils.CatchPanic(&retErr) - table := make([]uint32, 0) - globals := make([]int64, 0) - funcImports := make([]FunctionImportInfo, 0) + table := emptyTable + globals := emptyGlobals + funcImports := emptyFuncImports if m.Base.Import != nil && impResolver != nil { for _, imp := range m.Base.Import.Entries { @@ -209,7 +516,7 @@ func NewVirtualMachine( } // Load linear memory. - memory := make([]byte, 0) + memory := emptyMemory if m.Base.Memory != nil && len(m.Base.Memory.Entries) > 0 { initialLimit := int(m.Base.Memory.Entries[0].Limits.Initial) if config.MaxMemoryPages != 0 && initialLimit > config.MaxMemoryPages { @@ -249,6 +556,7 @@ func NewVirtualMachine( } func (vm *VirtualMachine) SetAOTService(s AOTService) { + s.Initialize(vm) vm.AOTService = s } diff --git a/go.mod b/go.mod index f83c738..b6af5cd 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,8 @@ module github.com/perlin-network/life -replace github.com/go-interpreter/wagon v0.0.0 => github.com/perlin-network/wagon v0.3.1-0.20180825141017-f8cb99b55a39 - require ( - github.com/go-interpreter/wagon v0.0.0 + github.com/go-interpreter/wagon v0.6.0 github.com/vmihailenco/msgpack v4.0.4+incompatible golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e // indirect + google.golang.org/appengine v1.6.0 // indirect ) diff --git a/main.go b/main.go index 6ab8e35..1b15627 100644 --- a/main.go +++ b/main.go @@ -5,7 +5,7 @@ import ( "fmt" "github.com/perlin-network/life/exec" "github.com/perlin-network/life/platform" - "github.com/perlin-network/life/wasm-validation" + wasm_validation "github.com/perlin-network/life/wasm-validation" "io/ioutil" "strconv" "time" @@ -76,15 +76,7 @@ func main() { panic(err) } - validator, err := wasm_validation.NewValidator() - if err != nil { - panic(err) - } - - err = validator.ValidateWasm(input) - if err != nil { - panic(err) - } + err = wasm_validation.ValidateWasm(input) // Instantiate a new WebAssembly VM with a few resolved imports. vm, err := exec.NewVirtualMachine(input, exec.VMConfig{ diff --git a/platform/aot_android_arm.go b/platform/aot_android_arm.go index adc0f0b..2858d7e 100644 --- a/platform/aot_android_arm.go +++ b/platform/aot_android_arm.go @@ -7,6 +7,9 @@ import ( type AOTContext struct { } +func (c *AOTContext) Initialize(vm *exec.VirtualMachine) { +} + func (c *AOTContext) UnsafeInvokeFunction_0(vm *exec.VirtualMachine, name string) uint64 { return 0 } diff --git a/platform/aot_unix.go b/platform/aot_unix.go index f22f67c..57fd662 100644 --- a/platform/aot_unix.go +++ b/platform/aot_unix.go @@ -81,7 +81,6 @@ static uint64_t unsafe_invoke_function_2(struct VirtualMachine *vm, void *sym, u import "C" import ( - "github.com/perlin-network/life/exec" "io/ioutil" "log" os_exec "os/exec" @@ -89,6 +88,8 @@ import ( "reflect" "runtime" "unsafe" + + "github.com/perlin-network/life/exec" ) //export go_vm_throw_s @@ -159,6 +160,18 @@ func (c *AOTContext) resolveNameForInvocation(name string) unsafe.Pointer { return sym } +func (c *AOTContext) Initialize(vm *exec.VirtualMachine) { + nativeVM := C.vm_alloc() + C.vm_build(nativeVM, C.uintptr_t(uintptr(unsafe.Pointer(vm))), C.uint64_t(len(vm.Memory))) + if len(vm.Memory) > 0 { + C.memcpy(unsafe.Pointer(nativeVM.mem), unsafe.Pointer(&vm.Memory[0]), C.ulong(len(vm.Memory))) + } + + updateMemory(nativeVM) + + c.vmHandle = nativeVM +} + func (c *AOTContext) UnsafeInvokeFunction_0(vm *exec.VirtualMachine, name string) uint64 { return uint64(C.unsafe_invoke_function_0( c.vmHandle, @@ -223,23 +236,71 @@ func FullAOTCompile(vm *exec.VirtualMachine) *AOTContext { return nil } - nativeVM := C.vm_alloc() - C.vm_build(nativeVM, C.uintptr_t(uintptr(unsafe.Pointer(vm))), C.uint64_t(len(vm.Memory))) - if len(vm.Memory) > 0 { - C.memcpy(unsafe.Pointer(nativeVM.mem), unsafe.Pointer(&vm.Memory[0]), C.ulong(len(vm.Memory))) + ctx := &AOTContext{ + dlHandle: handle, } - updateMemory(nativeVM) + runtime.SetFinalizer(ctx, func(ctx *AOTContext) { + C.dlclose(ctx.dlHandle) + if ctx.vmHandle != nil { + C.vm_destroy(ctx.vmHandle) + C.free(unsafe.Pointer(ctx.vmHandle)) + } + }) + + return ctx +} + +func FullAOTCompileModule(m *exec.Module) *AOTContext { + code := m.NCompile(exec.NCompileConfig{ + AliasDef: false, + DisableMemBoundCheck: C.need_mem_bound_check() == 0, + }) + tempDir, err := ioutil.TempDir("", "life-aot-") + if err != nil { + log.Println(err) + return nil + } + + inPath := path.Join(tempDir, "in.c") + outPath := path.Join(tempDir, "out") + + err = ioutil.WriteFile(inPath, []byte(code), 0644) + if err != nil { + log.Println(err) + return nil + } + + cmd := os_exec.Command("clang", "-fPIC", "-O2", "-lm", "-o", outPath, "-shared", inPath) + out, err := cmd.CombinedOutput() + + if len(out) > 0 { + log.Printf("compiler warnings/errors: \n%s\n", string(out)) + } + + if err != nil { + log.Println(err) + return nil + } + + outPathC := C.CString(outPath) + handle := C.dlopen(outPathC, C.RTLD_NOW|C.RTLD_LOCAL) + C.free(unsafe.Pointer(outPathC)) + if handle == nil { + log.Println("unable to open compiled code: " + C.GoString(C.dlerror())) + return nil + } ctx := &AOTContext{ dlHandle: handle, - vmHandle: nativeVM, } runtime.SetFinalizer(ctx, func(ctx *AOTContext) { C.dlclose(ctx.dlHandle) - C.vm_destroy(ctx.vmHandle) - C.free(unsafe.Pointer(ctx.vmHandle)) + if ctx.vmHandle != nil { + C.vm_destroy(ctx.vmHandle) + C.free(unsafe.Pointer(ctx.vmHandle)) + } }) return ctx diff --git a/platform/aot_windows.go b/platform/aot_windows.go index adc0f0b..2858d7e 100644 --- a/platform/aot_windows.go +++ b/platform/aot_windows.go @@ -7,6 +7,9 @@ import ( type AOTContext struct { } +func (c *AOTContext) Initialize(vm *exec.VirtualMachine) { +} + func (c *AOTContext) UnsafeInvokeFunction_0(vm *exec.VirtualMachine, name string) uint64 { return 0 } diff --git a/wasm-validation/lib.go b/wasm-validation/lib.go index ff9d35c..718ad2f 100644 --- a/wasm-validation/lib.go +++ b/wasm-validation/lib.go @@ -1,92 +1,19 @@ package wasm_validation import ( - "errors" - "github.com/perlin-network/life/exec" - "sync" + "bytes" + "github.com/go-interpreter/wagon/validate" + "github.com/go-interpreter/wagon/wasm" ) -type Resolver struct { -} - -func (r *Resolver) ResolveFunc(module, field string) exec.FunctionImport { - panic("not implemented") -} - -func (r *Resolver) ResolveGlobal(module, field string) int64 { - panic("not implemented") -} - -type Validator struct { - mu sync.Mutex - vm *exec.VirtualMachine - funcGetCodeBuf int - funcCheck int -} - -var globalValidator *Validator -var globalValidatorErr error -var globalValidatorInit sync.Once - -func NewValidator() (*Validator, error) { - vm, err := exec.NewVirtualMachine(ValidatorCode, exec.VMConfig{ - DefaultMemoryPages: 32, - DefaultTableSize: 128, - }, new(Resolver), nil) - +func ValidateWasm(code []byte) error { + m, err := wasm.DecodeModule(bytes.NewBuffer(code)) if err != nil { - return nil, err - } - - funcGetCodeBuf, ok := vm.GetFunctionExport("get_code_buf") - if !ok { - return nil, errors.New("cannot find get_code_buf") - } - funcCheck, ok := vm.GetFunctionExport("check") - if !ok { - return nil, errors.New("cannot find check") + return err } - - return &Validator{ - vm: vm, - funcGetCodeBuf: funcGetCodeBuf, - funcCheck: funcCheck, - }, nil -} - -func (v *Validator) ValidateWasm(input []byte) error { - v.mu.Lock() - defer v.mu.Unlock() - - _ret, err := v.vm.Run(v.funcGetCodeBuf, int64(len(input))) + err = validate.VerifyModule(m) if err != nil { return err } - ret := uint32(_ret) - if ret == 0 { - return errors.New("input too large") - } - copy(v.vm.Memory[int(ret):], input) - _ret, err = v.vm.Run(v.funcCheck, int64(ret), int64(len(input))) - ret = uint32(_ret) - - if ret == 0 { - return errors.New("validation failed") - } else if ret == 1 { - return nil - } else { - return errors.New("unknown return value") - } -} - -func GetValidator() *Validator { - globalValidatorInit.Do(func() { - globalValidator, globalValidatorErr = NewValidator() - }) - - if globalValidatorErr != nil { - panic(globalValidatorErr) // "poisoning" - } - - return globalValidator + return nil }