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