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