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