Black Lives Matter. Support the Equal Justice Initiative.

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

View as plain text