Giter Club home page Giter Club logo

wasmer-go's Introduction


A complete and mature WebAssembly runtime for Go based on Wasmer.

Features

  • Easy to use: The wasmer API mimics the standard WebAssembly API,
  • Fast: wasmer executes the WebAssembly modules as fast as possible, close to native speed,
  • Safe: All calls to WebAssembly will be fast, but more importantly, completely safe and sandboxed.

Documentation: browse the detailed API documentation full of examples.

Examples as tutorials: browse the examples/ directory, it's the best place for a complete introduction!

Install

To install the library, follow the classical:

$ go get github.com/wasmerio/wasmer-go/wasmer

And you're ready to get fun!

Supported platforms

This library embeds the Wasmer runtime compiled as shared library objects, and so uses cgo to consume it. A set of precompiled shared library objects are provided. Thus this library works (and is tested) on the following platforms:

Platform Architecture Triple Status
Linux amd64 x86_64-unknown-linux-gnu
aarch64 aarch64-unknown-linux-gnu
Darwin amd64 x86_64-apple-darwin
aarch64 aarch64-apple-darwin
Windows amd64 x86_64-pc-windows-msvc
What to do if your platform is missing?

Up to now, there is no script to automate that process. We are working on it.

Here are the steps to do that manually:

$ # Build the new Wasmer C API shared object library.
$ cargo build --release
$
$ # Configure cgo.
$ export CGO_CFLAGS="-I$(pwd)/wasmer/packaged/include/"
$ export CGO_LDFLAGS="-Wl,-rpath,$(pwd)/target/release/ -L$(pwd)/target/release/ -lwasmer_go"
$
$ # Run the tests.
$ just test -tags custom_wasmer_runtime

Examples

We highly recommend to read the examples/ directory, which contains a sequence of examples/tutorials. It's the best place to learn by reading examples.

But for the most eager of you, there is a quick toy program in examples/appendices/simple.go, written in Rust:

#[no_mangle]
pub extern "C" fn sum(x: i32, y: i32) -> i32 {
    x + y
}

A compiled WebAssembly binary is included in examples/appendices/simple.wasm.

Then, we can execute it in Go:

package main

import (
	"fmt"
	"io/ioutil"
	wasmer "github.com/wasmerio/wasmer-go/wasmer"
)

func main() {
    wasmBytes, _ := ioutil.ReadFile("simple.wasm")

    engine := wasmer.NewEngine()
    store := wasmer.NewStore(engine)

    // Compiles the module
    module, _ := wasmer.NewModule(store, wasmBytes)

    // Instantiates the module
    importObject := wasmer.NewImportObject()
    instance, _ := wasmer.NewInstance(module, importObject)

    // Gets the `sum` exported function from the WebAssembly instance.
    sum, _ := instance.Exports.GetFunction("sum")

    // Calls that exported function with Go standard values. The WebAssembly
    // types are inferred and values are casted automatically.
    result, _ := sum(5, 37)

    fmt.Println(result) // 42!
}

And then, finally, enjoy by running:

$ cd examples/appendices/
$ go run simple.go
42

Testing

Run the tests with the following command:

$ just test

What is WebAssembly?

Quoting the WebAssembly site:

WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable target for compilation of high-level languages like C/C++/Rust, enabling deployment on the web for client and server applications.

About speed:

WebAssembly aims to execute at native speed by taking advantage of common hardware capabilities available on a wide range of platforms.

About safety:

WebAssembly describes a memory-safe, sandboxed execution environment […].

License

The entire project is under the MIT License. Please read the LICENSE file.

FAQ

How to run Go programs compiled to WebAssembly modules with wasmer-go?

Let's start by emphasing that wasmer-go is a WebAssembly runtime. It allows to run WebAssembly inside Go. It's not a tool to compile a Go program into WebAssembly. Nonetheless, many people are reporting issues when compiling Go programs to WebAssembly, and then trying to run them with wasmer-go (or in another hosts, like Python, C, PHP, Ruby, Rust…).

The major problem is that, whilst the Go compiler supports WebAssembly, it does not support WASI (WebAssembly System Interface). It generates an ABI that is deeply tied to JavaScript, and one needs to use the wasm_exec.js file provided by the Go toolchain, which doesn't work outside a JavaScript host.

Fortunately, there are two solutions to this problem:

  1. Use TinyGo to compile your Go program to WebAssembly with the -target wasi option, e.g.:

    $ tinygo build -o module.wasm -target wasi .

    The generated WebAssembly module will be portable across all WebAssembly runtimes that support WASI.

  2. Use the Go compiler with adapters. Let's see how to compile:

    $ GOOS=js GOARCH=wasm go build -o module.wasm .

    (the GOOS=js is the sign that JavaScript is targeted, not a surprise).

    Then pick one adapter (they are written by the community):

    and follow their documentation.

We highly recommend the first solution (with TinyGo) if it works for you as the WebAssembly module will be portable across all WebAssembly runtimes. It's not a hacky solution based on adapters; it's the right way to… go.

wasmer-go's People

Contributors

adamslevy avatar antjack avatar bors[bot] avatar chai2010 avatar cvermilion avatar d0iasm avatar dependabot[bot] avatar diegobes avatar ethanfrey avatar etiennebruines avatar gzigzigzeo avatar howjmay avatar hywan avatar janfalkin avatar joesonw avatar jubianchi avatar koponen-styra avatar kris-watts-gravwell avatar markmccaskey avatar noot avatar peabodydu avatar prep avatar soulteary avatar syrusakbary avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

wasmer-go's Issues

Configure the namespace of the imports functions

Currently, the NewImports() hard-codes the import namespace as env.

If we compile a Rust program to WASM file, then the Rust compiler also hardcodes the
import namespace as env too (so the current wasmer go binding the Rust wasm output files play well together)

However, if we patch a wasm file import namespace to something else (different than env), then we won't be able to use it via the golang wasmer binding

Proposed solution

Instead of

imports, _ := wasm.NewImports().Append("sum", sum, C.sum)

Have this API:

imports, _ := wasm.NewImports().Append("my_namespace",  "sum", sum, C.sum)

Thank you! @Hywan :)

unable to install on osx - clang: warning: argument unused during compilation

Just getting started, so apologies for the noob issue:

% export CGO_ENABLED=1; export CC=gcc;
% go get github.com/wasmerio/go-ext-wasm/wasmer
% go install github.com/wasmerio/go-ext-wasm/go-wasmer
# github.com/wasmerio/go-ext-wasm/go-wasmer
clang: warning: argument unused during compilation: '-nopie' [-Wunused-command-line-argument]

Gas calculation

Summary

Is it possible to count Gas per operation like in Ethereum or perlin-life using your library to create Smart Contracts?

Allow imports to use functions from structs / Multi-threading

Motivation

I am testing several WebAssembly runtimes here. In my use case I will be sending and receiving JSON payloads to a function in a multi-threaded host. Currently Imports.Append can only use exported function pointers which are global. Unless there is some meaning to context unsafe.Pointer, I cannot encapsulate the input/output JSON in a struct per Instance.

Also, It would also be nice if compilation and execution were separate steps. In a multi-threaded host, this would mean that the cost of compilation is only incurred once.

Proposed solution

This might not be possible with CGO but somehow use the implementation interface{} parameter of Imports.Append as the execution target. Currently it is only used to read the parameters and return type. Another approach could be to pass the Instance pointer as context and include a UserData field so that you can cast and delegate to a struct implementation.

For separation of compilation and execution, there might be a func wasm.NewModuleWithImports(code []byte, imports *Imports) *Module function that returns the compiled module instructions, etc. Then module.NewInstance would return a single VM with its own memory, etc.

Additional context

General question: How does the one use the context unsafe.Pointer parameter? I've tried casting it to *Instance and *Memory. Both attempts ended poorly (core dump). A sample in the README would be super helpful.

int64 param not working for imported functions

Describe the bug

I have a wasm module that imports a function ext_print_num that takes an int64 parameter. However when I try to implement it, I get an error saying "conflicting types for ext_print_num".

Maybe this is an issue with int64? My other import functions that accept int32 all work perfectly.

Steps to reproduce

package main

// extern void ext_print_num(void *context, int64_t data);
import "C"

//export ext_print_num
func ext_print_num(context unsafe.Pointer, data int64) {
	return
}

func main() {
	bytes, err := wasm.ReadBytes("mymodule.wasm")
	if err != nil {
		return nil, err
	}
	
	imports, err := wasm.NewImports().Append("ext_print_num", ext_print_num, C.ext_print_num)
	if err != nil {
		return nil, err
	}

	instance, err := wasm.NewInstanceWithImports(bytes, imports)
	if err != nil {
		return nil, err
	}
	defer instance.Close()
}

the full code is here: https://github.com/ChainSafe/gossamer/blob/elizabeth/wasmer/runtime/wasmer.go

Expected behavior

I wouldn't expect an error since the types should match.

Actual behavior

In file included from _cgo_export.c:4:0:
cgo-gcc-export-header-prolog:58:13: error: conflicting types for ‘ext_print_num’
In file included from _cgo_export.c:4:0:
wasmer.go:9:14: note: previous declaration of ‘ext_print_num’ was here
_cgo_export.c:176:6: error: conflicting types for ‘ext_print_num’
 void ext_print_num(void* p0, GoInt64 p1)
      ^~~~~~~~~~~~~
In file included from _cgo_export.c:4:0:
wasmer.go:9:14: note: previous declaration of ‘ext_print_num’ was here
FAIL	github.com/ChainSafe/gossamer/runtime [build failed]

Additional context

Also, just wondering if you have a gitter or some other channel I can reach you on. Thanks :)

Can't handle multiple output

Hi,
Trying to return multiple result using wasmer. Here is sample code

#[no_mangle]
pub extern fn test(x: i32, y: i32, z: i32) -> (i32, i32, i32) {
    (x, y, z) 
}

// try this since test function returning 0
#[no_mangle]
pub extern fn sub_main() -> (i32, i32, i32) {
   test(5, 1, 3)
}

fn main() {}

When I try to call test function from wasmer

sum := instance.Exports["test"] 
result, _ := sum(4, 5, 3)

It is returning result as 0. And if I call sub_main function

sum := instance.Exports["sub_main"] 
result, _ := sum()

It is returning error

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x4a2f15]

goroutine 1 [running]:
main.main()
        /go-ext-plugin/wasmer-ext.go:21 +0x125
exit status 2

Is go-wasmer can handle wasm file build from golang codes

Summary

Thank u for your wonderful work. I ran all the example, and it works well on my mac. But when I intend to replace the simple.wam with my wasm, some thing panic happend.

Additional details

Here is my go file:
image
And I use this command to build my wasm File:
GOARCH=wasm GOOS=js go build -o hello.wasm hello.go
Then I replace the file and function call by your example code:
image
But when I try to run 'my example',panic happend!
image

Any help for me?

How can I have []byte array in a struct that is passed as my Context

Summary

I need to pass some byte[] array in addition to a few other values as my Context data. The unsafe Pointer thing with CGO seems to limit passing pointers with things other that value types.

Do you know of a workaround to allow passing more complex context object around.

"bad magic number" from wasm blob built with Go

I have a small go file:

package w

func sum(x, y int) int {
	return x + y
}

I build it to a wasm blob:

GOOS=js GOARCH=wasm go build -o web.wasm w/w.go

But when running wasmer.NewInstance on it, I get:

Failed to instantiate the module:
    compile error: Validation error "Bad magic number"

Does go not build compatible wasm blobs?

Close gap with CLI

This may be a meta issue, it's not specific to the Go bindings (feel free to close/move the issue), but I'm opening it here since I'd be using the Go bindings.

Motivation

Users expect to be able to do the same things from the Go code than from the wasmer CLI.
Very concretely I need --mapdir and --env (wasmerio/wasmer#475).

Proposed solution

Tediously implement all new CLI features in all the bindings.

Alternatives

Generate an Options struct that would be passed as a parameter to a NewInstance() constructor. The struct could be generated from a simple struct definition in the wasmer repo.

unable to handle `f32` types.

Hi, I couldn't handle f32 types.
Here is the code

#[no_mangle] 
pub extern fn sum(x:f32, y:f32) -> f32 {
x + y
}

I'm getting result as 0 when I pass it in go-wasmer
Thnaks

Using wasmer from goroutines

Summary

What is the safe way to use wasmer from multiple goroutines?

Additional details

If instance (result of wasm.NewInstance) is used by only one goroutine at any time - is it enough? The potential problem is that one goroutine might be executed by few threads.

"just test" fails

Describe the bug

just build and just build-bin work great, however just test fails.
I confirmed this for both master and 0.2.0 on my Ubuntu machine.

Steps to reproduce

run just test in the top directory

This can be reproduced minimally by:

cd wasmer
go test test/imports.go 

Expected behavior

I assume tests pass.

Actual behavior

output:

just test
# command-line-arguments
In file included from _cgo_export.c:4:0:
cgo-gcc-export-header-prolog:46:16: error: conflicting types for ‘sum_i64’
In file included from _cgo_export.c:4:0:
imports.go:6:17: note: previous declaration of ‘sum_i64’ was here
_cgo_export.c:46:9: error: conflicting types for ‘sum_i64’
 GoInt64 sum_i64(void* p0, GoInt64 p1, GoInt64 p2)
         ^~~~~~~
In file included from _cgo_export.c:4:0:
imports.go:6:17: note: previous declaration of ‘sum_i64’ was here
FAIL    command-line-arguments [build failed]

Additional context

I was trying to build it all from source and use a custom *.so. I thought I caused the error, but even after checking out 0.2.0 and running git clean -xcf and rm -rf target I still get the error.

Memory growth does not appear to be supported in Go

Motivation

Right now in Go, we get a Memory, but it's not clear how many pages it gets and there is no way to manage (grow) it dynamically.

Proposed solution

Rust lib has wasmer_memory_grow which is already linked, but not exposed in Go binding.

Setup Bors

Bors has just been enabled, we must finish the setup.

Panic when attempting to execute function that doesn't exist in wasm

Describe the bug

When calling a function that doesn't exist in the wasm module, a panic: runtime error: invalid memory address or nil pointer dereference [recovered] occurs.

Steps to reproduce

package main

import (
	"fmt"
	wasm "github.com/wasmerio/go-ext-wasm/wasmer"
)

func main() {
	bytes, _ := wasm.ReadBytes("mymodule.wasm")
	instance, _ := wasm.NewInstance(bytes)
	defer instance.Close()
	myfunc := instance.Exports["nonexistentfunction"]
	res, _ := myfunc()
        fmt.Print(res)
}

Expected behavior

Either, we should not be allowed to get an instance.Export that doesn't exist, or when trying to call the nonexistent function there should be an error saying it doesn't exist.

Actual behavior

panic: runtime error: invalid memory address or nil pointer dereference [recovered]
	panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x8458f2]

at the line res, _ := myfunc()

Problem while doing go get

Summary

I tried doing go get github.com/wasmerio/go-ext-wasm/wasmer

but it throws me error:

../go/src/github.com/wasmerio/go-ext-wasm/wasmer/bridge.go:162:87: cannot use (*_Ctype_struct___13)(instanceContext) (type *_Ctype_struct___13) as type *_Ctype_struct___16 in argument to _Cfunc_wasmer_instance_context_data_get

Additional details

My go env

GOARCH="amd64"
GOBIN=""
GOCACHE="/home/ubuntu/.cache/go-build"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/ubuntu/go"
GOPROXY=""
GORACE=""
GOROOT="/usr/local/go"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build913563706=/tmp/go-build -gno-record-gcc-switches"

Initially, I was getting error:

ubuntu@ip:~/1$ go get github.com/wasmerio/go-ext-wasm/wasmer
# github.com/wasmerio/go-ext-wasm/wasmer
exec: "gcc": executable file not found in $PATH

Than I did sudo apt install gcc and installed gcc.

Post that the go get for wasmer is giving mentioned error.

Any pointer on what might have gone wrong?

Support for both wasi_unstable and wasi_snapshot_preview1 import namespaces

Thanks to the commits of @Hywan today (see #72, so also thanks @MarkMcCaskey and @syrusakbary, of course), it is now possible to run WebAssembly code that makes use of the WASI ABI in go-ext-wasm.

We did encounter one minor issue though. The the current releases of Clang, Wasienv, and Emscripten still produce binaries that use the wasi_unstable import namespace.

Wasmer 0.11.0 seems to able to handle both this older, and the newer wasi_snapshot_preview1 namespaces. But go-ext-wasm only supports the latter

We currently handle this by running "wasm2wat", find and replace wasi_unstable with wasi_snapshot_preview1, and then run "wat2wasm".

That works just fine, but it would be easier if go-ext-wasm could somehow handle both these, and future, namespaces.

Benchmarks strongly favor wasmer runtime characteristics

Big fan of wasmer, but the benchmarks seem a little skewed. They are run in a Go benchmark loop and the compile time is included. I believe wasmer has an in-memory compile cache so likely better to hoist the cache step. Life can also be compiled AOT and compiles to native code:

vm, _ := life.NewVirtualMachine(wasmBytes, life.VMConfig{}, &life.NopResolver{}, nil)
aotSvc := platform.FullAOTCompile(vm)
vm.SetAOTService(aotSvc)
for i := 0; i < b.N; i++ {
    entryID, _ := vm.GetFunctionExport(exportName)
    result, _ := vm.Run(entryID, exportValues...)
    _ = result
}

After hoisting the compile step and enabling AOT for life I see these results:

pkg: github.com/wasmerio/go-ext-wasm/benchmarks-wasmer
Benchmark_Wasmer_Nbody-4                 	      30	  42971576 ns/op
pkg: github.com/wasmerio/go-ext-wasm/benchmarks-life
Benchmark_Life_Nbody-4                 	              30	  38405537 ns/op
pkg: github.com/wasmerio/go-ext-wasm/benchmarks-wagon
Benchmark_Wagon_Nbody-4                 	       1	2018888838 ns/op

How can we pass `String` or `str` data type in go-wasmer?

How can we pass String data types?
I could see this example, where we can pass it as a pointer. or we can use C_String (I'm not recommending this approach for my work)

 #[no_mangle]
pub extern fn string() -> *const u8 {
    b"Hello, World!\0".as_ptr()
}

How can I pass str or String directly?

Or How can we pass, one of the parameter as u8 array and the return type as f64?
EX:

#[no_mangle]
pub extern fn test( b:f64, s:*const u8) -> f64 {
 match s {
  "x" => b * b,
   _ => 0
  }
}

Thanks,

Windows support?

Uncertain whether to label this issue a bug report or an enhancement.

I tried to make go-ext-wasm work in Windows by manually copying the 0.7.0 wasmer_runtime_c_api.dll to github.com\wasmerio\go-ext-wasm\wasmer.

Yet on seeking to run the example I receive Process finished with exit code -1073741515 (0xC0000135)

Is it possible to get go-ext-wasm running in Windows somehow? Or does the go extension currently not support Windows yet?

How to access instance memory from imported go function

Summary

I am trying to access the memory of the wasm instance from an imported Go function, but I can't find info on this. Here's my imported Go func:

//export ext_print_utf8
func ext_print_utf8(context unsafe.Pointer, offset int32, size int32) {
	// want to print out some string from inside the instance memory at [offset:offset+size]
}


IDL RPC standard yet ?

Summary

Is there a standard yet ? For example many use protobufs as an IDL to describe and code generate the stubs to allow inter process communication.
We need something like this for wasi I think and I have not found it

Additional details

Have Mozilla specified this yet ?

The idea is that if party A writes in python and party B writes in golang, then how can the golang code call / RPC the python code when everyone is compiling to WASM.

Also you have the other aspect of message queues where your calling a wasi on another server and you have no idea if they are on the network or partitioned. This is sort of another issue but related.

Add support uint8, uint16, uint32, uint64 and []byte types

Thanks for proposing a new feature!

Motivation

I would like to have support of all types, which are present at the same time in Wasm and Go. No I see error: only accept int32, int64, float32, and float64.

Proposed solution

Change switches in func (imports *Imports) Append etc

Ability to pause, save state and restore VM

Motivation

With ability to pause the VM, save it's state (memory, execution context) and restore it later
it'd be possible to created sandboxed environments with ability to migrate them.

load wasm that build with golang

Thanks for the bug report!

Describe the bug

when I build wasm with go and wasm.NewInstance from the wasm bin, error result with:

Failed to compile the module.

A clear and concise description of what the bug is.

Steps to reproduce

  1. build wasm with go
    GOARCH=wasm GOOS=js go build -o wa.wasm wa.go
    cat wa.go:
package main

//export Sum
func Sum(x, y int) int {
	return x + y
}

func main() {
}
  1. build go program
    go build main.go
    cat main.go:
package main

import (
	"fmt"
	wasm "github.com/wasmerio/go-ext-wasm/wasmer"
)

func main() {
	bytes, err := wasm.ReadBytes("wa.wasm");
	if err != nil{
		fmt.Println(err);
		return
	}
	inst, err := wasm.NewInstance(bytes);
	if err != nil{
		fmt.Println(err);
		return
	}
	defer inst.Close();

	sum := inst.Exports["sum"]

	result, err := sum(5,37);
	if err != nil{
		fmt.Println(err);
		return;
	}
	fmt.Println(result);
}
  1. Run
./main
  1. See error
    Failed to compile the module.

Global and Memory imports

Thanks for proposing a new feature!

Motivation

How can i import global and memory from Go side when executing wasm bytecode? It's a feature in need when implementing smart contract virtual machine~

support abort the VM execution from imported function call.

Motivation

In our use case, we need do some check in the imported go function, if check failed, it means a fatal error, and we need abort the whole VM execution, so add a InstanceContext.Trap(err) like method for imported function to call will be helpful.

Option to select backend

Motivation

See also Wasmer issue 605 : It would be great if it would be possible to dynamically choose different back-ends, instead of defaulting to Cranelift. For instance, the LLVM back-end might be preferred for the speed of execution of the output, and also features like SIMD that are currently exclusive to LLVM (though for the latter, SIMD would first need to be exposed through the C API, see here).

Proposed solution

Expose compile_with - first in C API, then in go-ext-wasm.

Support for modules without exported memory

NewInstanceWithImports should support loading valid modules that do not export memory.

Motivation

NewInstanceWithImports currently will fail instantiation with No memory exported if the valid loaded module does not export memory. There are legitimate cases for "context-free" , stateless programs that do not need to export memory.

Spec says In the current version of WebAssembly, **at most one** memory is allowed in a module.

Proposed solution

Allow loading modules without memory.

Failed to run life benchmark with special wasm test file

Thanks for the bug report!

Describe the bug

Failed to run life benchmark with pollard_rho_128.wasm and snappy_compress.wasm file

Steps to reproduce

  1. Go to benchmarks dir
  2. go test --bench=".*" -cpuprofile=cpu.profile ./life
  3. error stack:
    goos: linux
    goarch: amd64
    pkg: github.com/wasmerio/go-ext-wasm/benchmarks/life
    Benchmark_Life_Nbody 1 3238359573 ns/op
    Benchmark_Life_Fibonacci_Recursive 1 4683564409 ns/op
    panic: runtime error: invalid memory address or nil pointer dereference
    [signal SIGSEGV: segmentation violation code=0x1 addr=0x48 pc=0x56215b]

goroutine 13 [running]:
github.com/perlin-network/life/exec.(*VirtualMachine).getExport(...)
/home/steven/work/src/github.com/perlin-network/life/exec/vm.go:737
github.com/perlin-network/life/exec.(*VirtualMachine).GetFunctionExport(...)
/home/steven/work/src/github.com/perlin-network/life/exec/vm.go:760
github.com/wasmerio/go-ext-wasm/benchmarks/life_test.benchmarkLife(0xc000072000, 0x5c0648, 0x14, 0x5bd597, 0x8, 0x0, 0x0, 0x0)
/home/steven/work/src/github.com/wasmerio/go-ext-wasm/benchmarks/life/benchmarks_test.go:43 +0x18b
github.com/wasmerio/go-ext-wasm/benchmarks/life_test.Benchmark_Life_Pollard_Rho_128(0xc000072000)
/home/steven/work/src/github.com/wasmerio/go-ext-wasm/benchmarks/life/benchmarks_test.go:31 +0x66
testing.(*B).runN(0xc000072000, 0x1)
/usr/local/go/src/testing/benchmark.go:176 +0xb3
testing.(*B).run1.func1(0xc000072000)
/usr/local/go/src/testing/benchmark.go:249 +0x5a
created by testing.(*B).run1
/usr/local/go/src/testing/benchmark.go:242 +0x7d
exit status 2
FAIL github.com/wasmerio/go-ext-wasm/benchmarks/life 8.176s

when I use perlin-network/life to run pollard_rho_128.wasm, it also throw error:
cmd:
./life -entry 'app_main' ~/work/src/github.com/wasmerio/go-ext-wasm/wasmer/test/testdata/benchmarks/pollard_rho_128.wasm
error stack:
panic: Error: inconsistent stack pattern: pt = false, lpt = false, ls = 1, sd = 0
---GO TRACEBACK---
goroutine 1 [running]:
runtime/debug.Stack(0x58bce0, 0xc000051180, 0x6312e0)
/usr/local/go/src/runtime/debug/stack.go:24 +0x9d
github.com/perlin-network/life/utils.CatchPanic(0xc000299aa0)
/home/steven/work/wasm/life/utils/catch.go:11 +0x86
panic(0x58bce0, 0xc000051180)
/usr/local/go/src/runtime/panic.go:522 +0x1b5
github.com/perlin-network/life/compiler.(*SSAFunctionCompiler).Compile(0xc0002999e8, 0xc000299800, 0x0, 0x0)
/home/steven/work/wasm/life/compiler/ssa.go:336 +0xca45
github.com/perlin-network/life/compiler.(*Module).CompileForInterpreter(0xc00000e020, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
/home/steven/work/wasm/life/compiler/module.go:237 +0xae8
github.com/perlin-network/life/exec.NewVirtualMachine(0xc00010a000, 0xcc859, 0xcca59, 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x10000, ...)
/home/steven/work/wasm/life/exec/vm.go:437 +0xd0
main.main()
/home/steven/work/wasm/life/main.go:82 +0x2f6

goroutine 1 [running]:
main.main()
/home/steven/work/wasm/life/main.go:89 +0xbe6

Ability to limit VM memory and execution time

Thanks for proposing a new feature!

Motivation

Ability to limit the memory of the VM and it's execution time allows us to build sandboxed environments.

Proposed solution

Export these functions in the go wrapper.

InstanceContext.Data pointers may become stale and cause panics

Describe the bug

Using a uintptr to work around CGo FFI limitations in PR #85 seemed like a good idea, and the tests are passing, but when I went to update Gossamer's runtime with the latest code I found that this approach has introduced a very serious bug that now affects all InstanceContext Data. I get various unpredictable panics when applying a type assertion on InstanceContext.Data(). Among the panics that I observed, I believe the most accurate error message that I got was

fatal error: exitsyscall: syscall frame is no longer valid

When the Go runtime has to grow a stack, the stack is copied and all pointers on the stack must be updated to the offset from the proper start of the new stack. However uintptr is not treated specially in Go, and so is not updated when this occurs. This results in the pointer becoming invalid.

Some useful further reading about this sort of issue:
https://grokbase.com/t/gg/golang-nuts/147qqaky1h/go-nuts-uintptr-to-go-object-unsafe-to-pass-to-any-function-including-syscalls-c-functions
https://blog.wallaroolabs.com/2018/04/adventures-with-cgo-part-1--the-pointering/
https://blog.cloudflare.com/how-stacks-are-handled-in-go/

Steps to reproduce

At the moment none of the tests in this repo reproduce the issue and I am working on adding some tests that panic reliably due to this issue.

However it can be easily reproduced in the Gossamer runtime tests when using the latest version of this package. I have a fork with a branch that reproduces this:

$ git clone https://github.com/AdamSLevy/gossamer.git
$ cd gossamer
$ git checkout RemoveRuntimeRegistry
$ go test ./runtime/...
t=2019-12-03T13:32:21-0900 lvl=dbug msg=[NewRuntime] runtimeCtx="{trie:0xc0000bd240 allocator:0xc00017e000 keystore:0xc0000bd260}"
t=2019-12-03T13:32:21-0900 lvl=trce msg="[ext_malloc] executing..."
fatal error: exitsyscall: syscall frame is no longer valid

goroutine 20 [running, locked to thread]:
runtime.throw(0x9a4891, 0x2d)
	/usr/lib/go/src/runtime/panic.go:774 +0x72 fp=0xc00018fb50 sp=0xc00018fb20 pc=0x435e02
runtime.exitsyscall()
	/usr/lib/go/src/runtime/proc.go:2957 +0x275 fp=0xc00018fb80 sp=0xc00018fb50 pc=0x43ead5
runtime.cgocall(0x871870, 0xc00018fbe8, 0xc)
	/usr/lib/go/src/runtime/cgocall.go:138 +0x9e fp=0xc00018fbb8 sp=0xc00018fb80 pc=0x4097ce
github.com/wasmerio/go-ext-wasm/wasmer._Cfunc_wasmer_instance_call(0x23e9520, 0x24a4b80, 0xc0001723c0, 0xc000000002, 0xc00016c2f0, 0x1, 0xc000000002)
	_cgo_gotypes.go:730 +0x4d fp=0xc00018fbe8 sp=0xc00018fbb8 pc=0x6421bd
github.com/wasmerio/go-ext-wasm/wasmer.cWasmerInstanceCall(...)
	/home/aslevy/go/pkg/mod/github.com/wasmerio/[email protected]/wasmer/bridge.go:381
github.com/wasmerio/go-ext-wasm/wasmer.getExportsFromInstance.func1(0xc000180080, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0)
	/home/aslevy/go/pkg/mod/github.com/wasmerio/[email protected]/wasmer/instance.go:359 +0x35e fp=0xc00018fdb8 sp=0xc00018fbe8 pc=0x64326e
github.com/ChainSafe/gossamer/runtime.(*Runtime).Exec(0xc00016eb40, 0x99187c, 0xc, 0x1, 0xc00018fee8, 0x0, 0x0, 0xf, 0xd3bc3f, 0x22, ...)
	/home/aslevy/repos/gossamer/runtime/wasmer.go:130 +0x1c8 fp=0xc00018fe88 sp=0xc00018fdb8 pc=0x85cee8
github.com/ChainSafe/gossamer/runtime.TestExecVersion(0xc00010a200)
	/home/aslevy/repos/gossamer/runtime/wasmer_test.go:111 +0x1d0 fp=0xc00018ff70 sp=0xc00018fe88 pc=0x85db80
testing.tRunner(0xc00010a200, 0x9b37f0)
	/usr/lib/go/src/testing/testing.go:909 +0xc9 fp=0xc00018ffd0 sp=0xc00018ff70 pc=0x504ef9
runtime.goexit()
	/usr/lib/go/src/runtime/asm_amd64.s:1357 +0x1 fp=0xc00018ffd8 sp=0xc00018ffd0 pc=0x464de1
created by testing.(*T).Run
	/usr/lib/go/src/testing/testing.go:960 +0x350

goroutine 1 [chan receive]:
testing.(*T).Run(0xc00010a200, 0x9933e2, 0xf, 0x9b37f0, 0x489e86)
	/usr/lib/go/src/testing/testing.go:961 +0x377
testing.runTests.func1(0xc00010a100)
	/usr/lib/go/src/testing/testing.go:1202 +0x78
testing.tRunner(0xc00010a100, 0xc000117dc0)
	/usr/lib/go/src/testing/testing.go:909 +0xc9
testing.runTests(0xc0000bd220, 0xd4ba60, 0x17, 0x17, 0x0)
	/usr/lib/go/src/testing/testing.go:1200 +0x2a7
testing.(*M).Run(0xc000106100, 0x0)
	/usr/lib/go/src/testing/testing.go:1117 +0x176
main.main()
	_testmain.go:88 +0x135
FAIL	github.com/ChainSafe/gossamer/runtime	0.348s
ok  	github.com/ChainSafe/gossamer/runtime/allocator	(cached)
FAIL

Alternatively you may see this panic instead:

t=2019-12-03T13:32:29-0900 lvl=dbug msg=[NewRuntime] runtimeCtx="{trie:0xc00000f280 allocator:0xc000164000 keystore:0xc00000f2a0}"
t=2019-12-03T13:32:29-0900 lvl=trce msg="[ext_malloc] executing..."
--- FAIL: TestExecVersion (0.32s)
panic: <invalid reflect.Value> [recovered]
	panic: <invalid reflect.Value>

goroutine 7 [running]:
testing.tRunner.func1(0xc0000f4200)
	/usr/lib/go/src/testing/testing.go:874 +0x3a3
panic(0x8d3260, 0xc0001680b0)
	/usr/lib/go/src/runtime/panic.go:679 +0x1b2
github.com/ChainSafe/gossamer/runtime.ext_malloc(0x292e4c0, 0xc000000001, 0xc0001759e8)
	/home/aslevy/repos/gossamer/runtime/imports.go:87 +0x222
github.com/ChainSafe/gossamer/runtime._cgoexpwrap_09d97fc801d6_ext_malloc(0x292e4c0, 0x1, 0x0)
	_cgo_gotypes.go:210 +0x33
github.com/wasmerio/go-ext-wasm/wasmer._Cfunc_wasmer_instance_call(0x7ff79801d710, 0x2925d20, 0xc0001583c0, 0xc000000002, 0xc0001522f0, 0x1, 0xc000000000)
	_cgo_gotypes.go:730 +0x4d
github.com/wasmerio/go-ext-wasm/wasmer.cWasmerInstanceCall(...)
	/home/aslevy/go/pkg/mod/github.com/wasmerio/[email protected]/wasmer/bridge.go:381
github.com/wasmerio/go-ext-wasm/wasmer.getExportsFromInstance.func1(0xc000166080, 0x2, 0x2, 0x0, 0x0, 0x0, 0x0)
	/home/aslevy/go/pkg/mod/github.com/wasmerio/[email protected]/wasmer/instance.go:359 +0x35e
github.com/ChainSafe/gossamer/runtime.(*Runtime).Exec(0xc000154b40, 0x99187c, 0xc, 0x1, 0xc000175ee8, 0x0, 0x0, 0xf, 0xd3bc3f, 0x22, ...)
	/home/aslevy/repos/gossamer/runtime/wasmer.go:130 +0x1c8
github.com/ChainSafe/gossamer/runtime.TestExecVersion(0xc0000f4200)
	/home/aslevy/repos/gossamer/runtime/wasmer_test.go:111 +0x1d0
testing.tRunner(0xc0000f4200, 0x9b37f0)
	/usr/lib/go/src/testing/testing.go:909 +0xc9
created by testing.(*T).Run
	/usr/lib/go/src/testing/testing.go:960 +0x350
FAIL	github.com/ChainSafe/gossamer/runtime	0.351s
ok  	github.com/ChainSafe/gossamer/runtime/allocator	(cached)
FAIL

After re-running the tests about 4 times you will likely see both panics.

Additional context

Originally I had proposed using a "registry" map to store InstanceContext data without passing any pointers across the CGo FFI but @losfair suggested using uintptr. This seemed to work until I came across this issue.

I don't think that there is a safe way to pass Go pointers across the CGo boundary, and even if there were a hack that could allow this to work, Go may change the rules around how the CGo FFI works, as they have done in the past. Using a registry map avoids all of those complex issues.

I am going to open a new PR that implements the registry map instead of the current approach. I highly recommend getting this merged soon because now all InstanceContext.Data may suffer from this issue, not just those data types that the PR was originally supposed to address.

Apologies for not more thoroughly testing the current approach.

Is go-wasmer can handle C codes?

Using go-wasmer we were able to handle Rust code by calling extern function.

can we do same for C codes?
is it compatible now?

If yes. How can we call C code as an extern function?

Running C++ exported functions from go-ext-wasm fails.

I am trying to call basic "main()" functions in some c and cpp based wasms from go-ext-wasm:

...

result, err := instance.Exports["main"](1,1)
if err != nil { panic(err) }

...

But go-ext-wasm returns:

panic: Failed to call the `main` exported function.

Is there some way other way to run main()?

Handling panics in wasm code?

Summary

Hello, let's suppose I have an exported function in Rust that I call and it does:

panic!("time to panic");

I would probably expect that the function will return an error, but I get this:

SIGABRT: abort
PC=0x7f23653747bb m=0 sigcode=18446744073709551610

goroutine 405 [running]:
runtime: unknown pc 0x7f23653747bb
stack: frame={sp:0xc0000097d0, fp:0x0} stack=[0xc000016000,0xc00001a000)

runtime: unknown pc 0x7f23653747bb
stack: frame={sp:0xc0000097d0, fp:0x0} stack=[0xc000016000,0xc00001a000)

created by github.com/rusenask/reactor/vendor/google.golang.org/grpc.(*Server).serveStreams.func1
	/usr/local/go/src/github.com/rusenask/reactor/vendor/google.golang.org/grpc/server.go:715 +0xa1
....
....

and it just stops. Ideally I wouldn't want my application to crash. Should I try catching SIGABRT and ignore it?

Add interface types support

Thanks for proposing a new feature!

Motivation

I would like to embed Go wasm code. Currently Go comes with syscall/js bindings. Having them implemented in go-ext-wasm would allow easy communication with the hosted Go code.

Proposed solution

Implement API bindings to syscall/js.

Alternatives

The better alternative would be a native way to bind to the hosted Go process functions. Ideally allowing more complex argument types like structures.

Make `GetLastError` threadsafe

Summary

If I am running multiple independent wasmer instances concurrently in separate goroutines, can those goroutines safely call wasmer.GetLastError() and trust that they will return the error relevant to their local instance?

For example, say I have multiple goroutine's doing the following. Can errStr be relied on?

func runWasm() {
    // load a Module from some wasm called mod
...

    i, _ := mod.Instantiate()
    v, err := i.Exports["run"]
    if err != nil {
        errStr, _ := wasmer.GetLastError()
        panic(errStr)
    }
    ...
}

example imported_function.wasm could not determine kind of name for C.sum

func sum(context unsafe.Pointer, x int32, y int32) int32 {
	return x + y
}

func Test3()  {
	bytes, err := wasm1.ReadBytes("/Users/andy/Downloads/imported_function.wasm")
	if err != nil {
		fmt.Println(err)
	}
	imports, err := wasm1.NewImports().Append("sum", sum, C.sum)
	if err != nil {
		fmt.Println(err)
	}

	instance, _ := wasm1.NewInstanceWithImports(bytes, imports)

	// Close the WebAssembly instance later.
	defer instance.Close()

	// Gets the `add1` exported function from the WebAssembly instance.
	add1 := instance.Exports["add1"]

	// Calls that exported function.
	// As a reminder: `add1` is defined as `fn add1(x: i32, y: i32) -> i32 { sum(x, y) + 1 }`
	result, _ := add1(1, 2)

	// Should output 4 (= 1 + 2 + 1).
	fmt.Println(result)
}

could not determine kind of name for C.sum


Edit from @Hywan: Markup.

Error: UnableToCreateMemory

Summary

When I iteratively create an instance of a module and then close it, I get an UnableToCreateMemory error.

Additional details

Here is the code I'm running:

package main

import (
	"fmt"
	wasm "github.com/wasmerio/go-ext-wasm/wasmer"
)

func main() {
	bytes, err := wasm.ReadBytes("./simple.wasm")
	if err != nil { panic(err) }

	module, err := wasm.Compile(bytes)
	if err != nil { panic(err) }
	defer module.Close()

	for i:=0; i<1000000; i++ {
		fmt.Println(i)

		instance, err := module.Instantiate()
		if err != nil { panic(err) }
		instance.Close()
	}
}

For reference, here is the body of the simple.wasm file in base64 encoding, but I'd assume the results will be similar with any wasm binary.

AGFzbQEAAAABBwFgAn9/AX8DAgEABAUBcAEBAQUDAQAQBhECfwBBgIDAAAt/AEGAgMAACwdHBQZtZW1vcnkCABlfX2luZGlyZWN0X2Z1bmN0aW9uX3RhYmxlAQALX19oZWFwX2Jhc2UDAApfX2RhdGFfZW5kAwEDc3VtAAAJAQAKCQEHACAAIAFqCw==

Here is the tail of the output that I get:

32723
32724
32725
32726
panic: Failed to instantiate the module:
    link error: unable to create memory: UnableToCreateMemory

goroutine 1 [running]:
main.main()
	/demo/src/hello/hello.go:20 +0x250
exit status 2

The iteration count at which it fails (the last printed number) is non-deterministic, it varies a bit between re-runs.

I must be doing something wrong, can you help find the issue?

Write data into the initial memory

Thanks for proposing a new feature!

Motivation

Write custom data into the initial memory from Go side and let the functions in wasm can use it~

panic: runtime error: index out of range

Tried the example which is there in docs

package main

import (
    "fmt"
    wasm "github.com/wasmerio/go-ext-wasm/wasmer"
    "path"
    "runtime"
)

func simpleWasmFile() string {
    _, filename, _, _ := runtime.Caller(0)
    return path.Join(path.Dir(filename), "test", "testdata", "examples", "simple.wasm")
}

func main() {
    // Reads the WebAssembly module as bytes.
    bytes, _ := wasm.ReadBytes(simpleWasmFile())

    // Instantiates the WebAssembly module.
    instance, _ := wasm.NewInstance(bytes)

    // Close the WebAssembly instance later.
    defer instance.Close()

    // Gets the `sum` exported function from the WebAssembly instance.
    sum := instance.Exports["sum"]

    // Calls that exported function with Go standard values. The
    // WebAssembly types are inferred and values are casted
    // automatically.
    result, _ := sum(1, 2)

    fmt.Print("Result of `sum(1, 2)` is: ")
    fmt.Println(result)

}

error logs

go run main.go
panic: runtime error: index out of range

goroutine 1 [running]:
github.com/wasmerio/go-ext-wasm/wasmer.NewInstanceWithImports.func1(0x0, 0x0, 0xc00005dd30, 0x7a47c8, 0x227f)
        /home/achala/go/src/github.com/wasmerio/go-ext-wasm/wasmer/instance.go:90 +0x1fd
github.com/wasmerio/go-ext-wasm/wasmer.newInstanceWithImports(0xc00000e060, 0xc00005de40, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0)
        /home/achala/go/src/github.com/wasmerio/go-ext-wasm/wasmer/instance.go:161 +0x307
github.com/wasmerio/go-ext-wasm/wasmer.NewInstanceWithImports(0x0, 0x0, 0x0, 0xc00000e060, 0x0, 0x0, 0x0, 0x4f08a0, 0xc000090150, 0x4d6880)
        /home/achala/go/src/github.com/wasmerio/go-ext-wasm/wasmer/instance.go:83 +0x76
github.com/wasmerio/go-ext-wasm/wasmer.NewInstance(0x0, 0x0, 0x0, 0x0, 0x0, 0x4f08a0, 0xc000090150, 0xc000012220, 0xc00004a738)
        /home/achala/go/src/github.com/wasmerio/go-ext-wasm/wasmer/instance.go:78 +0xa1
main.main()
        /home/achala/codes/wasmexamples/main.go:20 +0x5c
exit status 2

Able to pass simple .wasm files but from github or from directory

I want to pass something like directly from github ex https://github.com/wasmerio/go-ext-wasm/blob/master/wasmer/test/testdata/examples/simple.wasm

how can I achieve that?

How to run wasm that build with golang

Thanks for the bug report!

Describe the bug

the same as #18
error result with:

Failed to instantiate the module:
    8 link errors: (1 of 8) Import not found, 
namespace: go, name: debug (2 of 8) Import not found, 
namespace: go, name: runtime.wasmExit (3 of 8) Import not found, 
namespace: go, name: runtime.wasmWrite (4 of 8) Import not found, 
namespace: go, name: runtime.nanotime (5 of 8) Import not found, 
namespace: go, name: runtime.walltime (6 of 8) Import not found, 
namespace: go, name: runtime.scheduleTimeoutEvent (7 of 8) Import not found, 
namespace: go, name: runtime.clearTimeoutEvent (8 of 8) Import not found, 
namespace: go, name: runtime.getRandomData

Please, can you provide an example? thx

Steps to reproduce

  1. Go to '…'
  2. Compile with '…'
  3. Run '…'
  4. See error

If applicable, add a link to a test case (as a zip file or link to a repository we can clone).

Expected behavior

A clear and concise description of what you expected to happen.

Actual behavior

A clear and concise description of what actually happened.

If applicable, add screenshots to help explain your problem.

Additional context

Add any other context about the problem here.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.