Source file src/cmd/link/internal/wasm/asm.go

     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  
     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  	"encoding/binary"
    16  	"fmt"
    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  // funcValueOffset is the offset between the PC_F value of a function and the index of the function in WebAssembly
    45  const funcValueOffset = 0x1000 // TODO(neelance): make function addresses play nice with heap addresses
    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  	reportError := func(err error) { panic(fmt.Sprintf("failed to read WASM import in sym %v: %v", s, err)) }
    64  
    65  	data := ldr.Data(s)
    66  
    67  	readUint32 := func() (v uint32) {
    68  		v = binary.LittleEndian.Uint32(data)
    69  		data = data[4:]
    70  		return
    71  	}
    72  
    73  	readUint64 := func() (v uint64) {
    74  		v = binary.LittleEndian.Uint64(data)
    75  		data = data[8:]
    76  		return
    77  	}
    78  
    79  	readByte := func() byte {
    80  		if len(data) == 0 {
    81  			reportError(io.EOF)
    82  		}
    83  
    84  		b := data[0]
    85  		data = data[1:]
    86  		return b
    87  	}
    88  
    89  	readString := func() string {
    90  		n := readUint32()
    91  
    92  		s := string(data[:n])
    93  
    94  		data = data[n:]
    95  
    96  		return s
    97  	}
    98  
    99  	var wi obj.WasmImport
   100  	wi.Module = readString()
   101  	wi.Name = readString()
   102  	wi.Params = make([]obj.WasmField, readUint32())
   103  	for i := range wi.Params {
   104  		wi.Params[i].Type = obj.WasmFieldType(readByte())
   105  		wi.Params[i].Offset = int64(readUint64())
   106  	}
   107  	wi.Results = make([]obj.WasmField, readUint32())
   108  	for i := range wi.Results {
   109  		wi.Results[i].Type = obj.WasmFieldType(readByte())
   110  		wi.Results[i].Offset = int64(readUint64())
   111  	}
   112  	return wi
   113  }
   114  
   115  var wasmFuncTypes = map[string]*wasmFuncType{
   116  	"_rt0_wasm_js":            {Params: []byte{}},                                         //
   117  	"_rt0_wasm_wasip1":        {Params: []byte{}},                                         //
   118  	"wasm_export__start":      {},                                                         //
   119  	"wasm_export_run":         {Params: []byte{I32, I32}},                                 // argc, argv
   120  	"wasm_export_resume":      {Params: []byte{}},                                         //
   121  	"wasm_export_getsp":       {Results: []byte{I32}},                                     // sp
   122  	"wasm_pc_f_loop":          {Params: []byte{}},                                         //
   123  	"runtime.wasmDiv":         {Params: []byte{I64, I64}, Results: []byte{I64}},           // x, y -> x/y
   124  	"runtime.wasmTruncS":      {Params: []byte{F64}, Results: []byte{I64}},                // x -> int(x)
   125  	"runtime.wasmTruncU":      {Params: []byte{F64}, Results: []byte{I64}},                // x -> uint(x)
   126  	"gcWriteBarrier":          {Params: []byte{I64}, Results: []byte{I64}},                // #bytes -> bufptr
   127  	"runtime.gcWriteBarrier1": {Results: []byte{I64}},                                     // -> bufptr
   128  	"runtime.gcWriteBarrier2": {Results: []byte{I64}},                                     // -> bufptr
   129  	"runtime.gcWriteBarrier3": {Results: []byte{I64}},                                     // -> bufptr
   130  	"runtime.gcWriteBarrier4": {Results: []byte{I64}},                                     // -> bufptr
   131  	"runtime.gcWriteBarrier5": {Results: []byte{I64}},                                     // -> bufptr
   132  	"runtime.gcWriteBarrier6": {Results: []byte{I64}},                                     // -> bufptr
   133  	"runtime.gcWriteBarrier7": {Results: []byte{I64}},                                     // -> bufptr
   134  	"runtime.gcWriteBarrier8": {Results: []byte{I64}},                                     // -> bufptr
   135  	"cmpbody":                 {Params: []byte{I64, I64, I64, I64}, Results: []byte{I64}}, // a, alen, b, blen -> -1/0/1
   136  	"memeqbody":               {Params: []byte{I64, I64, I64}, Results: []byte{I64}},      // a, b, len -> 0/1
   137  	"memcmp":                  {Params: []byte{I32, I32, I32}, Results: []byte{I32}},      // a, b, len -> <0/0/>0
   138  	"memchr":                  {Params: []byte{I32, I32, I32}, Results: []byte{I32}},      // s, c, len -> index
   139  }
   140  
   141  func assignAddress(ldr *loader.Loader, sect *sym.Section, n int, s loader.Sym, va uint64, isTramp bool) (*sym.Section, int, uint64) {
   142  	// WebAssembly functions do not live in the same address space as the linear memory.
   143  	// Instead, WebAssembly automatically assigns indices. Imported functions (section "import")
   144  	// have indices 0 to n. They are followed by native functions (sections "function" and "code")
   145  	// with indices n+1 and following.
   146  	//
   147  	// The following rules describe how wasm handles function indices and addresses:
   148  	//   PC_F = funcValueOffset + WebAssembly function index (not including the imports)
   149  	//   s.Value = PC = PC_F<<16 + PC_B
   150  	//
   151  	// The funcValueOffset is necessary to avoid conflicts with expectations
   152  	// that the Go runtime has about function addresses.
   153  	// The field "s.Value" corresponds to the concept of PC at runtime.
   154  	// However, there is no PC register, only PC_F and PC_B. PC_F denotes the function,
   155  	// PC_B the resume point inside of that function. The entry of the function has PC_B = 0.
   156  	ldr.SetSymSect(s, sect)
   157  	ldr.SetSymValue(s, int64(funcValueOffset+va/ld.MINFUNC)<<16) // va starts at zero
   158  	va += uint64(ld.MINFUNC)
   159  	return sect, n, va
   160  }
   161  
   162  type wasmDataSect struct {
   163  	sect *sym.Section
   164  	data []byte
   165  }
   166  
   167  var dataSects []wasmDataSect
   168  
   169  func asmb(ctxt *ld.Link, ldr *loader.Loader) {
   170  	sections := []*sym.Section{
   171  		ldr.SymSect(ldr.Lookup("runtime.rodata", 0)),
   172  		ldr.SymSect(ldr.Lookup("runtime.typelink", 0)),
   173  		ldr.SymSect(ldr.Lookup("runtime.itablink", 0)),
   174  		ldr.SymSect(ldr.Lookup("runtime.symtab", 0)),
   175  		ldr.SymSect(ldr.Lookup("runtime.pclntab", 0)),
   176  		ldr.SymSect(ldr.Lookup("runtime.noptrdata", 0)),
   177  		ldr.SymSect(ldr.Lookup("runtime.data", 0)),
   178  	}
   179  
   180  	dataSects = make([]wasmDataSect, len(sections))
   181  	for i, sect := range sections {
   182  		data := ld.DatblkBytes(ctxt, int64(sect.Vaddr), int64(sect.Length))
   183  		dataSects[i] = wasmDataSect{sect, data}
   184  	}
   185  }
   186  
   187  // asmb writes the final WebAssembly module binary.
   188  // Spec: https://webassembly.github.io/spec/core/binary/modules.html
   189  func asmb2(ctxt *ld.Link, ldr *loader.Loader) {
   190  	types := []*wasmFuncType{
   191  		// For normal Go functions, the single parameter is PC_B,
   192  		// the return value is
   193  		// 0 if the function returned normally or
   194  		// 1 if the stack needs to be unwound.
   195  		{Params: []byte{I32}, Results: []byte{I32}},
   196  	}
   197  
   198  	// collect host imports (functions that get imported from the WebAssembly host, usually JavaScript)
   199  	// we store the import index of each imported function, so the R_WASMIMPORT relocation
   200  	// can write the correct index after a "call" instruction
   201  	// these are added as import statements to the top of the WebAssembly binary
   202  	var hostImports []*wasmFunc
   203  	hostImportMap := make(map[loader.Sym]int64)
   204  	for _, fn := range ctxt.Textp {
   205  		relocs := ldr.Relocs(fn)
   206  		for ri := 0; ri < relocs.Count(); ri++ {
   207  			r := relocs.At(ri)
   208  			if r.Type() == objabi.R_WASMIMPORT {
   209  				if lsym, ok := ldr.WasmImportSym(fn); ok {
   210  					wi := readWasmImport(ldr, lsym)
   211  					hostImportMap[fn] = int64(len(hostImports))
   212  					hostImports = append(hostImports, &wasmFunc{
   213  						Module: wi.Module,
   214  						Name:   wi.Name,
   215  						Type: lookupType(&wasmFuncType{
   216  							Params:  fieldsToTypes(wi.Params),
   217  							Results: fieldsToTypes(wi.Results),
   218  						}, &types),
   219  					})
   220  				} else {
   221  					panic(fmt.Sprintf("missing wasm symbol for %s", ldr.SymName(r.Sym())))
   222  				}
   223  			}
   224  		}
   225  	}
   226  
   227  	// collect functions with WebAssembly body
   228  	var buildid []byte
   229  	fns := make([]*wasmFunc, len(ctxt.Textp))
   230  	for i, fn := range ctxt.Textp {
   231  		wfn := new(bytes.Buffer)
   232  		if ldr.SymName(fn) == "go:buildid" {
   233  			writeUleb128(wfn, 0) // number of sets of locals
   234  			writeI32Const(wfn, 0)
   235  			wfn.WriteByte(0x0b) // end
   236  			buildid = ldr.Data(fn)
   237  		} else {
   238  			// Relocations have variable length, handle them here.
   239  			relocs := ldr.Relocs(fn)
   240  			P := ldr.Data(fn)
   241  			off := int32(0)
   242  			for ri := 0; ri < relocs.Count(); ri++ {
   243  				r := relocs.At(ri)
   244  				if r.Siz() == 0 {
   245  					continue // skip marker relocations
   246  				}
   247  				wfn.Write(P[off:r.Off()])
   248  				off = r.Off()
   249  				rs := r.Sym()
   250  				switch r.Type() {
   251  				case objabi.R_ADDR:
   252  					writeSleb128(wfn, ldr.SymValue(rs)+r.Add())
   253  				case objabi.R_CALL:
   254  					writeSleb128(wfn, int64(len(hostImports))+ldr.SymValue(rs)>>16-funcValueOffset)
   255  				case objabi.R_WASMIMPORT:
   256  					writeSleb128(wfn, hostImportMap[rs])
   257  				default:
   258  					ldr.Errorf(fn, "bad reloc type %d (%s)", r.Type(), sym.RelocName(ctxt.Arch, r.Type()))
   259  					continue
   260  				}
   261  			}
   262  			wfn.Write(P[off:])
   263  		}
   264  
   265  		typ := uint32(0)
   266  		if sig, ok := wasmFuncTypes[ldr.SymName(fn)]; ok {
   267  			typ = lookupType(sig, &types)
   268  		}
   269  
   270  		name := nameRegexp.ReplaceAllString(ldr.SymName(fn), "_")
   271  		fns[i] = &wasmFunc{Name: name, Type: typ, Code: wfn.Bytes()}
   272  	}
   273  
   274  	ctxt.Out.Write([]byte{0x00, 0x61, 0x73, 0x6d}) // magic
   275  	ctxt.Out.Write([]byte{0x01, 0x00, 0x00, 0x00}) // version
   276  
   277  	// Add any buildid early in the binary:
   278  	if len(buildid) != 0 {
   279  		writeBuildID(ctxt, buildid)
   280  	}
   281  
   282  	writeTypeSec(ctxt, types)
   283  	writeImportSec(ctxt, hostImports)
   284  	writeFunctionSec(ctxt, fns)
   285  	writeTableSec(ctxt, fns)
   286  	writeMemorySec(ctxt, ldr)
   287  	writeGlobalSec(ctxt)
   288  	writeExportSec(ctxt, ldr, len(hostImports))
   289  	writeElementSec(ctxt, uint64(len(hostImports)), uint64(len(fns)))
   290  	writeCodeSec(ctxt, fns)
   291  	writeDataSec(ctxt)
   292  	writeProducerSec(ctxt)
   293  	if !*ld.FlagS {
   294  		writeNameSec(ctxt, len(hostImports), fns)
   295  	}
   296  }
   297  
   298  func lookupType(sig *wasmFuncType, types *[]*wasmFuncType) uint32 {
   299  	for i, t := range *types {
   300  		if bytes.Equal(sig.Params, t.Params) && bytes.Equal(sig.Results, t.Results) {
   301  			return uint32(i)
   302  		}
   303  	}
   304  	*types = append(*types, sig)
   305  	return uint32(len(*types) - 1)
   306  }
   307  
   308  func writeSecHeader(ctxt *ld.Link, id uint8) int64 {
   309  	ctxt.Out.WriteByte(id)
   310  	sizeOffset := ctxt.Out.Offset()
   311  	ctxt.Out.Write(make([]byte, 5)) // placeholder for length
   312  	return sizeOffset
   313  }
   314  
   315  func writeSecSize(ctxt *ld.Link, sizeOffset int64) {
   316  	endOffset := ctxt.Out.Offset()
   317  	ctxt.Out.SeekSet(sizeOffset)
   318  	writeUleb128FixedLength(ctxt.Out, uint64(endOffset-sizeOffset-5), 5)
   319  	ctxt.Out.SeekSet(endOffset)
   320  }
   321  
   322  func writeBuildID(ctxt *ld.Link, buildid []byte) {
   323  	sizeOffset := writeSecHeader(ctxt, sectionCustom)
   324  	writeName(ctxt.Out, "go:buildid")
   325  	ctxt.Out.Write(buildid)
   326  	writeSecSize(ctxt, sizeOffset)
   327  }
   328  
   329  // writeTypeSec writes the section that declares all function types
   330  // so they can be referenced by index.
   331  func writeTypeSec(ctxt *ld.Link, types []*wasmFuncType) {
   332  	sizeOffset := writeSecHeader(ctxt, sectionType)
   333  
   334  	writeUleb128(ctxt.Out, uint64(len(types)))
   335  
   336  	for _, t := range types {
   337  		ctxt.Out.WriteByte(0x60) // functype
   338  		writeUleb128(ctxt.Out, uint64(len(t.Params)))
   339  		for _, v := range t.Params {
   340  			ctxt.Out.WriteByte(byte(v))
   341  		}
   342  		writeUleb128(ctxt.Out, uint64(len(t.Results)))
   343  		for _, v := range t.Results {
   344  			ctxt.Out.WriteByte(byte(v))
   345  		}
   346  	}
   347  
   348  	writeSecSize(ctxt, sizeOffset)
   349  }
   350  
   351  // writeImportSec writes the section that lists the functions that get
   352  // imported from the WebAssembly host, usually JavaScript.
   353  func writeImportSec(ctxt *ld.Link, hostImports []*wasmFunc) {
   354  	sizeOffset := writeSecHeader(ctxt, sectionImport)
   355  
   356  	writeUleb128(ctxt.Out, uint64(len(hostImports))) // number of imports
   357  	for _, fn := range hostImports {
   358  		if fn.Module != "" {
   359  			writeName(ctxt.Out, fn.Module)
   360  		} else {
   361  			writeName(ctxt.Out, wasm.GojsModule) // provided by the import object in wasm_exec.js
   362  		}
   363  		writeName(ctxt.Out, fn.Name)
   364  		ctxt.Out.WriteByte(0x00) // func import
   365  		writeUleb128(ctxt.Out, uint64(fn.Type))
   366  	}
   367  
   368  	writeSecSize(ctxt, sizeOffset)
   369  }
   370  
   371  // writeFunctionSec writes the section that declares the types of functions.
   372  // The bodies of these functions will later be provided in the "code" section.
   373  func writeFunctionSec(ctxt *ld.Link, fns []*wasmFunc) {
   374  	sizeOffset := writeSecHeader(ctxt, sectionFunction)
   375  
   376  	writeUleb128(ctxt.Out, uint64(len(fns)))
   377  	for _, fn := range fns {
   378  		writeUleb128(ctxt.Out, uint64(fn.Type))
   379  	}
   380  
   381  	writeSecSize(ctxt, sizeOffset)
   382  }
   383  
   384  // writeTableSec writes the section that declares tables. Currently there is only a single table
   385  // that is used by the CallIndirect operation to dynamically call any function.
   386  // The contents of the table get initialized by the "element" section.
   387  func writeTableSec(ctxt *ld.Link, fns []*wasmFunc) {
   388  	sizeOffset := writeSecHeader(ctxt, sectionTable)
   389  
   390  	numElements := uint64(funcValueOffset + len(fns))
   391  	writeUleb128(ctxt.Out, 1)           // number of tables
   392  	ctxt.Out.WriteByte(0x70)            // type: anyfunc
   393  	ctxt.Out.WriteByte(0x00)            // no max
   394  	writeUleb128(ctxt.Out, numElements) // min
   395  
   396  	writeSecSize(ctxt, sizeOffset)
   397  }
   398  
   399  // writeMemorySec writes the section that declares linear memories. Currently one linear memory is being used.
   400  // Linear memory always starts at address zero. More memory can be requested with the GrowMemory instruction.
   401  func writeMemorySec(ctxt *ld.Link, ldr *loader.Loader) {
   402  	sizeOffset := writeSecHeader(ctxt, sectionMemory)
   403  
   404  	dataSection := ldr.SymSect(ldr.Lookup("runtime.data", 0))
   405  	dataEnd := dataSection.Vaddr + dataSection.Length
   406  	var initialSize = dataEnd + 16<<20 // 16MB, enough for runtime init without growing
   407  
   408  	const wasmPageSize = 64 << 10 // 64KB
   409  
   410  	writeUleb128(ctxt.Out, 1)                        // number of memories
   411  	ctxt.Out.WriteByte(0x00)                         // no maximum memory size
   412  	writeUleb128(ctxt.Out, initialSize/wasmPageSize) // minimum (initial) memory size
   413  
   414  	writeSecSize(ctxt, sizeOffset)
   415  }
   416  
   417  // writeGlobalSec writes the section that declares global variables.
   418  func writeGlobalSec(ctxt *ld.Link) {
   419  	sizeOffset := writeSecHeader(ctxt, sectionGlobal)
   420  
   421  	globalRegs := []byte{
   422  		I32, // 0: SP
   423  		I64, // 1: CTXT
   424  		I64, // 2: g
   425  		I64, // 3: RET0
   426  		I64, // 4: RET1
   427  		I64, // 5: RET2
   428  		I64, // 6: RET3
   429  		I32, // 7: PAUSE
   430  	}
   431  
   432  	writeUleb128(ctxt.Out, uint64(len(globalRegs))) // number of globals
   433  
   434  	for _, typ := range globalRegs {
   435  		ctxt.Out.WriteByte(typ)
   436  		ctxt.Out.WriteByte(0x01) // var
   437  		switch typ {
   438  		case I32:
   439  			writeI32Const(ctxt.Out, 0)
   440  		case I64:
   441  			writeI64Const(ctxt.Out, 0)
   442  		}
   443  		ctxt.Out.WriteByte(0x0b) // end
   444  	}
   445  
   446  	writeSecSize(ctxt, sizeOffset)
   447  }
   448  
   449  // writeExportSec writes the section that declares exports.
   450  // Exports can be accessed by the WebAssembly host, usually JavaScript.
   451  // The wasm_export_* functions and the linear memory get exported.
   452  func writeExportSec(ctxt *ld.Link, ldr *loader.Loader, lenHostImports int) {
   453  	sizeOffset := writeSecHeader(ctxt, sectionExport)
   454  
   455  	switch buildcfg.GOOS {
   456  	case "wasip1":
   457  		writeUleb128(ctxt.Out, 2) // number of exports
   458  		s := ldr.Lookup("_rt0_wasm_wasip1", 0)
   459  		idx := uint32(lenHostImports) + uint32(ldr.SymValue(s)>>16) - funcValueOffset
   460  		writeName(ctxt.Out, "_start")       // the wasi entrypoint
   461  		ctxt.Out.WriteByte(0x00)            // func export
   462  		writeUleb128(ctxt.Out, uint64(idx)) // funcidx
   463  		writeName(ctxt.Out, "memory")       // memory in wasi
   464  		ctxt.Out.WriteByte(0x02)            // mem export
   465  		writeUleb128(ctxt.Out, 0)           // memidx
   466  	case "js":
   467  		writeUleb128(ctxt.Out, 4) // number of exports
   468  		for _, name := range []string{"run", "resume", "getsp"} {
   469  			s := ldr.Lookup("wasm_export_"+name, 0)
   470  			idx := uint32(lenHostImports) + uint32(ldr.SymValue(s)>>16) - funcValueOffset
   471  			writeName(ctxt.Out, name)           // inst.exports.run/resume/getsp in wasm_exec.js
   472  			ctxt.Out.WriteByte(0x00)            // func export
   473  			writeUleb128(ctxt.Out, uint64(idx)) // funcidx
   474  		}
   475  		writeName(ctxt.Out, "mem") // inst.exports.mem in wasm_exec.js
   476  		ctxt.Out.WriteByte(0x02)   // mem export
   477  		writeUleb128(ctxt.Out, 0)  // memidx
   478  	default:
   479  		ld.Exitf("internal error: writeExportSec: unrecognized GOOS %s", buildcfg.GOOS)
   480  	}
   481  
   482  	writeSecSize(ctxt, sizeOffset)
   483  }
   484  
   485  // writeElementSec writes the section that initializes the tables declared by the "table" section.
   486  // The table for CallIndirect gets initialized in a very simple way so that each table index (PC_F value)
   487  // maps linearly to the function index (numImports + PC_F).
   488  func writeElementSec(ctxt *ld.Link, numImports, numFns uint64) {
   489  	sizeOffset := writeSecHeader(ctxt, sectionElement)
   490  
   491  	writeUleb128(ctxt.Out, 1) // number of element segments
   492  
   493  	writeUleb128(ctxt.Out, 0) // tableidx
   494  	writeI32Const(ctxt.Out, funcValueOffset)
   495  	ctxt.Out.WriteByte(0x0b) // end
   496  
   497  	writeUleb128(ctxt.Out, numFns) // number of entries
   498  	for i := uint64(0); i < numFns; i++ {
   499  		writeUleb128(ctxt.Out, numImports+i)
   500  	}
   501  
   502  	writeSecSize(ctxt, sizeOffset)
   503  }
   504  
   505  // writeCodeSec writes the section that provides the function bodies for the functions
   506  // declared by the "func" section.
   507  func writeCodeSec(ctxt *ld.Link, fns []*wasmFunc) {
   508  	sizeOffset := writeSecHeader(ctxt, sectionCode)
   509  
   510  	writeUleb128(ctxt.Out, uint64(len(fns))) // number of code entries
   511  	for _, fn := range fns {
   512  		writeUleb128(ctxt.Out, uint64(len(fn.Code)))
   513  		ctxt.Out.Write(fn.Code)
   514  	}
   515  
   516  	writeSecSize(ctxt, sizeOffset)
   517  }
   518  
   519  // writeDataSec writes the section that provides data that will be used to initialize the linear memory.
   520  func writeDataSec(ctxt *ld.Link) {
   521  	sizeOffset := writeSecHeader(ctxt, sectionData)
   522  
   523  	type dataSegment struct {
   524  		offset int32
   525  		data   []byte
   526  	}
   527  
   528  	// Omit blocks of zeroes and instead emit data segments with offsets skipping the zeroes.
   529  	// This reduces the size of the WebAssembly binary. We use 8 bytes as an estimate for the
   530  	// overhead of adding a new segment (same as wasm-opt's memory-packing optimization uses).
   531  	const segmentOverhead = 8
   532  
   533  	// Generate at most this many segments. A higher number of segments gets rejected by some WebAssembly runtimes.
   534  	const maxNumSegments = 100000
   535  
   536  	var segments []*dataSegment
   537  	for secIndex, ds := range dataSects {
   538  		data := ds.data
   539  		offset := int32(ds.sect.Vaddr)
   540  
   541  		// skip leading zeroes
   542  		for len(data) > 0 && data[0] == 0 {
   543  			data = data[1:]
   544  			offset++
   545  		}
   546  
   547  		for len(data) > 0 {
   548  			dataLen := int32(len(data))
   549  			var segmentEnd, zeroEnd int32
   550  			if len(segments)+(len(dataSects)-secIndex) == maxNumSegments {
   551  				segmentEnd = dataLen
   552  				zeroEnd = dataLen
   553  			} else {
   554  				for {
   555  					// look for beginning of zeroes
   556  					for segmentEnd < dataLen && data[segmentEnd] != 0 {
   557  						segmentEnd++
   558  					}
   559  					// look for end of zeroes
   560  					zeroEnd = segmentEnd
   561  					for zeroEnd < dataLen && data[zeroEnd] == 0 {
   562  						zeroEnd++
   563  					}
   564  					// emit segment if omitting zeroes reduces the output size
   565  					if zeroEnd-segmentEnd >= segmentOverhead || zeroEnd == dataLen {
   566  						break
   567  					}
   568  					segmentEnd = zeroEnd
   569  				}
   570  			}
   571  
   572  			segments = append(segments, &dataSegment{
   573  				offset: offset,
   574  				data:   data[:segmentEnd],
   575  			})
   576  			data = data[zeroEnd:]
   577  			offset += zeroEnd
   578  		}
   579  	}
   580  
   581  	writeUleb128(ctxt.Out, uint64(len(segments))) // number of data entries
   582  	for _, seg := range segments {
   583  		writeUleb128(ctxt.Out, 0) // memidx
   584  		writeI32Const(ctxt.Out, seg.offset)
   585  		ctxt.Out.WriteByte(0x0b) // end
   586  		writeUleb128(ctxt.Out, uint64(len(seg.data)))
   587  		ctxt.Out.Write(seg.data)
   588  	}
   589  
   590  	writeSecSize(ctxt, sizeOffset)
   591  }
   592  
   593  // writeProducerSec writes an optional section that reports the source language and compiler version.
   594  func writeProducerSec(ctxt *ld.Link) {
   595  	sizeOffset := writeSecHeader(ctxt, sectionCustom)
   596  	writeName(ctxt.Out, "producers")
   597  
   598  	writeUleb128(ctxt.Out, 2) // number of fields
   599  
   600  	writeName(ctxt.Out, "language")       // field name
   601  	writeUleb128(ctxt.Out, 1)             // number of values
   602  	writeName(ctxt.Out, "Go")             // value: name
   603  	writeName(ctxt.Out, buildcfg.Version) // value: version
   604  
   605  	writeName(ctxt.Out, "processed-by")   // field name
   606  	writeUleb128(ctxt.Out, 1)             // number of values
   607  	writeName(ctxt.Out, "Go cmd/compile") // value: name
   608  	writeName(ctxt.Out, buildcfg.Version) // value: version
   609  
   610  	writeSecSize(ctxt, sizeOffset)
   611  }
   612  
   613  var nameRegexp = regexp.MustCompile(`[^\w.]`)
   614  
   615  // writeNameSec writes an optional section that assigns names to the functions declared by the "func" section.
   616  // The names are only used by WebAssembly stack traces, debuggers and decompilers.
   617  // TODO(neelance): add symbol table of DATA symbols
   618  func writeNameSec(ctxt *ld.Link, firstFnIndex int, fns []*wasmFunc) {
   619  	sizeOffset := writeSecHeader(ctxt, sectionCustom)
   620  	writeName(ctxt.Out, "name")
   621  
   622  	sizeOffset2 := writeSecHeader(ctxt, 0x01) // function names
   623  	writeUleb128(ctxt.Out, uint64(len(fns)))
   624  	for i, fn := range fns {
   625  		writeUleb128(ctxt.Out, uint64(firstFnIndex+i))
   626  		writeName(ctxt.Out, fn.Name)
   627  	}
   628  	writeSecSize(ctxt, sizeOffset2)
   629  
   630  	writeSecSize(ctxt, sizeOffset)
   631  }
   632  
   633  type nameWriter interface {
   634  	io.ByteWriter
   635  	io.Writer
   636  }
   637  
   638  func writeI32Const(w io.ByteWriter, v int32) {
   639  	w.WriteByte(0x41) // i32.const
   640  	writeSleb128(w, int64(v))
   641  }
   642  
   643  func writeI64Const(w io.ByteWriter, v int64) {
   644  	w.WriteByte(0x42) // i64.const
   645  	writeSleb128(w, v)
   646  }
   647  
   648  func writeName(w nameWriter, name string) {
   649  	writeUleb128(w, uint64(len(name)))
   650  	w.Write([]byte(name))
   651  }
   652  
   653  func writeUleb128(w io.ByteWriter, v uint64) {
   654  	if v < 128 {
   655  		w.WriteByte(uint8(v))
   656  		return
   657  	}
   658  	more := true
   659  	for more {
   660  		c := uint8(v & 0x7f)
   661  		v >>= 7
   662  		more = v != 0
   663  		if more {
   664  			c |= 0x80
   665  		}
   666  		w.WriteByte(c)
   667  	}
   668  }
   669  
   670  func writeUleb128FixedLength(w io.ByteWriter, v uint64, length int) {
   671  	for i := 0; i < length; i++ {
   672  		c := uint8(v & 0x7f)
   673  		v >>= 7
   674  		if i < length-1 {
   675  			c |= 0x80
   676  		}
   677  		w.WriteByte(c)
   678  	}
   679  	if v != 0 {
   680  		panic("writeUleb128FixedLength: length too small")
   681  	}
   682  }
   683  
   684  func writeSleb128(w io.ByteWriter, v int64) {
   685  	more := true
   686  	for more {
   687  		c := uint8(v & 0x7f)
   688  		s := uint8(v & 0x40)
   689  		v >>= 7
   690  		more = !((v == 0 && s == 0) || (v == -1 && s != 0))
   691  		if more {
   692  			c |= 0x80
   693  		}
   694  		w.WriteByte(c)
   695  	}
   696  }
   697  
   698  func fieldsToTypes(fields []obj.WasmField) []byte {
   699  	b := make([]byte, len(fields))
   700  	for i, f := range fields {
   701  		switch f.Type {
   702  		case obj.WasmI32, obj.WasmPtr:
   703  			b[i] = I32
   704  		case obj.WasmI64:
   705  			b[i] = I64
   706  		case obj.WasmF32:
   707  			b[i] = F32
   708  		case obj.WasmF64:
   709  			b[i] = F64
   710  		default:
   711  			panic(fmt.Sprintf("fieldsToTypes: unknown field type: %d", f.Type))
   712  		}
   713  	}
   714  	return b
   715  }
   716  

View as plain text