...

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

Documentation: cmd/link/internal/wasm

     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/abi"
    18  	"internal/buildcfg"
    19  	"io"
    20  	"regexp"
    21  )
    22  
    23  const (
    24  	I32 = 0x7F
    25  	I64 = 0x7E
    26  	F32 = 0x7D
    27  	F64 = 0x7C
    28  )
    29  
    30  const (
    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
    46  const funcValueOffset = 0x1000 // TODO(neelance): make function addresses play nice with heap addresses
    47  
    48  func gentext(ctxt *ld.Link, ldr *loader.Loader) {
    49  }
    50  
    51  type wasmFunc struct {
    52  	Module string
    53  	Name   string
    54  	Type   uint32
    55  	Code   []byte
    56  }
    57  
    58  type wasmFuncType struct {
    59  	Params  []byte
    60  	Results []byte
    61  }
    62  
    63  func 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  
   116  var 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  
   142  func 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  
   163  type wasmDataSect struct {
   164  	sect *sym.Section
   165  	data []byte
   166  }
   167  
   168  var dataSects []wasmDataSect
   169  
   170  func 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
   190  func 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  
   299  func 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  
   309  func 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  
   316  func 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  
   323  func 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.
   332  func 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.
   354  func 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.
   374  func 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.
   388  func 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.
   402  func 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.
   419  func 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.
   453  func 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).
   489  func 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.
   508  func 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.
   521  func 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.
   595  func 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  
   614  var 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
   619  func 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  
   634  type nameWriter interface {
   635  	io.ByteWriter
   636  	io.Writer
   637  }
   638  
   639  func writeI32Const(w io.ByteWriter, v int32) {
   640  	w.WriteByte(0x41) // i32.const
   641  	writeSleb128(w, int64(v))
   642  }
   643  
   644  func writeI64Const(w io.ByteWriter, v int64) {
   645  	w.WriteByte(0x42) // i64.const
   646  	writeSleb128(w, v)
   647  }
   648  
   649  func writeName(w nameWriter, name string) {
   650  	writeUleb128(w, uint64(len(name)))
   651  	w.Write([]byte(name))
   652  }
   653  
   654  func 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  
   671  func 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  
   685  func 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  
   699  func 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  

View as plain text