Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can I use go1.21 to compile plugins? #58

Open
inliquid opened this issue Sep 2, 2023 · 11 comments
Open

Can I use go1.21 to compile plugins? #58

inliquid opened this issue Sep 2, 2023 · 11 comments
Labels
help wanted Extra attention is needed

Comments

@inliquid
Copy link
Contributor

inliquid commented Sep 2, 2023

I just have tried to compile with go tool, since support for wasip1 added in go1.21, and got this error:

$ GOOS=wasip1 GOARCH=wasm go build -tags=tinygo.wasm -o wasm/plugin.wasm ./wasm
package gitlab.com/***/test-wasm-plugin/wasm
        imports gitlab.com/***/test-wasm-plugin/api
        imports github.com/knqyf263/go-plugin/wasm: build constraints exclude all Go files in C:\Users\***\go\pkg\mod\github.com\knqyf263\[email protected]\wasm

Note that I'm passing -tags=tinygo.wasm, because my plugin code is also protected with this build constraint. However, error message sounds like if go-plugin is not intended for use with official go compiler at all?

@saschagrunert
Copy link
Contributor

@knqyf263 do you see any chance to support cross compiling without tinygo?

@knqyf263
Copy link
Owner

I remember Go's wasip1 was complicated and difficult to run as Wasm, but it has been over a year since I tried it before, so we may try again. Contributions are welcome.

@saschagrunert
Copy link
Contributor

The issue seems to be that we exclude this file because CGO is disabled for GOOS=wasip1 GOARCH=wasm:

https://github.com/knqyf263/go-plugin/blob/main/wasm/plugin.go

If we enable CGO

> CGO_ENABLED=1 GOOS=wasip1 GOARCH=wasm go build -tags=tinygo.wasm examples/helloworld/plugin-evening/evening.go
# runtime/cgo
cgo: unknown ptrSize for $GOARCH "wasm"

@knqyf263
Copy link
Owner

We need to figure out how to do the same thing with Go wasip1.
#45

@saschagrunert
Copy link
Contributor

We need to figure out how to do the same thing with Go wasip1. #45

I was playing around with the code a bit. Right now don't see any way to get the memory from a malloc without cgo.

It also looks like that there will be no cgo support soon for the native golang wasm compiler: golang/go#40543

@inliquid
Copy link
Contributor Author

Big Go's wasip1 target still doesn't support exporting functions from Wasm module. See golang/go#65199 So this means. until it's done it won't be feasible.

@knqyf263
Copy link
Owner

@inliquid Thanks for sharing the issue. This issue seems to be accepted and the PR has also been merged. Does it mean this export support will come in Go 1.24?

@inliquid
Copy link
Contributor Author

inliquid commented Nov 27, 2024

@knqyf263

Thanks for sharing the issue. This issue seems to be accepted and the PR has also been merged. Does it mean this export support will come in Go 1.24?

Most likely it does. However there is another issue that needs to be considered. TinyGo does not actually stop the module when main is done as it should for "Command" type of wasip1 module (as opposed to "Reactor"). This makes it possible to call exported functions when main already exited, for instance:

// main is required for TinyGo to compile to Wasm.
func main() {
	greeting.RegisterGreeter(GoodEvening{})
}

Exiting main normally should invalidate module and its exports. However, in practice it works differently, and there is special note in Wazero's docs:

It is reasonable to think an exit error should be returned, even if the code is success (zero). Even on success, the module is no longer functional. For example, function exports would error later. However, wazero does not. The only time sys.ExitError is on error (non-zero).

This decision was to improve performance and ergonomics for guests that both use WASI (have a _start function), and also allow custom exports. Specifically, Rust, TinyGo and normal wasi-libc, don't exit the module during _start. If they did, it would invalidate their function exports. This means it is unlikely most compilers will change this behavior.

GOOS=waspi1 from Go 1.21 does exit during _start. However, it doesn't support other exports besides _start, and _start is not defined to be called multiple times anyway.

As far as I'm aware TinyGo merged "Reactor" support into dev, see tinygo-org/tinygo#4451, but not sure when it's planned for release.

Also, I'm not sure whether "Reactor" mode is 100% suitable for go-plugin. There are other things such as requirement to have exported linear memory to make host and guest able to communicate. Will it work the same way for "Reactor" modules? I don't know at the moment.

In order to make it consistent with big Go's planned implementation I see following options:

  1. Research and possibly switch to "Reactor" mode for both TinyGo and "big" Go
  2. Another option could be to stay with "Command" type, but not exit main in case of "big" Go, but instead block on something like nil channel, like we have to do in case of GOARCH=wasm GOOS=js

@knqyf263
Copy link
Owner

Thanks for the details. Sounds like we need some hacks for Go.

@leonm1
Copy link

leonm1 commented Dec 21, 2024

I recently implemented support for big Go for proxy-wasm as a wasi reactor in proxy-wasm/proxy-wasm-go-sdk#1.

The required work included:

  • Updating imports/exports to use the //go:wasmimport and //go:wasmexport directives
  • Updating types at the ABI boundary to the limited types supported by the Go compiler (int32, int64, uint32, uint64, unsafe.Pointer, bool, string, and pointers to the allowed types)
  • Adding new instructions for plugin authors to call global registration methods, since main is not called for a wasi reactor.
  • compiling with GOOS=wasip1 GOARCH=wasm go build -buildmode=c-shared

It was blocked on the wasmexport proposal and WASI reactor support, which was implemented recently by the go core team.

Also, I'm not sure whether "Reactor" mode is 100% suitable for go-plugin. There are other things such as requirement to have exported linear memory to make host and guest able to communicate. Will it work the same way for "Reactor" modules? I don't know at the moment.

I haven't looked deeply into go-plugin's ABI, but a WASI reactor can import functions exported by the host (with the //go:wasmimport directive) and export functions for the host to call (via //go:wasmexport).

The host is able to access the wasm module's linear memory, and pointers inside the linear memory can be alloc'd and passed by either side of the wasm module boundary.

I'd be happy to take a look or answer some questions if you have more questions about our approach!

@knqyf263 knqyf263 added the help wanted Extra attention is needed label Dec 25, 2024
@knqyf263
Copy link
Owner

knqyf263 commented Dec 25, 2024

@leonm1 Thanks for the detailed explanation. According to your work, adding big Go support doesn't sound so challenging. It would be great if someone could work on that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

4 participants