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