github.com/zxy12/go_duplicate_112_new@v0.0.0-20200807091221-747231827200/src/cmd/link/internal/wasm/asm.go (about)

     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/sym"
    12  	"io"
    13  	"regexp"
    14  	"runtime"
    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) {
    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":           &wasmFuncType{Params: []byte{}},                                         //
    58  	"wasm_export_run":        &wasmFuncType{Params: []byte{I32, I32}},                                 // argc, argv
    59  	"wasm_export_resume":     &wasmFuncType{Params: []byte{}},                                         //
    60  	"wasm_export_getsp":      &wasmFuncType{Results: []byte{I32}},                                     // sp
    61  	"wasm_pc_f_loop":         &wasmFuncType{Params: []byte{}},                                         //
    62  	"runtime.wasmMove":       &wasmFuncType{Params: []byte{I32, I32, I32}},                            // dst, src, len
    63  	"runtime.wasmZero":       &wasmFuncType{Params: []byte{I32, I32}},                                 // ptr, len
    64  	"runtime.wasmDiv":        &wasmFuncType{Params: []byte{I64, I64}, Results: []byte{I64}},           // x, y -> x/y
    65  	"runtime.wasmTruncS":     &wasmFuncType{Params: []byte{F64}, Results: []byte{I64}},                // x -> int(x)
    66  	"runtime.wasmTruncU":     &wasmFuncType{Params: []byte{F64}, Results: []byte{I64}},                // x -> uint(x)
    67  	"runtime.gcWriteBarrier": &wasmFuncType{Params: []byte{I64, I64}},                                 // ptr, val
    68  	"cmpbody":                &wasmFuncType{Params: []byte{I64, I64, I64, I64}, Results: []byte{I64}}, // a, alen, b, blen -> -1/0/1
    69  	"memeqbody":              &wasmFuncType{Params: []byte{I64, I64, I64}, Results: []byte{I64}},      // a, b, len -> 0/1
    70  	"memcmp":                 &wasmFuncType{Params: []byte{I32, I32, I32}, Results: []byte{I32}},      // a, b, len -> <0/0/>0
    71  	"memchr":                 &wasmFuncType{Params: []byte{I32, I32, I32}, Results: []byte{I32}},      // s, c, len -> index
    72  }
    73  
    74  func assignAddress(ctxt *ld.Link, sect *sym.Section, n int, s *sym.Symbol, 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  	s.Sect = sect
    90  	s.Value = int64(funcValueOffset+va/ld.MINFUNC) << 16 // va starts at zero
    91  	va += uint64(ld.MINFUNC)
    92  	return sect, n, va
    93  }
    94  
    95  // asmb writes the final WebAssembly module binary.
    96  // Spec: https://webassembly.github.io/spec/core/binary/modules.html
    97  func asmb(ctxt *ld.Link) {
    98  	if ctxt.Debugvlog != 0 {
    99  		ctxt.Logf("%5.2f asmb\n", ld.Cputime())
   100  	}
   101  
   102  	types := []*wasmFuncType{
   103  		// For normal Go functions the return value is
   104  		// 0 if the function returned normally or
   105  		// 1 if the stack needs to be unwound.
   106  		&wasmFuncType{Results: []byte{I32}},
   107  	}
   108  
   109  	// collect host imports (functions that get imported from the WebAssembly host, usually JavaScript)
   110  	hostImports := []*wasmFunc{
   111  		&wasmFunc{
   112  			Name: "debug",
   113  			Type: lookupType(&wasmFuncType{Params: []byte{I32}}, &types),
   114  		},
   115  	}
   116  	hostImportMap := make(map[*sym.Symbol]int64)
   117  	for _, fn := range ctxt.Textp {
   118  		for _, r := range fn.R {
   119  			if r.Type == objabi.R_WASMIMPORT {
   120  				hostImportMap[r.Sym] = int64(len(hostImports))
   121  				hostImports = append(hostImports, &wasmFunc{
   122  					Name: r.Sym.Name,
   123  					Type: lookupType(&wasmFuncType{Params: []byte{I32}}, &types),
   124  				})
   125  			}
   126  		}
   127  	}
   128  
   129  	// collect functions with WebAssembly body
   130  	var buildid []byte
   131  	fns := make([]*wasmFunc, len(ctxt.Textp))
   132  	for i, fn := range ctxt.Textp {
   133  		wfn := new(bytes.Buffer)
   134  		if fn.Name == "go.buildid" {
   135  			writeUleb128(wfn, 0) // number of sets of locals
   136  			writeI32Const(wfn, 0)
   137  			wfn.WriteByte(0x0b) // end
   138  			buildid = fn.P
   139  		} else {
   140  			// Relocations have variable length, handle them here.
   141  			off := int32(0)
   142  			for _, r := range fn.R {
   143  				wfn.Write(fn.P[off:r.Off])
   144  				off = r.Off
   145  				switch r.Type {
   146  				case objabi.R_ADDR:
   147  					writeSleb128(wfn, r.Sym.Value+r.Add)
   148  				case objabi.R_CALL:
   149  					writeSleb128(wfn, int64(len(hostImports))+r.Sym.Value>>16-funcValueOffset)
   150  				case objabi.R_WASMIMPORT:
   151  					writeSleb128(wfn, hostImportMap[r.Sym])
   152  				default:
   153  					ld.Errorf(fn, "bad reloc type %d (%s)", r.Type, sym.RelocName(ctxt.Arch, r.Type))
   154  					continue
   155  				}
   156  			}
   157  			wfn.Write(fn.P[off:])
   158  		}
   159  
   160  		typ := uint32(0)
   161  		if sig, ok := wasmFuncTypes[fn.Name]; ok {
   162  			typ = lookupType(sig, &types)
   163  		}
   164  
   165  		name := nameRegexp.ReplaceAllString(fn.Name, "_")
   166  		fns[i] = &wasmFunc{Name: name, Type: typ, Code: wfn.Bytes()}
   167  	}
   168  
   169  	ctxt.Out.Write([]byte{0x00, 0x61, 0x73, 0x6d}) // magic
   170  	ctxt.Out.Write([]byte{0x01, 0x00, 0x00, 0x00}) // version
   171  
   172  	// Add any buildid early in the binary:
   173  	if len(buildid) != 0 {
   174  		writeBuildID(ctxt, buildid)
   175  	}
   176  
   177  	writeGoVersion(ctxt)
   178  	writeTypeSec(ctxt, types)
   179  	writeImportSec(ctxt, hostImports)
   180  	writeFunctionSec(ctxt, fns)
   181  	writeTableSec(ctxt, fns)
   182  	writeMemorySec(ctxt)
   183  	writeGlobalSec(ctxt)
   184  	writeExportSec(ctxt, len(hostImports))
   185  	writeElementSec(ctxt, uint64(len(hostImports)), uint64(len(fns)))
   186  	writeCodeSec(ctxt, fns)
   187  	writeDataSec(ctxt)
   188  	if !*ld.FlagS {
   189  		writeNameSec(ctxt, len(hostImports), fns)
   190  	}
   191  
   192  	ctxt.Out.Flush()
   193  }
   194  
   195  func lookupType(sig *wasmFuncType, types *[]*wasmFuncType) uint32 {
   196  	for i, t := range *types {
   197  		if bytes.Equal(sig.Params, t.Params) && bytes.Equal(sig.Results, t.Results) {
   198  			return uint32(i)
   199  		}
   200  	}
   201  	*types = append(*types, sig)
   202  	return uint32(len(*types) - 1)
   203  }
   204  
   205  func writeSecHeader(ctxt *ld.Link, id uint8) int64 {
   206  	ctxt.Out.WriteByte(id)
   207  	sizeOffset := ctxt.Out.Offset()
   208  	ctxt.Out.Write(make([]byte, 5)) // placeholder for length
   209  	return sizeOffset
   210  }
   211  
   212  func writeSecSize(ctxt *ld.Link, sizeOffset int64) {
   213  	endOffset := ctxt.Out.Offset()
   214  	ctxt.Out.SeekSet(sizeOffset)
   215  	writeUleb128FixedLength(ctxt.Out, uint64(endOffset-sizeOffset-5), 5)
   216  	ctxt.Out.SeekSet(endOffset)
   217  }
   218  
   219  func writeBuildID(ctxt *ld.Link, buildid []byte) {
   220  	sizeOffset := writeSecHeader(ctxt, sectionCustom)
   221  	writeName(ctxt.Out, "go.buildid")
   222  	ctxt.Out.Write(buildid)
   223  	writeSecSize(ctxt, sizeOffset)
   224  }
   225  
   226  func writeGoVersion(ctxt *ld.Link) {
   227  	sizeOffset := writeSecHeader(ctxt, sectionCustom)
   228  	writeName(ctxt.Out, "go.version")
   229  	ctxt.Out.Write([]byte(runtime.Version()))
   230  	writeSecSize(ctxt, sizeOffset)
   231  }
   232  
   233  // writeTypeSec writes the section that declares all function types
   234  // so they can be referenced by index.
   235  func writeTypeSec(ctxt *ld.Link, types []*wasmFuncType) {
   236  	sizeOffset := writeSecHeader(ctxt, sectionType)
   237  
   238  	writeUleb128(ctxt.Out, uint64(len(types)))
   239  
   240  	for _, t := range types {
   241  		ctxt.Out.WriteByte(0x60) // functype
   242  		writeUleb128(ctxt.Out, uint64(len(t.Params)))
   243  		for _, v := range t.Params {
   244  			ctxt.Out.WriteByte(byte(v))
   245  		}
   246  		writeUleb128(ctxt.Out, uint64(len(t.Results)))
   247  		for _, v := range t.Results {
   248  			ctxt.Out.WriteByte(byte(v))
   249  		}
   250  	}
   251  
   252  	writeSecSize(ctxt, sizeOffset)
   253  }
   254  
   255  // writeImportSec writes the section that lists the functions that get
   256  // imported from the WebAssembly host, usually JavaScript.
   257  func writeImportSec(ctxt *ld.Link, hostImports []*wasmFunc) {
   258  	sizeOffset := writeSecHeader(ctxt, sectionImport)
   259  
   260  	writeUleb128(ctxt.Out, uint64(len(hostImports))) // number of imports
   261  	for _, fn := range hostImports {
   262  		writeName(ctxt.Out, "go") // provided by the import object in wasm_exec.js
   263  		writeName(ctxt.Out, fn.Name)
   264  		ctxt.Out.WriteByte(0x00) // func import
   265  		writeUleb128(ctxt.Out, uint64(fn.Type))
   266  	}
   267  
   268  	writeSecSize(ctxt, sizeOffset)
   269  }
   270  
   271  // writeFunctionSec writes the section that declares the types of functions.
   272  // The bodies of these functions will later be provided in the "code" section.
   273  func writeFunctionSec(ctxt *ld.Link, fns []*wasmFunc) {
   274  	sizeOffset := writeSecHeader(ctxt, sectionFunction)
   275  
   276  	writeUleb128(ctxt.Out, uint64(len(fns)))
   277  	for _, fn := range fns {
   278  		writeUleb128(ctxt.Out, uint64(fn.Type))
   279  	}
   280  
   281  	writeSecSize(ctxt, sizeOffset)
   282  }
   283  
   284  // writeTableSec writes the section that declares tables. Currently there is only a single table
   285  // that is used by the CallIndirect operation to dynamically call any function.
   286  // The contents of the table get initialized by the "element" section.
   287  func writeTableSec(ctxt *ld.Link, fns []*wasmFunc) {
   288  	sizeOffset := writeSecHeader(ctxt, sectionTable)
   289  
   290  	numElements := uint64(funcValueOffset + len(fns))
   291  	writeUleb128(ctxt.Out, 1)           // number of tables
   292  	ctxt.Out.WriteByte(0x70)            // type: anyfunc
   293  	ctxt.Out.WriteByte(0x00)            // no max
   294  	writeUleb128(ctxt.Out, numElements) // min
   295  
   296  	writeSecSize(ctxt, sizeOffset)
   297  }
   298  
   299  // writeMemorySec writes the section that declares linear memories. Currently one linear memory is being used.
   300  func writeMemorySec(ctxt *ld.Link) {
   301  	sizeOffset := writeSecHeader(ctxt, sectionMemory)
   302  
   303  	// Linear memory always starts at address zero.
   304  	// The unit of the sizes is "WebAssembly page size", which is 64Ki.
   305  	// The minimum is currently set to 1GB, which is a lot.
   306  	// More memory can be requested with the grow_memory instruction,
   307  	// but this operation currently is rather slow, so we avoid it for now.
   308  	// TODO(neelance): Use lower initial memory size.
   309  	writeUleb128(ctxt.Out, 1)       // number of memories
   310  	ctxt.Out.WriteByte(0x00)        // no maximum memory size
   311  	writeUleb128(ctxt.Out, 1024*16) // minimum (initial) memory size
   312  
   313  	writeSecSize(ctxt, sizeOffset)
   314  }
   315  
   316  // writeGlobalSec writes the section that declares global variables.
   317  func writeGlobalSec(ctxt *ld.Link) {
   318  	sizeOffset := writeSecHeader(ctxt, sectionGlobal)
   319  
   320  	globalRegs := []byte{
   321  		I32, // 0: PC_F
   322  		I32, // 1: PC_B
   323  		I32, // 2: SP
   324  		I64, // 3: CTXT
   325  		I64, // 4: g
   326  		I64, // 5: RET0
   327  		I64, // 6: RET1
   328  		I64, // 7: RET2
   329  		I64, // 8: RET3
   330  		I32, // 9: PAUSE
   331  	}
   332  
   333  	writeUleb128(ctxt.Out, uint64(len(globalRegs))) // number of globals
   334  
   335  	for _, typ := range globalRegs {
   336  		ctxt.Out.WriteByte(typ)
   337  		ctxt.Out.WriteByte(0x01) // var
   338  		switch typ {
   339  		case I32:
   340  			writeI32Const(ctxt.Out, 0)
   341  		case I64:
   342  			writeI64Const(ctxt.Out, 0)
   343  		}
   344  		ctxt.Out.WriteByte(0x0b) // end
   345  	}
   346  
   347  	writeSecSize(ctxt, sizeOffset)
   348  }
   349  
   350  // writeExportSec writes the section that declares exports.
   351  // Exports can be accessed by the WebAssembly host, usually JavaScript.
   352  // The wasm_export_* functions and the linear memory get exported.
   353  func writeExportSec(ctxt *ld.Link, lenHostImports int) {
   354  	sizeOffset := writeSecHeader(ctxt, sectionExport)
   355  
   356  	writeUleb128(ctxt.Out, 4) // number of exports
   357  
   358  	for _, name := range []string{"run", "resume", "getsp"} {
   359  		idx := uint32(lenHostImports) + uint32(ctxt.Syms.ROLookup("wasm_export_"+name, 0).Value>>16) - funcValueOffset
   360  		writeName(ctxt.Out, name)           // inst.exports.run/resume/getsp in wasm_exec.js
   361  		ctxt.Out.WriteByte(0x00)            // func export
   362  		writeUleb128(ctxt.Out, uint64(idx)) // funcidx
   363  	}
   364  
   365  	writeName(ctxt.Out, "mem") // inst.exports.mem in wasm_exec.js
   366  	ctxt.Out.WriteByte(0x02)   // mem export
   367  	writeUleb128(ctxt.Out, 0)  // memidx
   368  
   369  	writeSecSize(ctxt, sizeOffset)
   370  }
   371  
   372  // writeElementSec writes the section that initializes the tables declared by the "table" section.
   373  // The table for CallIndirect gets initialized in a very simple way so that each table index (PC_F value)
   374  // maps linearly to the function index (numImports + PC_F).
   375  func writeElementSec(ctxt *ld.Link, numImports, numFns uint64) {
   376  	sizeOffset := writeSecHeader(ctxt, sectionElement)
   377  
   378  	writeUleb128(ctxt.Out, 1) // number of element segments
   379  
   380  	writeUleb128(ctxt.Out, 0) // tableidx
   381  	writeI32Const(ctxt.Out, funcValueOffset)
   382  	ctxt.Out.WriteByte(0x0b) // end
   383  
   384  	writeUleb128(ctxt.Out, numFns) // number of entries
   385  	for i := uint64(0); i < numFns; i++ {
   386  		writeUleb128(ctxt.Out, numImports+i)
   387  	}
   388  
   389  	writeSecSize(ctxt, sizeOffset)
   390  }
   391  
   392  // writeElementSec writes the section that provides the function bodies for the functions
   393  // declared by the "func" section.
   394  func writeCodeSec(ctxt *ld.Link, fns []*wasmFunc) {
   395  	sizeOffset := writeSecHeader(ctxt, sectionCode)
   396  
   397  	writeUleb128(ctxt.Out, uint64(len(fns))) // number of code entries
   398  	for _, fn := range fns {
   399  		writeUleb128(ctxt.Out, uint64(len(fn.Code)))
   400  		ctxt.Out.Write(fn.Code)
   401  	}
   402  
   403  	writeSecSize(ctxt, sizeOffset)
   404  }
   405  
   406  // writeDataSec writes the section that provides data that will be used to initialize the linear memory.
   407  func writeDataSec(ctxt *ld.Link) {
   408  	sizeOffset := writeSecHeader(ctxt, sectionData)
   409  
   410  	sections := []*sym.Section{
   411  		ctxt.Syms.Lookup("runtime.rodata", 0).Sect,
   412  		ctxt.Syms.Lookup("runtime.typelink", 0).Sect,
   413  		ctxt.Syms.Lookup("runtime.itablink", 0).Sect,
   414  		ctxt.Syms.Lookup("runtime.symtab", 0).Sect,
   415  		ctxt.Syms.Lookup("runtime.pclntab", 0).Sect,
   416  		ctxt.Syms.Lookup("runtime.noptrdata", 0).Sect,
   417  		ctxt.Syms.Lookup("runtime.data", 0).Sect,
   418  	}
   419  
   420  	writeUleb128(ctxt.Out, uint64(len(sections))) // number of data entries
   421  
   422  	for _, sec := range sections {
   423  		writeUleb128(ctxt.Out, 0) // memidx
   424  		writeI32Const(ctxt.Out, int32(sec.Vaddr))
   425  		ctxt.Out.WriteByte(0x0b) // end
   426  		writeUleb128(ctxt.Out, uint64(sec.Length))
   427  		ld.Datblk(ctxt, int64(sec.Vaddr), int64(sec.Length))
   428  	}
   429  
   430  	writeSecSize(ctxt, sizeOffset)
   431  }
   432  
   433  var nameRegexp = regexp.MustCompile(`[^\w\.]`)
   434  
   435  // writeNameSec writes an optional section that assigns names to the functions declared by the "func" section.
   436  // The names are only used by WebAssembly stack traces, debuggers and decompilers.
   437  // TODO(neelance): add symbol table of DATA symbols
   438  func writeNameSec(ctxt *ld.Link, firstFnIndex int, fns []*wasmFunc) {
   439  	sizeOffset := writeSecHeader(ctxt, sectionCustom)
   440  	writeName(ctxt.Out, "name")
   441  
   442  	sizeOffset2 := writeSecHeader(ctxt, 0x01) // function names
   443  	writeUleb128(ctxt.Out, uint64(len(fns)))
   444  	for i, fn := range fns {
   445  		writeUleb128(ctxt.Out, uint64(firstFnIndex+i))
   446  		writeName(ctxt.Out, fn.Name)
   447  	}
   448  	writeSecSize(ctxt, sizeOffset2)
   449  
   450  	writeSecSize(ctxt, sizeOffset)
   451  }
   452  
   453  type nameWriter interface {
   454  	io.ByteWriter
   455  	io.Writer
   456  }
   457  
   458  func writeI32Const(w io.ByteWriter, v int32) {
   459  	w.WriteByte(0x41) // i32.const
   460  	writeSleb128(w, int64(v))
   461  }
   462  
   463  func writeI64Const(w io.ByteWriter, v int64) {
   464  	w.WriteByte(0x42) // i64.const
   465  	writeSleb128(w, v)
   466  }
   467  
   468  func writeName(w nameWriter, name string) {
   469  	writeUleb128(w, uint64(len(name)))
   470  	w.Write([]byte(name))
   471  }
   472  
   473  func writeUleb128(w io.ByteWriter, v uint64) {
   474  	more := true
   475  	for more {
   476  		c := uint8(v & 0x7f)
   477  		v >>= 7
   478  		more = v != 0
   479  		if more {
   480  			c |= 0x80
   481  		}
   482  		w.WriteByte(c)
   483  	}
   484  }
   485  
   486  func writeUleb128FixedLength(w io.ByteWriter, v uint64, length int) {
   487  	for i := 0; i < length; i++ {
   488  		c := uint8(v & 0x7f)
   489  		v >>= 7
   490  		if i < length-1 {
   491  			c |= 0x80
   492  		}
   493  		w.WriteByte(c)
   494  	}
   495  	if v != 0 {
   496  		panic("writeUleb128FixedLength: length too small")
   497  	}
   498  }
   499  
   500  func writeSleb128(w io.ByteWriter, v int64) {
   501  	more := true
   502  	for more {
   503  		c := uint8(v & 0x7f)
   504  		s := uint8(v & 0x40)
   505  		v >>= 7
   506  		more = !((v == 0 && s == 0) || (v == -1 && s != 0))
   507  		if more {
   508  			c |= 0x80
   509  		}
   510  		w.WriteByte(c)
   511  	}
   512  }