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/abi"
18 "internal/buildcfg"
19 "io"
20 "regexp"
21 )
22
23 const (
24 I32 = 0x7F
25 I64 = 0x7E
26 F32 = 0x7D
27 F64 = 0x7C
28 )
29
30 const (
31 sectionCustom = 0
32 sectionType = 1
33 sectionImport = 2
34 sectionFunction = 3
35 sectionTable = 4
36 sectionMemory = 5
37 sectionGlobal = 6
38 sectionExport = 7
39 sectionStart = 8
40 sectionElement = 9
41 sectionCode = 10
42 sectionData = 11
43 )
44
45
46 const funcValueOffset = 0x1000
47
48 func gentext(ctxt *ld.Link, ldr *loader.Loader) {
49 }
50
51 type wasmFunc struct {
52 Module string
53 Name string
54 Type uint32
55 Code []byte
56 }
57
58 type wasmFuncType struct {
59 Params []byte
60 Results []byte
61 }
62
63 func readWasmImport(ldr *loader.Loader, s loader.Sym) obj.WasmImport {
64 reportError := func(err error) { panic(fmt.Sprintf("failed to read WASM import in sym %v: %v", s, err)) }
65
66 data := ldr.Data(s)
67
68 readUint32 := func() (v uint32) {
69 v = binary.LittleEndian.Uint32(data)
70 data = data[4:]
71 return
72 }
73
74 readUint64 := func() (v uint64) {
75 v = binary.LittleEndian.Uint64(data)
76 data = data[8:]
77 return
78 }
79
80 readByte := func() byte {
81 if len(data) == 0 {
82 reportError(io.EOF)
83 }
84
85 b := data[0]
86 data = data[1:]
87 return b
88 }
89
90 readString := func() string {
91 n := readUint32()
92
93 s := string(data[:n])
94
95 data = data[n:]
96
97 return s
98 }
99
100 var wi obj.WasmImport
101 wi.Module = readString()
102 wi.Name = readString()
103 wi.Params = make([]obj.WasmField, readUint32())
104 for i := range wi.Params {
105 wi.Params[i].Type = obj.WasmFieldType(readByte())
106 wi.Params[i].Offset = int64(readUint64())
107 }
108 wi.Results = make([]obj.WasmField, readUint32())
109 for i := range wi.Results {
110 wi.Results[i].Type = obj.WasmFieldType(readByte())
111 wi.Results[i].Offset = int64(readUint64())
112 }
113 return wi
114 }
115
116 var wasmFuncTypes = map[string]*wasmFuncType{
117 "_rt0_wasm_js": {Params: []byte{}},
118 "_rt0_wasm_wasip1": {Params: []byte{}},
119 "wasm_export__start": {},
120 "wasm_export_run": {Params: []byte{I32, I32}},
121 "wasm_export_resume": {Params: []byte{}},
122 "wasm_export_getsp": {Results: []byte{I32}},
123 "wasm_pc_f_loop": {Params: []byte{}},
124 "runtime.wasmDiv": {Params: []byte{I64, I64}, Results: []byte{I64}},
125 "runtime.wasmTruncS": {Params: []byte{F64}, Results: []byte{I64}},
126 "runtime.wasmTruncU": {Params: []byte{F64}, Results: []byte{I64}},
127 "gcWriteBarrier": {Params: []byte{I64}, Results: []byte{I64}},
128 "runtime.gcWriteBarrier1": {Results: []byte{I64}},
129 "runtime.gcWriteBarrier2": {Results: []byte{I64}},
130 "runtime.gcWriteBarrier3": {Results: []byte{I64}},
131 "runtime.gcWriteBarrier4": {Results: []byte{I64}},
132 "runtime.gcWriteBarrier5": {Results: []byte{I64}},
133 "runtime.gcWriteBarrier6": {Results: []byte{I64}},
134 "runtime.gcWriteBarrier7": {Results: []byte{I64}},
135 "runtime.gcWriteBarrier8": {Results: []byte{I64}},
136 "cmpbody": {Params: []byte{I64, I64, I64, I64}, Results: []byte{I64}},
137 "memeqbody": {Params: []byte{I64, I64, I64}, Results: []byte{I64}},
138 "memcmp": {Params: []byte{I32, I32, I32}, Results: []byte{I32}},
139 "memchr": {Params: []byte{I32, I32, I32}, Results: []byte{I32}},
140 }
141
142 func assignAddress(ldr *loader.Loader, sect *sym.Section, n int, s loader.Sym, va uint64, isTramp bool) (*sym.Section, int, uint64) {
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157 ldr.SetSymSect(s, sect)
158 ldr.SetSymValue(s, int64(funcValueOffset+va/abi.MINFUNC)<<16)
159 va += uint64(abi.MINFUNC)
160 return sect, n, va
161 }
162
163 type wasmDataSect struct {
164 sect *sym.Section
165 data []byte
166 }
167
168 var dataSects []wasmDataSect
169
170 func asmb(ctxt *ld.Link, ldr *loader.Loader) {
171 sections := []*sym.Section{
172 ldr.SymSect(ldr.Lookup("runtime.rodata", 0)),
173 ldr.SymSect(ldr.Lookup("runtime.typelink", 0)),
174 ldr.SymSect(ldr.Lookup("runtime.itablink", 0)),
175 ldr.SymSect(ldr.Lookup("runtime.symtab", 0)),
176 ldr.SymSect(ldr.Lookup("runtime.pclntab", 0)),
177 ldr.SymSect(ldr.Lookup("runtime.noptrdata", 0)),
178 ldr.SymSect(ldr.Lookup("runtime.data", 0)),
179 }
180
181 dataSects = make([]wasmDataSect, len(sections))
182 for i, sect := range sections {
183 data := ld.DatblkBytes(ctxt, int64(sect.Vaddr), int64(sect.Length))
184 dataSects[i] = wasmDataSect{sect, data}
185 }
186 }
187
188
189
190 func asmb2(ctxt *ld.Link, ldr *loader.Loader) {
191 types := []*wasmFuncType{
192
193
194
195
196 {Params: []byte{I32}, Results: []byte{I32}},
197 }
198
199
200
201
202
203 var hostImports []*wasmFunc
204 hostImportMap := make(map[loader.Sym]int64)
205 for _, fn := range ctxt.Textp {
206 relocs := ldr.Relocs(fn)
207 for ri := 0; ri < relocs.Count(); ri++ {
208 r := relocs.At(ri)
209 if r.Type() == objabi.R_WASMIMPORT {
210 if lsym, ok := ldr.WasmImportSym(fn); ok {
211 wi := readWasmImport(ldr, lsym)
212 hostImportMap[fn] = int64(len(hostImports))
213 hostImports = append(hostImports, &wasmFunc{
214 Module: wi.Module,
215 Name: wi.Name,
216 Type: lookupType(&wasmFuncType{
217 Params: fieldsToTypes(wi.Params),
218 Results: fieldsToTypes(wi.Results),
219 }, &types),
220 })
221 } else {
222 panic(fmt.Sprintf("missing wasm symbol for %s", ldr.SymName(r.Sym())))
223 }
224 }
225 }
226 }
227
228
229 var buildid []byte
230 fns := make([]*wasmFunc, len(ctxt.Textp))
231 for i, fn := range ctxt.Textp {
232 wfn := new(bytes.Buffer)
233 if ldr.SymName(fn) == "go:buildid" {
234 writeUleb128(wfn, 0)
235 writeI32Const(wfn, 0)
236 wfn.WriteByte(0x0b)
237 buildid = ldr.Data(fn)
238 } else {
239
240 relocs := ldr.Relocs(fn)
241 P := ldr.Data(fn)
242 off := int32(0)
243 for ri := 0; ri < relocs.Count(); ri++ {
244 r := relocs.At(ri)
245 if r.Siz() == 0 {
246 continue
247 }
248 wfn.Write(P[off:r.Off()])
249 off = r.Off()
250 rs := r.Sym()
251 switch r.Type() {
252 case objabi.R_ADDR:
253 writeSleb128(wfn, ldr.SymValue(rs)+r.Add())
254 case objabi.R_CALL:
255 writeSleb128(wfn, int64(len(hostImports))+ldr.SymValue(rs)>>16-funcValueOffset)
256 case objabi.R_WASMIMPORT:
257 writeSleb128(wfn, hostImportMap[rs])
258 default:
259 ldr.Errorf(fn, "bad reloc type %d (%s)", r.Type(), sym.RelocName(ctxt.Arch, r.Type()))
260 continue
261 }
262 }
263 wfn.Write(P[off:])
264 }
265
266 typ := uint32(0)
267 if sig, ok := wasmFuncTypes[ldr.SymName(fn)]; ok {
268 typ = lookupType(sig, &types)
269 }
270
271 name := nameRegexp.ReplaceAllString(ldr.SymName(fn), "_")
272 fns[i] = &wasmFunc{Name: name, Type: typ, Code: wfn.Bytes()}
273 }
274
275 ctxt.Out.Write([]byte{0x00, 0x61, 0x73, 0x6d})
276 ctxt.Out.Write([]byte{0x01, 0x00, 0x00, 0x00})
277
278
279 if len(buildid) != 0 {
280 writeBuildID(ctxt, buildid)
281 }
282
283 writeTypeSec(ctxt, types)
284 writeImportSec(ctxt, hostImports)
285 writeFunctionSec(ctxt, fns)
286 writeTableSec(ctxt, fns)
287 writeMemorySec(ctxt, ldr)
288 writeGlobalSec(ctxt)
289 writeExportSec(ctxt, ldr, len(hostImports))
290 writeElementSec(ctxt, uint64(len(hostImports)), uint64(len(fns)))
291 writeCodeSec(ctxt, fns)
292 writeDataSec(ctxt)
293 writeProducerSec(ctxt)
294 if !*ld.FlagS {
295 writeNameSec(ctxt, len(hostImports), fns)
296 }
297 }
298
299 func lookupType(sig *wasmFuncType, types *[]*wasmFuncType) uint32 {
300 for i, t := range *types {
301 if bytes.Equal(sig.Params, t.Params) && bytes.Equal(sig.Results, t.Results) {
302 return uint32(i)
303 }
304 }
305 *types = append(*types, sig)
306 return uint32(len(*types) - 1)
307 }
308
309 func writeSecHeader(ctxt *ld.Link, id uint8) int64 {
310 ctxt.Out.WriteByte(id)
311 sizeOffset := ctxt.Out.Offset()
312 ctxt.Out.Write(make([]byte, 5))
313 return sizeOffset
314 }
315
316 func writeSecSize(ctxt *ld.Link, sizeOffset int64) {
317 endOffset := ctxt.Out.Offset()
318 ctxt.Out.SeekSet(sizeOffset)
319 writeUleb128FixedLength(ctxt.Out, uint64(endOffset-sizeOffset-5), 5)
320 ctxt.Out.SeekSet(endOffset)
321 }
322
323 func writeBuildID(ctxt *ld.Link, buildid []byte) {
324 sizeOffset := writeSecHeader(ctxt, sectionCustom)
325 writeName(ctxt.Out, "go:buildid")
326 ctxt.Out.Write(buildid)
327 writeSecSize(ctxt, sizeOffset)
328 }
329
330
331
332 func writeTypeSec(ctxt *ld.Link, types []*wasmFuncType) {
333 sizeOffset := writeSecHeader(ctxt, sectionType)
334
335 writeUleb128(ctxt.Out, uint64(len(types)))
336
337 for _, t := range types {
338 ctxt.Out.WriteByte(0x60)
339 writeUleb128(ctxt.Out, uint64(len(t.Params)))
340 for _, v := range t.Params {
341 ctxt.Out.WriteByte(byte(v))
342 }
343 writeUleb128(ctxt.Out, uint64(len(t.Results)))
344 for _, v := range t.Results {
345 ctxt.Out.WriteByte(byte(v))
346 }
347 }
348
349 writeSecSize(ctxt, sizeOffset)
350 }
351
352
353
354 func writeImportSec(ctxt *ld.Link, hostImports []*wasmFunc) {
355 sizeOffset := writeSecHeader(ctxt, sectionImport)
356
357 writeUleb128(ctxt.Out, uint64(len(hostImports)))
358 for _, fn := range hostImports {
359 if fn.Module != "" {
360 writeName(ctxt.Out, fn.Module)
361 } else {
362 writeName(ctxt.Out, wasm.GojsModule)
363 }
364 writeName(ctxt.Out, fn.Name)
365 ctxt.Out.WriteByte(0x00)
366 writeUleb128(ctxt.Out, uint64(fn.Type))
367 }
368
369 writeSecSize(ctxt, sizeOffset)
370 }
371
372
373
374 func writeFunctionSec(ctxt *ld.Link, fns []*wasmFunc) {
375 sizeOffset := writeSecHeader(ctxt, sectionFunction)
376
377 writeUleb128(ctxt.Out, uint64(len(fns)))
378 for _, fn := range fns {
379 writeUleb128(ctxt.Out, uint64(fn.Type))
380 }
381
382 writeSecSize(ctxt, sizeOffset)
383 }
384
385
386
387
388 func writeTableSec(ctxt *ld.Link, fns []*wasmFunc) {
389 sizeOffset := writeSecHeader(ctxt, sectionTable)
390
391 numElements := uint64(funcValueOffset + len(fns))
392 writeUleb128(ctxt.Out, 1)
393 ctxt.Out.WriteByte(0x70)
394 ctxt.Out.WriteByte(0x00)
395 writeUleb128(ctxt.Out, numElements)
396
397 writeSecSize(ctxt, sizeOffset)
398 }
399
400
401
402 func writeMemorySec(ctxt *ld.Link, ldr *loader.Loader) {
403 sizeOffset := writeSecHeader(ctxt, sectionMemory)
404
405 dataSection := ldr.SymSect(ldr.Lookup("runtime.data", 0))
406 dataEnd := dataSection.Vaddr + dataSection.Length
407 var initialSize = dataEnd + 16<<20
408
409 const wasmPageSize = 64 << 10
410
411 writeUleb128(ctxt.Out, 1)
412 ctxt.Out.WriteByte(0x00)
413 writeUleb128(ctxt.Out, initialSize/wasmPageSize)
414
415 writeSecSize(ctxt, sizeOffset)
416 }
417
418
419 func writeGlobalSec(ctxt *ld.Link) {
420 sizeOffset := writeSecHeader(ctxt, sectionGlobal)
421
422 globalRegs := []byte{
423 I32,
424 I64,
425 I64,
426 I64,
427 I64,
428 I64,
429 I64,
430 I32,
431 }
432
433 writeUleb128(ctxt.Out, uint64(len(globalRegs)))
434
435 for _, typ := range globalRegs {
436 ctxt.Out.WriteByte(typ)
437 ctxt.Out.WriteByte(0x01)
438 switch typ {
439 case I32:
440 writeI32Const(ctxt.Out, 0)
441 case I64:
442 writeI64Const(ctxt.Out, 0)
443 }
444 ctxt.Out.WriteByte(0x0b)
445 }
446
447 writeSecSize(ctxt, sizeOffset)
448 }
449
450
451
452
453 func writeExportSec(ctxt *ld.Link, ldr *loader.Loader, lenHostImports int) {
454 sizeOffset := writeSecHeader(ctxt, sectionExport)
455
456 switch buildcfg.GOOS {
457 case "wasip1":
458 writeUleb128(ctxt.Out, 2)
459 s := ldr.Lookup("_rt0_wasm_wasip1", 0)
460 idx := uint32(lenHostImports) + uint32(ldr.SymValue(s)>>16) - funcValueOffset
461 writeName(ctxt.Out, "_start")
462 ctxt.Out.WriteByte(0x00)
463 writeUleb128(ctxt.Out, uint64(idx))
464 writeName(ctxt.Out, "memory")
465 ctxt.Out.WriteByte(0x02)
466 writeUleb128(ctxt.Out, 0)
467 case "js":
468 writeUleb128(ctxt.Out, 4)
469 for _, name := range []string{"run", "resume", "getsp"} {
470 s := ldr.Lookup("wasm_export_"+name, 0)
471 idx := uint32(lenHostImports) + uint32(ldr.SymValue(s)>>16) - funcValueOffset
472 writeName(ctxt.Out, name)
473 ctxt.Out.WriteByte(0x00)
474 writeUleb128(ctxt.Out, uint64(idx))
475 }
476 writeName(ctxt.Out, "mem")
477 ctxt.Out.WriteByte(0x02)
478 writeUleb128(ctxt.Out, 0)
479 default:
480 ld.Exitf("internal error: writeExportSec: unrecognized GOOS %s", buildcfg.GOOS)
481 }
482
483 writeSecSize(ctxt, sizeOffset)
484 }
485
486
487
488
489 func writeElementSec(ctxt *ld.Link, numImports, numFns uint64) {
490 sizeOffset := writeSecHeader(ctxt, sectionElement)
491
492 writeUleb128(ctxt.Out, 1)
493
494 writeUleb128(ctxt.Out, 0)
495 writeI32Const(ctxt.Out, funcValueOffset)
496 ctxt.Out.WriteByte(0x0b)
497
498 writeUleb128(ctxt.Out, numFns)
499 for i := uint64(0); i < numFns; i++ {
500 writeUleb128(ctxt.Out, numImports+i)
501 }
502
503 writeSecSize(ctxt, sizeOffset)
504 }
505
506
507
508 func writeCodeSec(ctxt *ld.Link, fns []*wasmFunc) {
509 sizeOffset := writeSecHeader(ctxt, sectionCode)
510
511 writeUleb128(ctxt.Out, uint64(len(fns)))
512 for _, fn := range fns {
513 writeUleb128(ctxt.Out, uint64(len(fn.Code)))
514 ctxt.Out.Write(fn.Code)
515 }
516
517 writeSecSize(ctxt, sizeOffset)
518 }
519
520
521 func writeDataSec(ctxt *ld.Link) {
522 sizeOffset := writeSecHeader(ctxt, sectionData)
523
524 type dataSegment struct {
525 offset int32
526 data []byte
527 }
528
529
530
531
532 const segmentOverhead = 8
533
534
535 const maxNumSegments = 100000
536
537 var segments []*dataSegment
538 for secIndex, ds := range dataSects {
539 data := ds.data
540 offset := int32(ds.sect.Vaddr)
541
542
543 for len(data) > 0 && data[0] == 0 {
544 data = data[1:]
545 offset++
546 }
547
548 for len(data) > 0 {
549 dataLen := int32(len(data))
550 var segmentEnd, zeroEnd int32
551 if len(segments)+(len(dataSects)-secIndex) == maxNumSegments {
552 segmentEnd = dataLen
553 zeroEnd = dataLen
554 } else {
555 for {
556
557 for segmentEnd < dataLen && data[segmentEnd] != 0 {
558 segmentEnd++
559 }
560
561 zeroEnd = segmentEnd
562 for zeroEnd < dataLen && data[zeroEnd] == 0 {
563 zeroEnd++
564 }
565
566 if zeroEnd-segmentEnd >= segmentOverhead || zeroEnd == dataLen {
567 break
568 }
569 segmentEnd = zeroEnd
570 }
571 }
572
573 segments = append(segments, &dataSegment{
574 offset: offset,
575 data: data[:segmentEnd],
576 })
577 data = data[zeroEnd:]
578 offset += zeroEnd
579 }
580 }
581
582 writeUleb128(ctxt.Out, uint64(len(segments)))
583 for _, seg := range segments {
584 writeUleb128(ctxt.Out, 0)
585 writeI32Const(ctxt.Out, seg.offset)
586 ctxt.Out.WriteByte(0x0b)
587 writeUleb128(ctxt.Out, uint64(len(seg.data)))
588 ctxt.Out.Write(seg.data)
589 }
590
591 writeSecSize(ctxt, sizeOffset)
592 }
593
594
595 func writeProducerSec(ctxt *ld.Link) {
596 sizeOffset := writeSecHeader(ctxt, sectionCustom)
597 writeName(ctxt.Out, "producers")
598
599 writeUleb128(ctxt.Out, 2)
600
601 writeName(ctxt.Out, "language")
602 writeUleb128(ctxt.Out, 1)
603 writeName(ctxt.Out, "Go")
604 writeName(ctxt.Out, buildcfg.Version)
605
606 writeName(ctxt.Out, "processed-by")
607 writeUleb128(ctxt.Out, 1)
608 writeName(ctxt.Out, "Go cmd/compile")
609 writeName(ctxt.Out, buildcfg.Version)
610
611 writeSecSize(ctxt, sizeOffset)
612 }
613
614 var nameRegexp = regexp.MustCompile(`[^\w.]`)
615
616
617
618
619 func writeNameSec(ctxt *ld.Link, firstFnIndex int, fns []*wasmFunc) {
620 sizeOffset := writeSecHeader(ctxt, sectionCustom)
621 writeName(ctxt.Out, "name")
622
623 sizeOffset2 := writeSecHeader(ctxt, 0x01)
624 writeUleb128(ctxt.Out, uint64(len(fns)))
625 for i, fn := range fns {
626 writeUleb128(ctxt.Out, uint64(firstFnIndex+i))
627 writeName(ctxt.Out, fn.Name)
628 }
629 writeSecSize(ctxt, sizeOffset2)
630
631 writeSecSize(ctxt, sizeOffset)
632 }
633
634 type nameWriter interface {
635 io.ByteWriter
636 io.Writer
637 }
638
639 func writeI32Const(w io.ByteWriter, v int32) {
640 w.WriteByte(0x41)
641 writeSleb128(w, int64(v))
642 }
643
644 func writeI64Const(w io.ByteWriter, v int64) {
645 w.WriteByte(0x42)
646 writeSleb128(w, v)
647 }
648
649 func writeName(w nameWriter, name string) {
650 writeUleb128(w, uint64(len(name)))
651 w.Write([]byte(name))
652 }
653
654 func writeUleb128(w io.ByteWriter, v uint64) {
655 if v < 128 {
656 w.WriteByte(uint8(v))
657 return
658 }
659 more := true
660 for more {
661 c := uint8(v & 0x7f)
662 v >>= 7
663 more = v != 0
664 if more {
665 c |= 0x80
666 }
667 w.WriteByte(c)
668 }
669 }
670
671 func writeUleb128FixedLength(w io.ByteWriter, v uint64, length int) {
672 for i := 0; i < length; i++ {
673 c := uint8(v & 0x7f)
674 v >>= 7
675 if i < length-1 {
676 c |= 0x80
677 }
678 w.WriteByte(c)
679 }
680 if v != 0 {
681 panic("writeUleb128FixedLength: length too small")
682 }
683 }
684
685 func writeSleb128(w io.ByteWriter, v int64) {
686 more := true
687 for more {
688 c := uint8(v & 0x7f)
689 s := uint8(v & 0x40)
690 v >>= 7
691 more = !((v == 0 && s == 0) || (v == -1 && s != 0))
692 if more {
693 c |= 0x80
694 }
695 w.WriteByte(c)
696 }
697 }
698
699 func fieldsToTypes(fields []obj.WasmField) []byte {
700 b := make([]byte, len(fields))
701 for i, f := range fields {
702 switch f.Type {
703 case obj.WasmI32, obj.WasmPtr:
704 b[i] = I32
705 case obj.WasmI64:
706 b[i] = I64
707 case obj.WasmF32:
708 b[i] = F32
709 case obj.WasmF64:
710 b[i] = F64
711 default:
712 panic(fmt.Sprintf("fieldsToTypes: unknown field type: %d", f.Type))
713 }
714 }
715 return b
716 }
717
View as plain text