wa-lang.org/wazero@v1.0.2/site/content/languages/go.md (about) 1 +++ 2 title = "Go" 3 +++ 4 5 ## Introduction 6 7 When `GOARCH=wasm GOOS=js`, Go's compiler targets WebAssembly Binary format 8 (%.wasm). 9 10 Here's a typical compilation command: 11 ```bash 12 $ GOOS=js GOARCH=wasm go build -o my.wasm . 13 ``` 14 15 The operating system is "js", but more specifically it is [wasm_exec.js][1]. 16 This package runs the `%.wasm` just like `wasm_exec.js` would. 17 18 ## Experimental 19 20 It is important to note that while there are some interesting features, such 21 as HTTP client support, the ABI (host functions imported by Wasm) used is 22 complicated and custom to Go. For this reason, there are few implementations 23 outside the web browser. 24 25 Moreover, Go defines js "EXPERIMENTAL... exempt from the Go compatibility 26 promise." While WebAssembly signatures haven't broken between 1.18 and 1.19, 27 then have in the past and can in the future. 28 29 For this reason implementations such as wazero's [gojs][14], cannot guarantee 30 portability from release to release, or that the code will work well in 31 production. 32 33 Due to lack of adoption, support and relatively high implementation overhead, 34 most choose [TinyGo]({{< relref "/tinygo.md" >}}) to compile source code, even if it supports less 35 features. 36 37 ## WebAssembly Features 38 39 `GOARCH=wasm GOOS=js` uses instructions in [WebAssembly Core Specification 1.0] 40 [15] unless `GOWASM` includes features added afterwards. 41 42 Here are the valid [GOWASM values][16]: 43 * `satconv` - [Non-trapping Float-to-int Conversions][17] 44 * `signext` - [Sign-extension operators][18] 45 46 Note that both the above features are included [working draft][19] of 47 WebAssembly Core Specification 2.0. 48 49 ## Constraints 50 51 Please read our overview of WebAssembly and 52 [constraints]({{< ref "_index.md#constraints" >}}). In short, expect 53 limitations in both language features and library choices when developing your 54 software. 55 56 `GOARCH=wasm GOOS=js` has a custom ABI which supports a subset of features in 57 the Go standard library. Notably, the host can implement time, crypto, file 58 system and HTTP client functions. Even where implemented, certain operations 59 will have no effect for reasons like ignoring HTTP request properties or fake 60 values returned (such as the pid). When not supported, many functions return 61 `syscall.ENOSYS` errors, or the string form: "not implemented on js". 62 63 Here are the more notable parts of Go which will not work when compiled via 64 `GOARCH=wasm GOOS=js`, resulting in `syscall.ENOSYS` errors: 65 * Raw network access. e.g. `net.Bind` 66 * File descriptor control (`fnctl`). e.g. `syscall.Pipe` 67 * Arbitrary syscalls. Ex `syscall.Syscall` 68 * Process control. e.g. `syscall.Kill` 69 * Kernel parameters. e.g. `syscall.Sysctl` 70 * Timezone-specific clock readings. e.g. `syscall.Gettimeofday` 71 72 ## Memory 73 74 Memory layout begins with the "zero page" of size `runtime.minLegalPointer` 75 (4KB) which matches the `ssa.minZeroPage` in the compiler. It is then followed 76 by 8KB reserved for args and environment variables. This means the data section 77 begins at [ld.wasmMinDataAddr][12], offset 12288. 78 79 ## System Calls 80 81 Please read our overview of WebAssembly and 82 [System Calls]({{< ref "_index.md#system-calls" >}}). In short, WebAssembly is 83 a stack-based virtual machine specification, so operates at a lower level than 84 an operating system. 85 86 "syscall/js.*" are host functions for features the operating system would 87 otherwise provide. These also manage the JavaScript object graph, including 88 functions to make and finalize objects, arrays and numbers (`js.Value`). 89 90 Each `js.Value` has a `js.ref`, which is either a numeric literal or an object 91 reference depending on its 64-bit bit pattern. When an object, the first 31 92 bits are its identifier. 93 94 There are several pre-defined values with constant `js.ref` patterns. These are 95 either constants, globals or otherwise needed in initializers. 96 97 For example, the "global" value includes properties like "fs" and "process" 98 which implement [system calls][7] needed for functions like `os.Getuid`. 99 100 Notably, not all system calls are implemented as some are stubbed by the 101 compiler to return zero values or `syscall.ENOSYS`. This means not all Go code 102 compiled to wasm will operate. For example, you cannot launch processes. 103 104 Details beyond this are best looking at the source code of [js.go][5], or its 105 unit tests. 106 107 ## Concurrency 108 109 Please read our overview of WebAssembly and 110 [concurrency]({{< ref "_index.md#concurrency" >}}). In short, the current 111 WebAssembly specification does not support parallel processing. 112 113 Some internal code may seem strange knowing this. For example, Go's [function 114 wrapper][9] used for `GOOS=js` is implemented using locks. Seeing this, you may 115 feel the host side of this code (`_makeFuncWrapper`) should lock its ID 116 namespace for parallel use as well. 117 118 Digging deeper, you'll notice the [atomics][10] defined by `GOARCH=wasm` are 119 not actually implemented with locks, rather it is awaiting the ["Threads" 120 proposal][11]. 121 122 In summary, while goroutines are supported in `GOARCH=wasm GOOS=js`, they won't 123 be able to run in parallel until the WebAssembly Specification includes atomics 124 and Go's compiler is updated to use them. 125 126 ## Error handling 127 128 There are several `js.Value` used to implement `GOARCH=wasm GOOS=js` including 129 the global, file system, HTTP round tripping, processes, etc. All of these have 130 functions that may return an error on `js.Value.Call`. 131 132 However, `js.Value.Call` does not return an error result. Internally, this 133 dispatches to the wasm imported function `valueCall`, and interprets its two 134 results: the real result and a boolean, represented by an integer. 135 136 When false, `js.Value.Call` panics with a `js.Error` constructed from the first 137 result. This result must be an object with one of the below properties: 138 139 * JavaScript (GOOS=js): the string property "message" can be anything. 140 * Syscall error (GOARCH=wasm): the string property "code" is constrained. 141 * The code must be like "EIO" in [errnoByCode][13] to avoid a panic. 142 143 Details beyond this are best looking at the source code of [js.go][5], or its 144 unit tests. 145 146 ## Identifying wasm compiled by Go 147 148 If you have a `%.wasm` file compiled by Go (via [asm.go][2]), it has a custom 149 section named "go.buildid". 150 151 You can verify this with wasm-objdump, a part of [wabt][3]: 152 ``` 153 $ wasm-objdump --section=go.buildid -x my.wasm 154 155 example3.wasm: file format wasm 0x1 156 157 Section Details: 158 159 Custom: 160 - name: "go.buildid" 161 ``` 162 163 ## Module Exports 164 165 Until [wasmexport][4] is implemented, the [compiled][2] WebAssembly exports are 166 always the same: 167 168 * "mem" - (memory 265) 265 = data section plus 16MB 169 * "run" - (func (param $argc i32) (param $argv i32)) the entrypoint 170 * "resume" - (func) continues work after a timer delay 171 * "getsp" - (func (result i32)) returns the stack pointer 172 173 ## Module Imports 174 175 Go's [compiles][3] all WebAssembly imports in the module "go", and only 176 functions are imported. 177 178 Except for the "debug" function, all function names are prefixed by their go 179 package. Here are the defaults: 180 181 * "debug" - is always function index zero, but it has unknown use. 182 * "runtime.*" - supports system-call like functionality `GOARCH=wasm` 183 * "syscall/js.*" - supports the JavaScript model `GOOS=js` 184 185 ## PC_B calling conventions 186 187 The assembly `CallImport` instruction doesn't compile signatures to WebAssembly 188 function types, invoked by the `call` instruction. 189 190 Instead, the compiler generates the same signature for all functions: a single 191 parameter of the stack pointer, and invokes them via `call.indirect`. 192 193 Specifically, any function compiled with `CallImport` has the same function 194 type: `(func (param $sp i32))`. `$sp` is the base memory offset to read and 195 write parameters to the stack (at 8 byte strides even if the value is 32-bit). 196 197 So, implementors need to read the actual parameters from memory. Similarly, if 198 there are results, the implementation must write those to memory. 199 200 For example, `func walltime() (sec int64, nsec int32)` writes its results to 201 memory at offsets `sp+8` and `sp+16` respectively. 202 203 Note: WebAssembly compatible calling conventions has been discussed and 204 [attempted](https://go-review.googlesource.com/c/go/+/350737) in Go before. 205 206 ## Go-defined exported functions 207 208 [Several functions][6] differ in calling convention by using WebAssembly type 209 signatures instead of the single SP parameter summarized above. Functions used 210 by the host have a "wasm_export_" prefix, which is stripped. For example, 211 "wasm_export_run" is exported as "run", defined in [rt0_js_wasm.s][7] 212 213 Here is an overview of the Go-defined exported functions: 214 * "run" - Accepts "argc" and "argv" i32 params and begins the "wasm_pc_f_loop" 215 * "resume" - Nullary function that resumes execution until it needs an event. 216 * "getsp" - Returns the i32 stack pointer (SP) 217 218 ## User-defined Host Functions 219 220 Users can define their own "go" module function imports by defining a func 221 without a body in their source and a `%_wasm.s` or `%_js.s` file that uses the 222 `CallImport` instruction. 223 224 For example, given `func logString(msg string)` and the below assembly: 225 ```assembly 226 #include "textflag.h" 227 228 TEXT ·logString(SB), NOSPLIT, $0 229 CallImport 230 RET 231 ``` 232 233 If the package was `main`, the WebAssembly function name would be 234 "main.logString". If it was `util` and your `go.mod` module was 235 "github.com/user/me", the WebAssembly function name would be 236 "github.com/user/me/util.logString". 237 238 Regardless of whether the function import was built-in to Go, or defined by an 239 end user, all imports use `CallImport` conventions. Since these compile to a 240 signature unrelated to the source, more care is needed implementing the host 241 side, to ensure the proper count of parameters are read and results written to 242 the Go stack. 243 244 ## Hacking 245 246 If you run into an issue where you need to change Go's sourcecode, the first 247 thing you should do is read the [contributing guide][20], which details how to 248 confirm an issue exists and a fix would be accepted. Assuming they say yes, the 249 next step is to ensure you can build and test go. 250 251 ### Make a branch for your changes 252 253 First, clone upstream or your fork of golang/go and make a branch off `master` 254 for your work, as GitHub pull requests are against that branch. 255 256 ```bash 257 $ git clone --depth=1 https://github.com/golang/go.git 258 $ cd go 259 $ git checkout -b my-fix 260 ``` 261 262 ### Build a branch-specific `go` binary 263 264 While your change may not affect the go binary itself, there are checks inside 265 go that require version matching. Build a go binary from source to avoid these: 266 267 ```bash 268 $ cd src 269 $ GOOS=js GOARCH=wasm ./make.bash 270 Building Go cmd/dist using /usr/local/go. (go1.19 darwin/amd64) 271 Building Go toolchain1 using /usr/local/go. 272 --snip-- 273 $ cd .. 274 $ bin/go version 275 go version devel go1.19-c5da4fb7ac Fri Jul 22 20:12:19 2022 +0000 darwin/amd64 276 ``` 277 278 Tips: 279 * The above `bin/go` was built with whatever go version you had in your path! 280 * `GOARCH` here is what the resulting `go` binary can target. It isn't the 281 architecture of the current host (`GOHOSTARCH`). 282 283 ### Setup ENV variables for your branch. 284 285 To test the Go you just built, you need to have `GOROOT` set to your workspace, 286 and your PATH configured to find both `bin/go` and `misc/wasm/go_js_wasm_exec`. 287 288 ```bash 289 $ export GOROOT=$PWD 290 $ export PATH=${GOROOT}/misc/wasm:${GOROOT}/bin:$PATH 291 ``` 292 293 Tip: `go_js_wasm_exec` is used because Go doesn't embed a WebAssembly runtime 294 like wazero. In other words, go can't run the wasm it just built. Instead, 295 `go test` uses Node.js which it assumes is installed on your host! 296 297 ### Iterate until ready to submit 298 299 Now, you should be all set and can iterate similar to normal Go development. 300 The main thing to keep in mind is where files are, and remember to set 301 `GOOS=js GOARCH=wasm` when running go commands. 302 303 For example, if you fixed something in the `syscall/js` package 304 (`${GOROOT}/src/syscall/js`), test it like so: 305 ```bash 306 $ GOOS=js GOARCH=wasm go test syscall/js 307 ok syscall/js 1.093s 308 ``` 309 310 [1]: https://github.com/golang/go/blob/go1.19/misc/wasm/wasm_exec.js 311 [2]: https://github.com/golang/go/blob/go1.19/src/cmd/link/internal/wasm/asm.go 312 [3]: https://github.com/WebAssembly/wabt 313 [4]: https://github.com/golang/proposal/blob/master/design/42372-wasmexport.md 314 [5]: https://github.com/golang/go/blob/go1.19/src/syscall/js/js.go 315 [6]: https://github.com/golang/go/blob/go1.19/src/cmd/internal/obj/wasm/wasmobj.go#L794-L812 316 [7]: https://github.com/golang/go/blob/go1.19/src/runtime/rt0_js_wasm.s#L17-L21 317 [8]: https://github.com/golang/go/blob/go1.19/src/syscall/syscall_js.go#L292-L306 318 [9]: https://github.com/golang/go/blob/go1.19/src/syscall/js/func.go#L41-L44 319 [10]: https://github.com/golang/go/blob/go1.19/src/runtime/internal/atomic/atomic_wasm.go#L5-L6 320 [11]: https://github.com/WebAssembly/proposals 321 [12]: https://github.com/golang/go/blob/go1.19/src/cmd/link/internal/ld/data.go#L2457 322 [13]: https://github.com/golang/go/blob/go1.19/src/syscall/tables_js.go#L371-L494 323 [14]: https://github.com/tetratelabs/wazero/tree/main/imports/go/example 324 [15]: https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/ 325 [16]: https://github.com/golang/go/blob/go1.19/src/internal/buildcfg/cfg.go#L133-L147 326 [17]: https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/nontrapping-float-to-int-conversion/Overview.md 327 [18]: https://github.com/WebAssembly/spec/blob/wg-2.0.draft1/proposals/sign-extension-ops/Overview.md 328 [19]: https://www.w3.org/TR/2022/WD-wasm-core-2-20220419/ 329 [20]: https://github.com/golang/go/blob/go1.19/CONTRIBUTING.md