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