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