github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/link/internal/wasm/asm.go (about) 1 // Copyright 2018 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package wasm 6 7 import ( 8 "bytes" 9 "github.com/gagliardetto/golang-go/cmd/internal/objabi" 10 "github.com/gagliardetto/golang-go/cmd/link/internal/ld" 11 "github.com/gagliardetto/golang-go/cmd/link/internal/sym" 12 "io" 13 "regexp" 14 ) 15 16 const ( 17 I32 = 0x7F 18 I64 = 0x7E 19 F32 = 0x7D 20 F64 = 0x7C 21 ) 22 23 const ( 24 sectionCustom = 0 25 sectionType = 1 26 sectionImport = 2 27 sectionFunction = 3 28 sectionTable = 4 29 sectionMemory = 5 30 sectionGlobal = 6 31 sectionExport = 7 32 sectionStart = 8 33 sectionElement = 9 34 sectionCode = 10 35 sectionData = 11 36 ) 37 38 // funcValueOffset is the offset between the PC_F value of a function and the index of the function in WebAssembly 39 const funcValueOffset = 0x1000 // TODO(neelance): make function addresses play nice with heap addresses 40 41 func gentext(ctxt *ld.Link) { 42 } 43 44 type wasmFunc struct { 45 Name string 46 Type uint32 47 Code []byte 48 } 49 50 type wasmFuncType struct { 51 Params []byte 52 Results []byte 53 } 54 55 var wasmFuncTypes = map[string]*wasmFuncType{ 56 "_rt0_wasm_js": {Params: []byte{}}, // 57 "wasm_export_run": {Params: []byte{I32, I32}}, // argc, argv 58 "wasm_export_resume": {Params: []byte{}}, // 59 "wasm_export_getsp": {Results: []byte{I32}}, // sp 60 "wasm_pc_f_loop": {Params: []byte{}}, // 61 "runtime.wasmMove": {Params: []byte{I32, I32, I32}}, // dst, src, len 62 "runtime.wasmZero": {Params: []byte{I32, I32}}, // ptr, len 63 "runtime.wasmDiv": {Params: []byte{I64, I64}, Results: []byte{I64}}, // x, y -> x/y 64 "runtime.wasmTruncS": {Params: []byte{F64}, Results: []byte{I64}}, // x -> int(x) 65 "runtime.wasmTruncU": {Params: []byte{F64}, Results: []byte{I64}}, // x -> uint(x) 66 "runtime.gcWriteBarrier": {Params: []byte{I64, I64}}, // ptr, val 67 "cmpbody": {Params: []byte{I64, I64, I64, I64}, Results: []byte{I64}}, // a, alen, b, blen -> -1/0/1 68 "memeqbody": {Params: []byte{I64, I64, I64}, Results: []byte{I64}}, // a, b, len -> 0/1 69 "memcmp": {Params: []byte{I32, I32, I32}, Results: []byte{I32}}, // a, b, len -> <0/0/>0 70 "memchr": {Params: []byte{I32, I32, I32}, Results: []byte{I32}}, // s, c, len -> index 71 } 72 73 func assignAddress(ctxt *ld.Link, sect *sym.Section, n int, s *sym.Symbol, va uint64, isTramp bool) (*sym.Section, int, uint64) { 74 // WebAssembly functions do not live in the same address space as the linear memory. 75 // Instead, WebAssembly automatically assigns indices. Imported functions (section "import") 76 // have indices 0 to n. They are followed by native functions (sections "function" and "code") 77 // with indices n+1 and following. 78 // 79 // The following rules describe how wasm handles function indices and addresses: 80 // PC_F = funcValueOffset + WebAssembly function index (not including the imports) 81 // s.Value = PC = PC_F<<16 + PC_B 82 // 83 // The funcValueOffset is necessary to avoid conflicts with expectations 84 // that the Go runtime has about function addresses. 85 // The field "s.Value" corresponds to the concept of PC at runtime. 86 // However, there is no PC register, only PC_F and PC_B. PC_F denotes the function, 87 // PC_B the resume point inside of that function. The entry of the function has PC_B = 0. 88 s.Sect = sect 89 s.Value = int64(funcValueOffset+va/ld.MINFUNC) << 16 // va starts at zero 90 va += uint64(ld.MINFUNC) 91 return sect, n, va 92 } 93 94 func asmb(ctxt *ld.Link) {} // dummy 95 96 // asmb writes the final WebAssembly module binary. 97 // Spec: https://webassembly.github.io/spec/core/binary/modules.html 98 func asmb2(ctxt *ld.Link) { 99 types := []*wasmFuncType{ 100 // For normal Go functions, the single parameter is PC_B, 101 // the return value is 102 // 0 if the function returned normally or 103 // 1 if the stack needs to be unwound. 104 {Params: []byte{I32}, Results: []byte{I32}}, 105 } 106 107 // collect host imports (functions that get imported from the WebAssembly host, usually JavaScript) 108 hostImports := []*wasmFunc{ 109 { 110 Name: "debug", 111 Type: lookupType(&wasmFuncType{Params: []byte{I32}}, &types), 112 }, 113 } 114 hostImportMap := make(map[*sym.Symbol]int64) 115 for _, fn := range ctxt.Textp { 116 for _, r := range fn.R { 117 if r.Type == objabi.R_WASMIMPORT { 118 hostImportMap[r.Sym] = int64(len(hostImports)) 119 hostImports = append(hostImports, &wasmFunc{ 120 Name: r.Sym.Name, 121 Type: lookupType(&wasmFuncType{Params: []byte{I32}}, &types), 122 }) 123 } 124 } 125 } 126 127 // collect functions with WebAssembly body 128 var buildid []byte 129 fns := make([]*wasmFunc, len(ctxt.Textp)) 130 for i, fn := range ctxt.Textp { 131 wfn := new(bytes.Buffer) 132 if fn.Name == "go.buildid" { 133 writeUleb128(wfn, 0) // number of sets of locals 134 writeI32Const(wfn, 0) 135 wfn.WriteByte(0x0b) // end 136 buildid = fn.P 137 } else { 138 // Relocations have variable length, handle them here. 139 off := int32(0) 140 for _, r := range fn.R { 141 wfn.Write(fn.P[off:r.Off]) 142 off = r.Off 143 switch r.Type { 144 case objabi.R_ADDR: 145 writeSleb128(wfn, r.Sym.Value+r.Add) 146 case objabi.R_CALL: 147 writeSleb128(wfn, int64(len(hostImports))+r.Sym.Value>>16-funcValueOffset) 148 case objabi.R_WASMIMPORT: 149 writeSleb128(wfn, hostImportMap[r.Sym]) 150 default: 151 ld.Errorf(fn, "bad reloc type %d (%s)", r.Type, sym.RelocName(ctxt.Arch, r.Type)) 152 continue 153 } 154 } 155 wfn.Write(fn.P[off:]) 156 } 157 158 typ := uint32(0) 159 if sig, ok := wasmFuncTypes[fn.Name]; ok { 160 typ = lookupType(sig, &types) 161 } 162 163 name := nameRegexp.ReplaceAllString(fn.Name, "_") 164 fns[i] = &wasmFunc{Name: name, Type: typ, Code: wfn.Bytes()} 165 } 166 167 ctxt.Out.Write([]byte{0x00, 0x61, 0x73, 0x6d}) // magic 168 ctxt.Out.Write([]byte{0x01, 0x00, 0x00, 0x00}) // version 169 170 // Add any buildid early in the binary: 171 if len(buildid) != 0 { 172 writeBuildID(ctxt, buildid) 173 } 174 175 writeTypeSec(ctxt, types) 176 writeImportSec(ctxt, hostImports) 177 writeFunctionSec(ctxt, fns) 178 writeTableSec(ctxt, fns) 179 writeMemorySec(ctxt) 180 writeGlobalSec(ctxt) 181 writeExportSec(ctxt, len(hostImports)) 182 writeElementSec(ctxt, uint64(len(hostImports)), uint64(len(fns))) 183 writeCodeSec(ctxt, fns) 184 writeDataSec(ctxt) 185 writeProducerSec(ctxt) 186 if !*ld.FlagS { 187 writeNameSec(ctxt, len(hostImports), fns) 188 } 189 190 ctxt.Out.Flush() 191 } 192 193 func lookupType(sig *wasmFuncType, types *[]*wasmFuncType) uint32 { 194 for i, t := range *types { 195 if bytes.Equal(sig.Params, t.Params) && bytes.Equal(sig.Results, t.Results) { 196 return uint32(i) 197 } 198 } 199 *types = append(*types, sig) 200 return uint32(len(*types) - 1) 201 } 202 203 func writeSecHeader(ctxt *ld.Link, id uint8) int64 { 204 ctxt.Out.WriteByte(id) 205 sizeOffset := ctxt.Out.Offset() 206 ctxt.Out.Write(make([]byte, 5)) // placeholder for length 207 return sizeOffset 208 } 209 210 func writeSecSize(ctxt *ld.Link, sizeOffset int64) { 211 endOffset := ctxt.Out.Offset() 212 ctxt.Out.SeekSet(sizeOffset) 213 writeUleb128FixedLength(ctxt.Out, uint64(endOffset-sizeOffset-5), 5) 214 ctxt.Out.SeekSet(endOffset) 215 } 216 217 func writeBuildID(ctxt *ld.Link, buildid []byte) { 218 sizeOffset := writeSecHeader(ctxt, sectionCustom) 219 writeName(ctxt.Out, "go.buildid") 220 ctxt.Out.Write(buildid) 221 writeSecSize(ctxt, sizeOffset) 222 } 223 224 // writeTypeSec writes the section that declares all function types 225 // so they can be referenced by index. 226 func writeTypeSec(ctxt *ld.Link, types []*wasmFuncType) { 227 sizeOffset := writeSecHeader(ctxt, sectionType) 228 229 writeUleb128(ctxt.Out, uint64(len(types))) 230 231 for _, t := range types { 232 ctxt.Out.WriteByte(0x60) // functype 233 writeUleb128(ctxt.Out, uint64(len(t.Params))) 234 for _, v := range t.Params { 235 ctxt.Out.WriteByte(byte(v)) 236 } 237 writeUleb128(ctxt.Out, uint64(len(t.Results))) 238 for _, v := range t.Results { 239 ctxt.Out.WriteByte(byte(v)) 240 } 241 } 242 243 writeSecSize(ctxt, sizeOffset) 244 } 245 246 // writeImportSec writes the section that lists the functions that get 247 // imported from the WebAssembly host, usually JavaScript. 248 func writeImportSec(ctxt *ld.Link, hostImports []*wasmFunc) { 249 sizeOffset := writeSecHeader(ctxt, sectionImport) 250 251 writeUleb128(ctxt.Out, uint64(len(hostImports))) // number of imports 252 for _, fn := range hostImports { 253 writeName(ctxt.Out, "go") // provided by the import object in wasm_exec.js 254 writeName(ctxt.Out, fn.Name) 255 ctxt.Out.WriteByte(0x00) // func import 256 writeUleb128(ctxt.Out, uint64(fn.Type)) 257 } 258 259 writeSecSize(ctxt, sizeOffset) 260 } 261 262 // writeFunctionSec writes the section that declares the types of functions. 263 // The bodies of these functions will later be provided in the "code" section. 264 func writeFunctionSec(ctxt *ld.Link, fns []*wasmFunc) { 265 sizeOffset := writeSecHeader(ctxt, sectionFunction) 266 267 writeUleb128(ctxt.Out, uint64(len(fns))) 268 for _, fn := range fns { 269 writeUleb128(ctxt.Out, uint64(fn.Type)) 270 } 271 272 writeSecSize(ctxt, sizeOffset) 273 } 274 275 // writeTableSec writes the section that declares tables. Currently there is only a single table 276 // that is used by the CallIndirect operation to dynamically call any function. 277 // The contents of the table get initialized by the "element" section. 278 func writeTableSec(ctxt *ld.Link, fns []*wasmFunc) { 279 sizeOffset := writeSecHeader(ctxt, sectionTable) 280 281 numElements := uint64(funcValueOffset + len(fns)) 282 writeUleb128(ctxt.Out, 1) // number of tables 283 ctxt.Out.WriteByte(0x70) // type: anyfunc 284 ctxt.Out.WriteByte(0x00) // no max 285 writeUleb128(ctxt.Out, numElements) // min 286 287 writeSecSize(ctxt, sizeOffset) 288 } 289 290 // writeMemorySec writes the section that declares linear memories. Currently one linear memory is being used. 291 // Linear memory always starts at address zero. More memory can be requested with the GrowMemory instruction. 292 func writeMemorySec(ctxt *ld.Link) { 293 sizeOffset := writeSecHeader(ctxt, sectionMemory) 294 295 dataSection := ctxt.Syms.Lookup("runtime.data", 0).Sect 296 dataEnd := dataSection.Vaddr + dataSection.Length 297 var initialSize = dataEnd + 16<<20 // 16MB, enough for runtime init without growing 298 299 const wasmPageSize = 64 << 10 // 64KB 300 301 writeUleb128(ctxt.Out, 1) // number of memories 302 ctxt.Out.WriteByte(0x00) // no maximum memory size 303 writeUleb128(ctxt.Out, initialSize/wasmPageSize) // minimum (initial) memory size 304 305 writeSecSize(ctxt, sizeOffset) 306 } 307 308 // writeGlobalSec writes the section that declares global variables. 309 func writeGlobalSec(ctxt *ld.Link) { 310 sizeOffset := writeSecHeader(ctxt, sectionGlobal) 311 312 globalRegs := []byte{ 313 I32, // 0: SP 314 I64, // 1: CTXT 315 I64, // 2: g 316 I64, // 3: RET0 317 I64, // 4: RET1 318 I64, // 5: RET2 319 I64, // 6: RET3 320 I32, // 7: PAUSE 321 } 322 323 writeUleb128(ctxt.Out, uint64(len(globalRegs))) // number of globals 324 325 for _, typ := range globalRegs { 326 ctxt.Out.WriteByte(typ) 327 ctxt.Out.WriteByte(0x01) // var 328 switch typ { 329 case I32: 330 writeI32Const(ctxt.Out, 0) 331 case I64: 332 writeI64Const(ctxt.Out, 0) 333 } 334 ctxt.Out.WriteByte(0x0b) // end 335 } 336 337 writeSecSize(ctxt, sizeOffset) 338 } 339 340 // writeExportSec writes the section that declares exports. 341 // Exports can be accessed by the WebAssembly host, usually JavaScript. 342 // The wasm_export_* functions and the linear memory get exported. 343 func writeExportSec(ctxt *ld.Link, lenHostImports int) { 344 sizeOffset := writeSecHeader(ctxt, sectionExport) 345 346 writeUleb128(ctxt.Out, 4) // number of exports 347 348 for _, name := range []string{"run", "resume", "getsp"} { 349 idx := uint32(lenHostImports) + uint32(ctxt.Syms.ROLookup("wasm_export_"+name, 0).Value>>16) - funcValueOffset 350 writeName(ctxt.Out, name) // inst.exports.run/resume/getsp in wasm_exec.js 351 ctxt.Out.WriteByte(0x00) // func export 352 writeUleb128(ctxt.Out, uint64(idx)) // funcidx 353 } 354 355 writeName(ctxt.Out, "mem") // inst.exports.mem in wasm_exec.js 356 ctxt.Out.WriteByte(0x02) // mem export 357 writeUleb128(ctxt.Out, 0) // memidx 358 359 writeSecSize(ctxt, sizeOffset) 360 } 361 362 // writeElementSec writes the section that initializes the tables declared by the "table" section. 363 // The table for CallIndirect gets initialized in a very simple way so that each table index (PC_F value) 364 // maps linearly to the function index (numImports + PC_F). 365 func writeElementSec(ctxt *ld.Link, numImports, numFns uint64) { 366 sizeOffset := writeSecHeader(ctxt, sectionElement) 367 368 writeUleb128(ctxt.Out, 1) // number of element segments 369 370 writeUleb128(ctxt.Out, 0) // tableidx 371 writeI32Const(ctxt.Out, funcValueOffset) 372 ctxt.Out.WriteByte(0x0b) // end 373 374 writeUleb128(ctxt.Out, numFns) // number of entries 375 for i := uint64(0); i < numFns; i++ { 376 writeUleb128(ctxt.Out, numImports+i) 377 } 378 379 writeSecSize(ctxt, sizeOffset) 380 } 381 382 // writeElementSec writes the section that provides the function bodies for the functions 383 // declared by the "func" section. 384 func writeCodeSec(ctxt *ld.Link, fns []*wasmFunc) { 385 sizeOffset := writeSecHeader(ctxt, sectionCode) 386 387 writeUleb128(ctxt.Out, uint64(len(fns))) // number of code entries 388 for _, fn := range fns { 389 writeUleb128(ctxt.Out, uint64(len(fn.Code))) 390 ctxt.Out.Write(fn.Code) 391 } 392 393 writeSecSize(ctxt, sizeOffset) 394 } 395 396 // writeDataSec writes the section that provides data that will be used to initialize the linear memory. 397 func writeDataSec(ctxt *ld.Link) { 398 sizeOffset := writeSecHeader(ctxt, sectionData) 399 400 sections := []*sym.Section{ 401 ctxt.Syms.Lookup("runtime.rodata", 0).Sect, 402 ctxt.Syms.Lookup("runtime.typelink", 0).Sect, 403 ctxt.Syms.Lookup("runtime.itablink", 0).Sect, 404 ctxt.Syms.Lookup("runtime.symtab", 0).Sect, 405 ctxt.Syms.Lookup("runtime.pclntab", 0).Sect, 406 ctxt.Syms.Lookup("runtime.noptrdata", 0).Sect, 407 ctxt.Syms.Lookup("runtime.data", 0).Sect, 408 } 409 410 type dataSegment struct { 411 offset int32 412 data []byte 413 } 414 415 // Omit blocks of zeroes and instead emit data segments with offsets skipping the zeroes. 416 // This reduces the size of the WebAssembly binary. We use 8 bytes as an estimate for the 417 // overhead of adding a new segment (same as wasm-opt's memory-packing optimization uses). 418 const segmentOverhead = 8 419 420 // Generate at most this many segments. A higher number of segments gets rejected by some WebAssembly runtimes. 421 const maxNumSegments = 100000 422 423 var segments []*dataSegment 424 for secIndex, sec := range sections { 425 data := ld.DatblkBytes(ctxt, int64(sec.Vaddr), int64(sec.Length)) 426 offset := int32(sec.Vaddr) 427 428 // skip leading zeroes 429 for len(data) > 0 && data[0] == 0 { 430 data = data[1:] 431 offset++ 432 } 433 434 for len(data) > 0 { 435 dataLen := int32(len(data)) 436 var segmentEnd, zeroEnd int32 437 if len(segments)+(len(sections)-secIndex) == maxNumSegments { 438 segmentEnd = dataLen 439 zeroEnd = dataLen 440 } else { 441 for { 442 // look for beginning of zeroes 443 for segmentEnd < dataLen && data[segmentEnd] != 0 { 444 segmentEnd++ 445 } 446 // look for end of zeroes 447 zeroEnd = segmentEnd 448 for zeroEnd < dataLen && data[zeroEnd] == 0 { 449 zeroEnd++ 450 } 451 // emit segment if omitting zeroes reduces the output size 452 if zeroEnd-segmentEnd >= segmentOverhead || zeroEnd == dataLen { 453 break 454 } 455 segmentEnd = zeroEnd 456 } 457 } 458 459 segments = append(segments, &dataSegment{ 460 offset: offset, 461 data: data[:segmentEnd], 462 }) 463 data = data[zeroEnd:] 464 offset += zeroEnd 465 } 466 } 467 468 writeUleb128(ctxt.Out, uint64(len(segments))) // number of data entries 469 for _, seg := range segments { 470 writeUleb128(ctxt.Out, 0) // memidx 471 writeI32Const(ctxt.Out, seg.offset) 472 ctxt.Out.WriteByte(0x0b) // end 473 writeUleb128(ctxt.Out, uint64(len(seg.data))) 474 ctxt.Out.Write(seg.data) 475 } 476 477 writeSecSize(ctxt, sizeOffset) 478 } 479 480 // writeProducerSec writes an optional section that reports the source language and compiler version. 481 func writeProducerSec(ctxt *ld.Link) { 482 sizeOffset := writeSecHeader(ctxt, sectionCustom) 483 writeName(ctxt.Out, "producers") 484 485 writeUleb128(ctxt.Out, 2) // number of fields 486 487 writeName(ctxt.Out, "language") // field name 488 writeUleb128(ctxt.Out, 1) // number of values 489 writeName(ctxt.Out, "Go") // value: name 490 writeName(ctxt.Out, objabi.Version) // value: version 491 492 writeName(ctxt.Out, "processed-by") // field name 493 writeUleb128(ctxt.Out, 1) // number of values 494 writeName(ctxt.Out, "Go cmd/compile") // value: name 495 writeName(ctxt.Out, objabi.Version) // value: version 496 497 writeSecSize(ctxt, sizeOffset) 498 } 499 500 var nameRegexp = regexp.MustCompile(`[^\w\.]`) 501 502 // writeNameSec writes an optional section that assigns names to the functions declared by the "func" section. 503 // The names are only used by WebAssembly stack traces, debuggers and decompilers. 504 // TODO(neelance): add symbol table of DATA symbols 505 func writeNameSec(ctxt *ld.Link, firstFnIndex int, fns []*wasmFunc) { 506 sizeOffset := writeSecHeader(ctxt, sectionCustom) 507 writeName(ctxt.Out, "name") 508 509 sizeOffset2 := writeSecHeader(ctxt, 0x01) // function names 510 writeUleb128(ctxt.Out, uint64(len(fns))) 511 for i, fn := range fns { 512 writeUleb128(ctxt.Out, uint64(firstFnIndex+i)) 513 writeName(ctxt.Out, fn.Name) 514 } 515 writeSecSize(ctxt, sizeOffset2) 516 517 writeSecSize(ctxt, sizeOffset) 518 } 519 520 type nameWriter interface { 521 io.ByteWriter 522 io.Writer 523 } 524 525 func writeI32Const(w io.ByteWriter, v int32) { 526 w.WriteByte(0x41) // i32.const 527 writeSleb128(w, int64(v)) 528 } 529 530 func writeI64Const(w io.ByteWriter, v int64) { 531 w.WriteByte(0x42) // i64.const 532 writeSleb128(w, v) 533 } 534 535 func writeName(w nameWriter, name string) { 536 writeUleb128(w, uint64(len(name))) 537 w.Write([]byte(name)) 538 } 539 540 func writeUleb128(w io.ByteWriter, v uint64) { 541 if v < 128 { 542 w.WriteByte(uint8(v)) 543 return 544 } 545 more := true 546 for more { 547 c := uint8(v & 0x7f) 548 v >>= 7 549 more = v != 0 550 if more { 551 c |= 0x80 552 } 553 w.WriteByte(c) 554 } 555 } 556 557 func writeUleb128FixedLength(w io.ByteWriter, v uint64, length int) { 558 for i := 0; i < length; i++ { 559 c := uint8(v & 0x7f) 560 v >>= 7 561 if i < length-1 { 562 c |= 0x80 563 } 564 w.WriteByte(c) 565 } 566 if v != 0 { 567 panic("writeUleb128FixedLength: length too small") 568 } 569 } 570 571 func writeSleb128(w io.ByteWriter, v int64) { 572 more := true 573 for more { 574 c := uint8(v & 0x7f) 575 s := uint8(v & 0x40) 576 v >>= 7 577 more = !((v == 0 && s == 0) || (v == -1 && s != 0)) 578 if more { 579 c |= 0x80 580 } 581 w.WriteByte(c) 582 } 583 }