1
2
3
4
5 package wasm
6
7 import (
8 "bytes"
9 "cmd/internal/obj"
10 "cmd/internal/obj/wasm"
11 "cmd/internal/objabi"
12 "cmd/link/internal/ld"
13 "cmd/link/internal/loader"
14 "cmd/link/internal/sym"
15 "encoding/binary"
16 "fmt"
17 "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
45 const funcValueOffset = 0x1000
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}},
120 "wasm_export_resume": {Params: []byte{}},
121 "wasm_export_getsp": {Results: []byte{I32}},
122 "wasm_pc_f_loop": {Params: []byte{}},
123 "runtime.wasmDiv": {Params: []byte{I64, I64}, Results: []byte{I64}},
124 "runtime.wasmTruncS": {Params: []byte{F64}, Results: []byte{I64}},
125 "runtime.wasmTruncU": {Params: []byte{F64}, Results: []byte{I64}},
126 "gcWriteBarrier": {Params: []byte{I64}, Results: []byte{I64}},
127 "runtime.gcWriteBarrier1": {Results: []byte{I64}},
128 "runtime.gcWriteBarrier2": {Results: []byte{I64}},
129 "runtime.gcWriteBarrier3": {Results: []byte{I64}},
130 "runtime.gcWriteBarrier4": {Results: []byte{I64}},
131 "runtime.gcWriteBarrier5": {Results: []byte{I64}},
132 "runtime.gcWriteBarrier6": {Results: []byte{I64}},
133 "runtime.gcWriteBarrier7": {Results: []byte{I64}},
134 "runtime.gcWriteBarrier8": {Results: []byte{I64}},
135 "cmpbody": {Params: []byte{I64, I64, I64, I64}, Results: []byte{I64}},
136 "memeqbody": {Params: []byte{I64, I64, I64}, Results: []byte{I64}},
137 "memcmp": {Params: []byte{I32, I32, I32}, Results: []byte{I32}},
138 "memchr": {Params: []byte{I32, I32, I32}, Results: []byte{I32}},
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156 ldr.SetSymSect(s, sect)
157 ldr.SetSymValue(s, int64(funcValueOffset+va/ld.MINFUNC)<<16)
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
188
189 func asmb2(ctxt *ld.Link, ldr *loader.Loader) {
190 types := []*wasmFuncType{
191
192
193
194
195 {Params: []byte{I32}, Results: []byte{I32}},
196 }
197
198
199
200
201
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
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)
234 writeI32Const(wfn, 0)
235 wfn.WriteByte(0x0b)
236 buildid = ldr.Data(fn)
237 } else {
238
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
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})
275 ctxt.Out.Write([]byte{0x01, 0x00, 0x00, 0x00})
276
277
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))
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
330
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)
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
352
353 func writeImportSec(ctxt *ld.Link, hostImports []*wasmFunc) {
354 sizeOffset := writeSecHeader(ctxt, sectionImport)
355
356 writeUleb128(ctxt.Out, uint64(len(hostImports)))
357 for _, fn := range hostImports {
358 if fn.Module != "" {
359 writeName(ctxt.Out, fn.Module)
360 } else {
361 writeName(ctxt.Out, wasm.GojsModule)
362 }
363 writeName(ctxt.Out, fn.Name)
364 ctxt.Out.WriteByte(0x00)
365 writeUleb128(ctxt.Out, uint64(fn.Type))
366 }
367
368 writeSecSize(ctxt, sizeOffset)
369 }
370
371
372
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
385
386
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)
392 ctxt.Out.WriteByte(0x70)
393 ctxt.Out.WriteByte(0x00)
394 writeUleb128(ctxt.Out, numElements)
395
396 writeSecSize(ctxt, sizeOffset)
397 }
398
399
400
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
407
408 const wasmPageSize = 64 << 10
409
410 writeUleb128(ctxt.Out, 1)
411 ctxt.Out.WriteByte(0x00)
412 writeUleb128(ctxt.Out, initialSize/wasmPageSize)
413
414 writeSecSize(ctxt, sizeOffset)
415 }
416
417
418 func writeGlobalSec(ctxt *ld.Link) {
419 sizeOffset := writeSecHeader(ctxt, sectionGlobal)
420
421 globalRegs := []byte{
422 I32,
423 I64,
424 I64,
425 I64,
426 I64,
427 I64,
428 I64,
429 I32,
430 }
431
432 writeUleb128(ctxt.Out, uint64(len(globalRegs)))
433
434 for _, typ := range globalRegs {
435 ctxt.Out.WriteByte(typ)
436 ctxt.Out.WriteByte(0x01)
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)
444 }
445
446 writeSecSize(ctxt, sizeOffset)
447 }
448
449
450
451
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)
458 s := ldr.Lookup("_rt0_wasm_wasip1", 0)
459 idx := uint32(lenHostImports) + uint32(ldr.SymValue(s)>>16) - funcValueOffset
460 writeName(ctxt.Out, "_start")
461 ctxt.Out.WriteByte(0x00)
462 writeUleb128(ctxt.Out, uint64(idx))
463 writeName(ctxt.Out, "memory")
464 ctxt.Out.WriteByte(0x02)
465 writeUleb128(ctxt.Out, 0)
466 case "js":
467 writeUleb128(ctxt.Out, 4)
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)
472 ctxt.Out.WriteByte(0x00)
473 writeUleb128(ctxt.Out, uint64(idx))
474 }
475 writeName(ctxt.Out, "mem")
476 ctxt.Out.WriteByte(0x02)
477 writeUleb128(ctxt.Out, 0)
478 default:
479 ld.Exitf("internal error: writeExportSec: unrecognized GOOS %s", buildcfg.GOOS)
480 }
481
482 writeSecSize(ctxt, sizeOffset)
483 }
484
485
486
487
488 func writeElementSec(ctxt *ld.Link, numImports, numFns uint64) {
489 sizeOffset := writeSecHeader(ctxt, sectionElement)
490
491 writeUleb128(ctxt.Out, 1)
492
493 writeUleb128(ctxt.Out, 0)
494 writeI32Const(ctxt.Out, funcValueOffset)
495 ctxt.Out.WriteByte(0x0b)
496
497 writeUleb128(ctxt.Out, numFns)
498 for i := uint64(0); i < numFns; i++ {
499 writeUleb128(ctxt.Out, numImports+i)
500 }
501
502 writeSecSize(ctxt, sizeOffset)
503 }
504
505
506
507 func writeCodeSec(ctxt *ld.Link, fns []*wasmFunc) {
508 sizeOffset := writeSecHeader(ctxt, sectionCode)
509
510 writeUleb128(ctxt.Out, uint64(len(fns)))
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
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
529
530
531 const segmentOverhead = 8
532
533
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
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
556 for segmentEnd < dataLen && data[segmentEnd] != 0 {
557 segmentEnd++
558 }
559
560 zeroEnd = segmentEnd
561 for zeroEnd < dataLen && data[zeroEnd] == 0 {
562 zeroEnd++
563 }
564
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)))
582 for _, seg := range segments {
583 writeUleb128(ctxt.Out, 0)
584 writeI32Const(ctxt.Out, seg.offset)
585 ctxt.Out.WriteByte(0x0b)
586 writeUleb128(ctxt.Out, uint64(len(seg.data)))
587 ctxt.Out.Write(seg.data)
588 }
589
590 writeSecSize(ctxt, sizeOffset)
591 }
592
593
594 func writeProducerSec(ctxt *ld.Link) {
595 sizeOffset := writeSecHeader(ctxt, sectionCustom)
596 writeName(ctxt.Out, "producers")
597
598 writeUleb128(ctxt.Out, 2)
599
600 writeName(ctxt.Out, "language")
601 writeUleb128(ctxt.Out, 1)
602 writeName(ctxt.Out, "Go")
603 writeName(ctxt.Out, buildcfg.Version)
604
605 writeName(ctxt.Out, "processed-by")
606 writeUleb128(ctxt.Out, 1)
607 writeName(ctxt.Out, "Go cmd/compile")
608 writeName(ctxt.Out, buildcfg.Version)
609
610 writeSecSize(ctxt, sizeOffset)
611 }
612
613 var nameRegexp = regexp.MustCompile(`[^\w.]`)
614
615
616
617
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)
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)
640 writeSleb128(w, int64(v))
641 }
642
643 func writeI64Const(w io.ByteWriter, v int64) {
644 w.WriteByte(0x42)
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 }
716
View as plain text