1// Copyright 2018 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package wasm
6
7import (
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
23const (
24	I32 = 0x7F
25	I64 = 0x7E
26	F32 = 0x7D
27	F64 = 0x7C
28)
29
30const (
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// funcValueOffset is the offset between the PC_F value of a function and the index of the function in WebAssembly
46const funcValueOffset = 0x1000 // TODO(neelance): make function addresses play nice with heap addresses
47
48func gentext(ctxt *ld.Link, ldr *loader.Loader) {
49}
50
51type wasmFunc struct {
52	Module string
53	Name   string
54	Type   uint32
55	Code   []byte
56}
57
58type wasmFuncType struct {
59	Params  []byte
60	Results []byte
61}
62
63func 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
116var 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}},                                 // argc, argv
121	"wasm_export_resume":      {Params: []byte{}},                                         //
122	"wasm_export_getsp":       {Results: []byte{I32}},                                     // sp
123	"wasm_pc_f_loop":          {Params: []byte{}},                                         //
124	"runtime.wasmDiv":         {Params: []byte{I64, I64}, Results: []byte{I64}},           // x, y -> x/y
125	"runtime.wasmTruncS":      {Params: []byte{F64}, Results: []byte{I64}},                // x -> int(x)
126	"runtime.wasmTruncU":      {Params: []byte{F64}, Results: []byte{I64}},                // x -> uint(x)
127	"gcWriteBarrier":          {Params: []byte{I64}, Results: []byte{I64}},                // #bytes -> bufptr
128	"runtime.gcWriteBarrier1": {Results: []byte{I64}},                                     // -> bufptr
129	"runtime.gcWriteBarrier2": {Results: []byte{I64}},                                     // -> bufptr
130	"runtime.gcWriteBarrier3": {Results: []byte{I64}},                                     // -> bufptr
131	"runtime.gcWriteBarrier4": {Results: []byte{I64}},                                     // -> bufptr
132	"runtime.gcWriteBarrier5": {Results: []byte{I64}},                                     // -> bufptr
133	"runtime.gcWriteBarrier6": {Results: []byte{I64}},                                     // -> bufptr
134	"runtime.gcWriteBarrier7": {Results: []byte{I64}},                                     // -> bufptr
135	"runtime.gcWriteBarrier8": {Results: []byte{I64}},                                     // -> bufptr
136	"cmpbody":                 {Params: []byte{I64, I64, I64, I64}, Results: []byte{I64}}, // a, alen, b, blen -> -1/0/1
137	"memeqbody":               {Params: []byte{I64, I64, I64}, Results: []byte{I64}},      // a, b, len -> 0/1
138	"memcmp":                  {Params: []byte{I32, I32, I32}, Results: []byte{I32}},      // a, b, len -> <0/0/>0
139	"memchr":                  {Params: []byte{I32, I32, I32}, Results: []byte{I32}},      // s, c, len -> index
140}
141
142func assignAddress(ldr *loader.Loader, sect *sym.Section, n int, s loader.Sym, va uint64, isTramp bool) (*sym.Section, int, uint64) {
143	// WebAssembly functions do not live in the same address space as the linear memory.
144	// Instead, WebAssembly automatically assigns indices. Imported functions (section "import")
145	// have indices 0 to n. They are followed by native functions (sections "function" and "code")
146	// with indices n+1 and following.
147	//
148	// The following rules describe how wasm handles function indices and addresses:
149	//   PC_F = funcValueOffset + WebAssembly function index (not including the imports)
150	//   s.Value = PC = PC_F<<16 + PC_B
151	//
152	// The funcValueOffset is necessary to avoid conflicts with expectations
153	// that the Go runtime has about function addresses.
154	// The field "s.Value" corresponds to the concept of PC at runtime.
155	// However, there is no PC register, only PC_F and PC_B. PC_F denotes the function,
156	// PC_B the resume point inside of that function. The entry of the function has PC_B = 0.
157	ldr.SetSymSect(s, sect)
158	ldr.SetSymValue(s, int64(funcValueOffset+va/abi.MINFUNC)<<16) // va starts at zero
159	va += uint64(abi.MINFUNC)
160	return sect, n, va
161}
162
163type wasmDataSect struct {
164	sect *sym.Section
165	data []byte
166}
167
168var dataSects []wasmDataSect
169
170func 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// asmb writes the final WebAssembly module binary.
189// Spec: https://webassembly.github.io/spec/core/binary/modules.html
190func asmb2(ctxt *ld.Link, ldr *loader.Loader) {
191	types := []*wasmFuncType{
192		// For normal Go functions, the single parameter is PC_B,
193		// the return value is
194		// 0 if the function returned normally or
195		// 1 if the stack needs to be unwound.
196		{Params: []byte{I32}, Results: []byte{I32}},
197	}
198
199	// collect host imports (functions that get imported from the WebAssembly host, usually JavaScript)
200	// we store the import index of each imported function, so the R_WASMIMPORT relocation
201	// can write the correct index after a "call" instruction
202	// these are added as import statements to the top of the WebAssembly binary
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	// collect functions with WebAssembly body
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) // number of sets of locals
235			writeI32Const(wfn, 0)
236			wfn.WriteByte(0x0b) // end
237			buildid = ldr.Data(fn)
238		} else {
239			// Relocations have variable length, handle them here.
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 // skip marker relocations
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}) // magic
276	ctxt.Out.Write([]byte{0x01, 0x00, 0x00, 0x00}) // version
277
278	// Add any buildid early in the binary:
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
299func 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
309func writeSecHeader(ctxt *ld.Link, id uint8) int64 {
310	ctxt.Out.WriteByte(id)
311	sizeOffset := ctxt.Out.Offset()
312	ctxt.Out.Write(make([]byte, 5)) // placeholder for length
313	return sizeOffset
314}
315
316func 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
323func 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// writeTypeSec writes the section that declares all function types
331// so they can be referenced by index.
332func 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) // functype
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// writeImportSec writes the section that lists the functions that get
353// imported from the WebAssembly host, usually JavaScript.
354func writeImportSec(ctxt *ld.Link, hostImports []*wasmFunc) {
355	sizeOffset := writeSecHeader(ctxt, sectionImport)
356
357	writeUleb128(ctxt.Out, uint64(len(hostImports))) // number of imports
358	for _, fn := range hostImports {
359		if fn.Module != "" {
360			writeName(ctxt.Out, fn.Module)
361		} else {
362			writeName(ctxt.Out, wasm.GojsModule) // provided by the import object in wasm_exec.js
363		}
364		writeName(ctxt.Out, fn.Name)
365		ctxt.Out.WriteByte(0x00) // func import
366		writeUleb128(ctxt.Out, uint64(fn.Type))
367	}
368
369	writeSecSize(ctxt, sizeOffset)
370}
371
372// writeFunctionSec writes the section that declares the types of functions.
373// The bodies of these functions will later be provided in the "code" section.
374func 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// writeTableSec writes the section that declares tables. Currently there is only a single table
386// that is used by the CallIndirect operation to dynamically call any function.
387// The contents of the table get initialized by the "element" section.
388func writeTableSec(ctxt *ld.Link, fns []*wasmFunc) {
389	sizeOffset := writeSecHeader(ctxt, sectionTable)
390
391	numElements := uint64(funcValueOffset + len(fns))
392	writeUleb128(ctxt.Out, 1)           // number of tables
393	ctxt.Out.WriteByte(0x70)            // type: anyfunc
394	ctxt.Out.WriteByte(0x00)            // no max
395	writeUleb128(ctxt.Out, numElements) // min
396
397	writeSecSize(ctxt, sizeOffset)
398}
399
400// writeMemorySec writes the section that declares linear memories. Currently one linear memory is being used.
401// Linear memory always starts at address zero. More memory can be requested with the GrowMemory instruction.
402func 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 // 16MB, enough for runtime init without growing
408
409	const wasmPageSize = 64 << 10 // 64KB
410
411	writeUleb128(ctxt.Out, 1)                        // number of memories
412	ctxt.Out.WriteByte(0x00)                         // no maximum memory size
413	writeUleb128(ctxt.Out, initialSize/wasmPageSize) // minimum (initial) memory size
414
415	writeSecSize(ctxt, sizeOffset)
416}
417
418// writeGlobalSec writes the section that declares global variables.
419func writeGlobalSec(ctxt *ld.Link) {
420	sizeOffset := writeSecHeader(ctxt, sectionGlobal)
421
422	globalRegs := []byte{
423		I32, // 0: SP
424		I64, // 1: CTXT
425		I64, // 2: g
426		I64, // 3: RET0
427		I64, // 4: RET1
428		I64, // 5: RET2
429		I64, // 6: RET3
430		I32, // 7: PAUSE
431	}
432
433	writeUleb128(ctxt.Out, uint64(len(globalRegs))) // number of globals
434
435	for _, typ := range globalRegs {
436		ctxt.Out.WriteByte(typ)
437		ctxt.Out.WriteByte(0x01) // var
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) // end
445	}
446
447	writeSecSize(ctxt, sizeOffset)
448}
449
450// writeExportSec writes the section that declares exports.
451// Exports can be accessed by the WebAssembly host, usually JavaScript.
452// The wasm_export_* functions and the linear memory get exported.
453func 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) // number of exports
459		s := ldr.Lookup("_rt0_wasm_wasip1", 0)
460		idx := uint32(lenHostImports) + uint32(ldr.SymValue(s)>>16) - funcValueOffset
461		writeName(ctxt.Out, "_start")       // the wasi entrypoint
462		ctxt.Out.WriteByte(0x00)            // func export
463		writeUleb128(ctxt.Out, uint64(idx)) // funcidx
464		writeName(ctxt.Out, "memory")       // memory in wasi
465		ctxt.Out.WriteByte(0x02)            // mem export
466		writeUleb128(ctxt.Out, 0)           // memidx
467	case "js":
468		writeUleb128(ctxt.Out, 4) // number of exports
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)           // inst.exports.run/resume/getsp in wasm_exec.js
473			ctxt.Out.WriteByte(0x00)            // func export
474			writeUleb128(ctxt.Out, uint64(idx)) // funcidx
475		}
476		writeName(ctxt.Out, "mem") // inst.exports.mem in wasm_exec.js
477		ctxt.Out.WriteByte(0x02)   // mem export
478		writeUleb128(ctxt.Out, 0)  // memidx
479	default:
480		ld.Exitf("internal error: writeExportSec: unrecognized GOOS %s", buildcfg.GOOS)
481	}
482
483	writeSecSize(ctxt, sizeOffset)
484}
485
486// writeElementSec writes the section that initializes the tables declared by the "table" section.
487// The table for CallIndirect gets initialized in a very simple way so that each table index (PC_F value)
488// maps linearly to the function index (numImports + PC_F).
489func writeElementSec(ctxt *ld.Link, numImports, numFns uint64) {
490	sizeOffset := writeSecHeader(ctxt, sectionElement)
491
492	writeUleb128(ctxt.Out, 1) // number of element segments
493
494	writeUleb128(ctxt.Out, 0) // tableidx
495	writeI32Const(ctxt.Out, funcValueOffset)
496	ctxt.Out.WriteByte(0x0b) // end
497
498	writeUleb128(ctxt.Out, numFns) // number of entries
499	for i := uint64(0); i < numFns; i++ {
500		writeUleb128(ctxt.Out, numImports+i)
501	}
502
503	writeSecSize(ctxt, sizeOffset)
504}
505
506// writeCodeSec writes the section that provides the function bodies for the functions
507// declared by the "func" section.
508func writeCodeSec(ctxt *ld.Link, fns []*wasmFunc) {
509	sizeOffset := writeSecHeader(ctxt, sectionCode)
510
511	writeUleb128(ctxt.Out, uint64(len(fns))) // number of code entries
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// writeDataSec writes the section that provides data that will be used to initialize the linear memory.
521func writeDataSec(ctxt *ld.Link) {
522	sizeOffset := writeSecHeader(ctxt, sectionData)
523
524	type dataSegment struct {
525		offset int32
526		data   []byte
527	}
528
529	// Omit blocks of zeroes and instead emit data segments with offsets skipping the zeroes.
530	// This reduces the size of the WebAssembly binary. We use 8 bytes as an estimate for the
531	// overhead of adding a new segment (same as wasm-opt's memory-packing optimization uses).
532	const segmentOverhead = 8
533
534	// Generate at most this many segments. A higher number of segments gets rejected by some WebAssembly runtimes.
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		// skip leading zeroes
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					// look for beginning of zeroes
557					for segmentEnd < dataLen && data[segmentEnd] != 0 {
558						segmentEnd++
559					}
560					// look for end of zeroes
561					zeroEnd = segmentEnd
562					for zeroEnd < dataLen && data[zeroEnd] == 0 {
563						zeroEnd++
564					}
565					// emit segment if omitting zeroes reduces the output size
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))) // number of data entries
583	for _, seg := range segments {
584		writeUleb128(ctxt.Out, 0) // memidx
585		writeI32Const(ctxt.Out, seg.offset)
586		ctxt.Out.WriteByte(0x0b) // end
587		writeUleb128(ctxt.Out, uint64(len(seg.data)))
588		ctxt.Out.Write(seg.data)
589	}
590
591	writeSecSize(ctxt, sizeOffset)
592}
593
594// writeProducerSec writes an optional section that reports the source language and compiler version.
595func writeProducerSec(ctxt *ld.Link) {
596	sizeOffset := writeSecHeader(ctxt, sectionCustom)
597	writeName(ctxt.Out, "producers")
598
599	writeUleb128(ctxt.Out, 2) // number of fields
600
601	writeName(ctxt.Out, "language")       // field name
602	writeUleb128(ctxt.Out, 1)             // number of values
603	writeName(ctxt.Out, "Go")             // value: name
604	writeName(ctxt.Out, buildcfg.Version) // value: version
605
606	writeName(ctxt.Out, "processed-by")   // field name
607	writeUleb128(ctxt.Out, 1)             // number of values
608	writeName(ctxt.Out, "Go cmd/compile") // value: name
609	writeName(ctxt.Out, buildcfg.Version) // value: version
610
611	writeSecSize(ctxt, sizeOffset)
612}
613
614var nameRegexp = regexp.MustCompile(`[^\w.]`)
615
616// writeNameSec writes an optional section that assigns names to the functions declared by the "func" section.
617// The names are only used by WebAssembly stack traces, debuggers and decompilers.
618// TODO(neelance): add symbol table of DATA symbols
619func writeNameSec(ctxt *ld.Link, firstFnIndex int, fns []*wasmFunc) {
620	sizeOffset := writeSecHeader(ctxt, sectionCustom)
621	writeName(ctxt.Out, "name")
622
623	sizeOffset2 := writeSecHeader(ctxt, 0x01) // function names
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
634type nameWriter interface {
635	io.ByteWriter
636	io.Writer
637}
638
639func writeI32Const(w io.ByteWriter, v int32) {
640	w.WriteByte(0x41) // i32.const
641	writeSleb128(w, int64(v))
642}
643
644func writeI64Const(w io.ByteWriter, v int64) {
645	w.WriteByte(0x42) // i64.const
646	writeSleb128(w, v)
647}
648
649func writeName(w nameWriter, name string) {
650	writeUleb128(w, uint64(len(name)))
651	w.Write([]byte(name))
652}
653
654func 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
671func 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
685func 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
699func 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