Source file src/pkg/cmd/link/internal/wasm/asm.go
     1	
     2	
     3	
     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	
    40	const funcValueOffset = 0x1000 
    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":           {Params: []byte{}},                                         
    58		"wasm_export_run":        {Params: []byte{I32, I32}},                                 
    59		"wasm_export_resume":     {Params: []byte{}},                                         
    60		"wasm_export_getsp":      {Results: []byte{I32}},                                     
    61		"wasm_pc_f_loop":         {Params: []byte{}},                                         
    62		"runtime.wasmMove":       {Params: []byte{I32, I32, I32}},                            
    63		"runtime.wasmZero":       {Params: []byte{I32, I32}},                                 
    64		"runtime.wasmDiv":        {Params: []byte{I64, I64}, Results: []byte{I64}},           
    65		"runtime.wasmTruncS":     {Params: []byte{F64}, Results: []byte{I64}},                
    66		"runtime.wasmTruncU":     {Params: []byte{F64}, Results: []byte{I64}},                
    67		"runtime.gcWriteBarrier": {Params: []byte{I64, I64}},                                 
    68		"cmpbody":                {Params: []byte{I64, I64, I64, I64}, Results: []byte{I64}}, 
    69		"memeqbody":              {Params: []byte{I64, I64, I64}, Results: []byte{I64}},      
    70		"memcmp":                 {Params: []byte{I32, I32, I32}, Results: []byte{I32}},      
    71		"memchr":                 {Params: []byte{I32, I32, I32}, Results: []byte{I32}},      
    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		
    76		
    77		
    78		
    79		
    80		
    81		
    82		
    83		
    84		
    85		
    86		
    87		
    88		
    89		s.Sect = sect
    90		s.Value = int64(funcValueOffset+va/ld.MINFUNC) << 16 
    91		va += uint64(ld.MINFUNC)
    92		return sect, n, va
    93	}
    94	
    95	func asmb(ctxt *ld.Link) {} 
    96	
    97	
    98	
    99	func asmb2(ctxt *ld.Link) {
   100		if ctxt.Debugvlog != 0 {
   101			ctxt.Logf("%5.2f asmb\n", ld.Cputime())
   102		}
   103	
   104		types := []*wasmFuncType{
   105			
   106			
   107			
   108			
   109			{Params: []byte{I32}, Results: []byte{I32}},
   110		}
   111	
   112		
   113		hostImports := []*wasmFunc{
   114			{
   115				Name: "debug",
   116				Type: lookupType(&wasmFuncType{Params: []byte{I32}}, &types),
   117			},
   118		}
   119		hostImportMap := make(map[*sym.Symbol]int64)
   120		for _, fn := range ctxt.Textp {
   121			for _, r := range fn.R {
   122				if r.Type == objabi.R_WASMIMPORT {
   123					hostImportMap[r.Sym] = int64(len(hostImports))
   124					hostImports = append(hostImports, &wasmFunc{
   125						Name: r.Sym.Name,
   126						Type: lookupType(&wasmFuncType{Params: []byte{I32}}, &types),
   127					})
   128				}
   129			}
   130		}
   131	
   132		
   133		var buildid []byte
   134		fns := make([]*wasmFunc, len(ctxt.Textp))
   135		for i, fn := range ctxt.Textp {
   136			wfn := new(bytes.Buffer)
   137			if fn.Name == "go.buildid" {
   138				writeUleb128(wfn, 0) 
   139				writeI32Const(wfn, 0)
   140				wfn.WriteByte(0x0b) 
   141				buildid = fn.P
   142			} else {
   143				
   144				off := int32(0)
   145				for _, r := range fn.R {
   146					wfn.Write(fn.P[off:r.Off])
   147					off = r.Off
   148					switch r.Type {
   149					case objabi.R_ADDR:
   150						writeSleb128(wfn, r.Sym.Value+r.Add)
   151					case objabi.R_CALL:
   152						writeSleb128(wfn, int64(len(hostImports))+r.Sym.Value>>16-funcValueOffset)
   153					case objabi.R_WASMIMPORT:
   154						writeSleb128(wfn, hostImportMap[r.Sym])
   155					default:
   156						ld.Errorf(fn, "bad reloc type %d (%s)", r.Type, sym.RelocName(ctxt.Arch, r.Type))
   157						continue
   158					}
   159				}
   160				wfn.Write(fn.P[off:])
   161			}
   162	
   163			typ := uint32(0)
   164			if sig, ok := wasmFuncTypes[fn.Name]; ok {
   165				typ = lookupType(sig, &types)
   166			}
   167	
   168			name := nameRegexp.ReplaceAllString(fn.Name, "_")
   169			fns[i] = &wasmFunc{Name: name, Type: typ, Code: wfn.Bytes()}
   170		}
   171	
   172		ctxt.Out.Write([]byte{0x00, 0x61, 0x73, 0x6d}) 
   173		ctxt.Out.Write([]byte{0x01, 0x00, 0x00, 0x00}) 
   174	
   175		
   176		if len(buildid) != 0 {
   177			writeBuildID(ctxt, buildid)
   178		}
   179	
   180		writeGoVersion(ctxt)
   181		writeTypeSec(ctxt, types)
   182		writeImportSec(ctxt, hostImports)
   183		writeFunctionSec(ctxt, fns)
   184		writeTableSec(ctxt, fns)
   185		writeMemorySec(ctxt)
   186		writeGlobalSec(ctxt)
   187		writeExportSec(ctxt, len(hostImports))
   188		writeElementSec(ctxt, uint64(len(hostImports)), uint64(len(fns)))
   189		writeCodeSec(ctxt, fns)
   190		writeDataSec(ctxt)
   191		if !*ld.FlagS {
   192			writeNameSec(ctxt, len(hostImports), fns)
   193		}
   194	
   195		ctxt.Out.Flush()
   196	}
   197	
   198	func lookupType(sig *wasmFuncType, types *[]*wasmFuncType) uint32 {
   199		for i, t := range *types {
   200			if bytes.Equal(sig.Params, t.Params) && bytes.Equal(sig.Results, t.Results) {
   201				return uint32(i)
   202			}
   203		}
   204		*types = append(*types, sig)
   205		return uint32(len(*types) - 1)
   206	}
   207	
   208	func writeSecHeader(ctxt *ld.Link, id uint8) int64 {
   209		ctxt.Out.WriteByte(id)
   210		sizeOffset := ctxt.Out.Offset()
   211		ctxt.Out.Write(make([]byte, 5)) 
   212		return sizeOffset
   213	}
   214	
   215	func writeSecSize(ctxt *ld.Link, sizeOffset int64) {
   216		endOffset := ctxt.Out.Offset()
   217		ctxt.Out.SeekSet(sizeOffset)
   218		writeUleb128FixedLength(ctxt.Out, uint64(endOffset-sizeOffset-5), 5)
   219		ctxt.Out.SeekSet(endOffset)
   220	}
   221	
   222	func writeBuildID(ctxt *ld.Link, buildid []byte) {
   223		sizeOffset := writeSecHeader(ctxt, sectionCustom)
   224		writeName(ctxt.Out, "go.buildid")
   225		ctxt.Out.Write(buildid)
   226		writeSecSize(ctxt, sizeOffset)
   227	}
   228	
   229	func writeGoVersion(ctxt *ld.Link) {
   230		sizeOffset := writeSecHeader(ctxt, sectionCustom)
   231		writeName(ctxt.Out, "go.version")
   232		ctxt.Out.Write([]byte(runtime.Version()))
   233		writeSecSize(ctxt, sizeOffset)
   234	}
   235	
   236	
   237	
   238	func writeTypeSec(ctxt *ld.Link, types []*wasmFuncType) {
   239		sizeOffset := writeSecHeader(ctxt, sectionType)
   240	
   241		writeUleb128(ctxt.Out, uint64(len(types)))
   242	
   243		for _, t := range types {
   244			ctxt.Out.WriteByte(0x60) 
   245			writeUleb128(ctxt.Out, uint64(len(t.Params)))
   246			for _, v := range t.Params {
   247				ctxt.Out.WriteByte(byte(v))
   248			}
   249			writeUleb128(ctxt.Out, uint64(len(t.Results)))
   250			for _, v := range t.Results {
   251				ctxt.Out.WriteByte(byte(v))
   252			}
   253		}
   254	
   255		writeSecSize(ctxt, sizeOffset)
   256	}
   257	
   258	
   259	
   260	func writeImportSec(ctxt *ld.Link, hostImports []*wasmFunc) {
   261		sizeOffset := writeSecHeader(ctxt, sectionImport)
   262	
   263		writeUleb128(ctxt.Out, uint64(len(hostImports))) 
   264		for _, fn := range hostImports {
   265			writeName(ctxt.Out, "go") 
   266			writeName(ctxt.Out, fn.Name)
   267			ctxt.Out.WriteByte(0x00) 
   268			writeUleb128(ctxt.Out, uint64(fn.Type))
   269		}
   270	
   271		writeSecSize(ctxt, sizeOffset)
   272	}
   273	
   274	
   275	
   276	func writeFunctionSec(ctxt *ld.Link, fns []*wasmFunc) {
   277		sizeOffset := writeSecHeader(ctxt, sectionFunction)
   278	
   279		writeUleb128(ctxt.Out, uint64(len(fns)))
   280		for _, fn := range fns {
   281			writeUleb128(ctxt.Out, uint64(fn.Type))
   282		}
   283	
   284		writeSecSize(ctxt, sizeOffset)
   285	}
   286	
   287	
   288	
   289	
   290	func writeTableSec(ctxt *ld.Link, fns []*wasmFunc) {
   291		sizeOffset := writeSecHeader(ctxt, sectionTable)
   292	
   293		numElements := uint64(funcValueOffset + len(fns))
   294		writeUleb128(ctxt.Out, 1)           
   295		ctxt.Out.WriteByte(0x70)            
   296		ctxt.Out.WriteByte(0x00)            
   297		writeUleb128(ctxt.Out, numElements) 
   298	
   299		writeSecSize(ctxt, sizeOffset)
   300	}
   301	
   302	
   303	
   304	func writeMemorySec(ctxt *ld.Link) {
   305		sizeOffset := writeSecHeader(ctxt, sectionMemory)
   306	
   307		const (
   308			initialSize  = 16 << 20 
   309			wasmPageSize = 64 << 10 
   310		)
   311	
   312		writeUleb128(ctxt.Out, 1)                        
   313		ctxt.Out.WriteByte(0x00)                         
   314		writeUleb128(ctxt.Out, initialSize/wasmPageSize) 
   315	
   316		writeSecSize(ctxt, sizeOffset)
   317	}
   318	
   319	
   320	func writeGlobalSec(ctxt *ld.Link) {
   321		sizeOffset := writeSecHeader(ctxt, sectionGlobal)
   322	
   323		globalRegs := []byte{
   324			I32, 
   325			I64, 
   326			I64, 
   327			I64, 
   328			I64, 
   329			I64, 
   330			I64, 
   331			I32, 
   332		}
   333	
   334		writeUleb128(ctxt.Out, uint64(len(globalRegs))) 
   335	
   336		for _, typ := range globalRegs {
   337			ctxt.Out.WriteByte(typ)
   338			ctxt.Out.WriteByte(0x01) 
   339			switch typ {
   340			case I32:
   341				writeI32Const(ctxt.Out, 0)
   342			case I64:
   343				writeI64Const(ctxt.Out, 0)
   344			}
   345			ctxt.Out.WriteByte(0x0b) 
   346		}
   347	
   348		writeSecSize(ctxt, sizeOffset)
   349	}
   350	
   351	
   352	
   353	
   354	func writeExportSec(ctxt *ld.Link, lenHostImports int) {
   355		sizeOffset := writeSecHeader(ctxt, sectionExport)
   356	
   357		writeUleb128(ctxt.Out, 4) 
   358	
   359		for _, name := range []string{"run", "resume", "getsp"} {
   360			idx := uint32(lenHostImports) + uint32(ctxt.Syms.ROLookup("wasm_export_"+name, 0).Value>>16) - funcValueOffset
   361			writeName(ctxt.Out, name)           
   362			ctxt.Out.WriteByte(0x00)            
   363			writeUleb128(ctxt.Out, uint64(idx)) 
   364		}
   365	
   366		writeName(ctxt.Out, "mem") 
   367		ctxt.Out.WriteByte(0x02)   
   368		writeUleb128(ctxt.Out, 0)  
   369	
   370		writeSecSize(ctxt, sizeOffset)
   371	}
   372	
   373	
   374	
   375	
   376	func writeElementSec(ctxt *ld.Link, numImports, numFns uint64) {
   377		sizeOffset := writeSecHeader(ctxt, sectionElement)
   378	
   379		writeUleb128(ctxt.Out, 1) 
   380	
   381		writeUleb128(ctxt.Out, 0) 
   382		writeI32Const(ctxt.Out, funcValueOffset)
   383		ctxt.Out.WriteByte(0x0b) 
   384	
   385		writeUleb128(ctxt.Out, numFns) 
   386		for i := uint64(0); i < numFns; i++ {
   387			writeUleb128(ctxt.Out, numImports+i)
   388		}
   389	
   390		writeSecSize(ctxt, sizeOffset)
   391	}
   392	
   393	
   394	
   395	func writeCodeSec(ctxt *ld.Link, fns []*wasmFunc) {
   396		sizeOffset := writeSecHeader(ctxt, sectionCode)
   397	
   398		writeUleb128(ctxt.Out, uint64(len(fns))) 
   399		for _, fn := range fns {
   400			writeUleb128(ctxt.Out, uint64(len(fn.Code)))
   401			ctxt.Out.Write(fn.Code)
   402		}
   403	
   404		writeSecSize(ctxt, sizeOffset)
   405	}
   406	
   407	
   408	func writeDataSec(ctxt *ld.Link) {
   409		sizeOffset := writeSecHeader(ctxt, sectionData)
   410	
   411		sections := []*sym.Section{
   412			ctxt.Syms.Lookup("runtime.rodata", 0).Sect,
   413			ctxt.Syms.Lookup("runtime.typelink", 0).Sect,
   414			ctxt.Syms.Lookup("runtime.itablink", 0).Sect,
   415			ctxt.Syms.Lookup("runtime.symtab", 0).Sect,
   416			ctxt.Syms.Lookup("runtime.pclntab", 0).Sect,
   417			ctxt.Syms.Lookup("runtime.noptrdata", 0).Sect,
   418			ctxt.Syms.Lookup("runtime.data", 0).Sect,
   419		}
   420	
   421		type dataSegment struct {
   422			offset int32
   423			data   []byte
   424		}
   425	
   426		
   427		
   428		
   429		const segmentOverhead = 8
   430	
   431		
   432		const maxNumSegments = 100000
   433	
   434		var segments []*dataSegment
   435		for secIndex, sec := range sections {
   436			data := ld.DatblkBytes(ctxt, int64(sec.Vaddr), int64(sec.Length))
   437			offset := int32(sec.Vaddr)
   438	
   439			
   440			for len(data) > 0 && data[0] == 0 {
   441				data = data[1:]
   442				offset++
   443			}
   444	
   445			for len(data) > 0 {
   446				dataLen := int32(len(data))
   447				var segmentEnd, zeroEnd int32
   448				if len(segments)+(len(sections)-secIndex) == maxNumSegments {
   449					segmentEnd = dataLen
   450					zeroEnd = dataLen
   451				} else {
   452					for {
   453						
   454						for segmentEnd < dataLen && data[segmentEnd] != 0 {
   455							segmentEnd++
   456						}
   457						
   458						zeroEnd = segmentEnd
   459						for zeroEnd < dataLen && data[zeroEnd] == 0 {
   460							zeroEnd++
   461						}
   462						
   463						if zeroEnd-segmentEnd >= segmentOverhead || zeroEnd == dataLen {
   464							break
   465						}
   466						segmentEnd = zeroEnd
   467					}
   468				}
   469	
   470				segments = append(segments, &dataSegment{
   471					offset: offset,
   472					data:   data[:segmentEnd],
   473				})
   474				data = data[zeroEnd:]
   475				offset += zeroEnd
   476			}
   477		}
   478	
   479		writeUleb128(ctxt.Out, uint64(len(segments))) 
   480		for _, seg := range segments {
   481			writeUleb128(ctxt.Out, 0) 
   482			writeI32Const(ctxt.Out, seg.offset)
   483			ctxt.Out.WriteByte(0x0b) 
   484			writeUleb128(ctxt.Out, uint64(len(seg.data)))
   485			ctxt.Out.Write(seg.data)
   486		}
   487	
   488		writeSecSize(ctxt, sizeOffset)
   489	}
   490	
   491	var nameRegexp = regexp.MustCompile(`[^\w\.]`)
   492	
   493	
   494	
   495	
   496	func writeNameSec(ctxt *ld.Link, firstFnIndex int, fns []*wasmFunc) {
   497		sizeOffset := writeSecHeader(ctxt, sectionCustom)
   498		writeName(ctxt.Out, "name")
   499	
   500		sizeOffset2 := writeSecHeader(ctxt, 0x01) 
   501		writeUleb128(ctxt.Out, uint64(len(fns)))
   502		for i, fn := range fns {
   503			writeUleb128(ctxt.Out, uint64(firstFnIndex+i))
   504			writeName(ctxt.Out, fn.Name)
   505		}
   506		writeSecSize(ctxt, sizeOffset2)
   507	
   508		writeSecSize(ctxt, sizeOffset)
   509	}
   510	
   511	type nameWriter interface {
   512		io.ByteWriter
   513		io.Writer
   514	}
   515	
   516	func writeI32Const(w io.ByteWriter, v int32) {
   517		w.WriteByte(0x41) 
   518		writeSleb128(w, int64(v))
   519	}
   520	
   521	func writeI64Const(w io.ByteWriter, v int64) {
   522		w.WriteByte(0x42) 
   523		writeSleb128(w, v)
   524	}
   525	
   526	func writeName(w nameWriter, name string) {
   527		writeUleb128(w, uint64(len(name)))
   528		w.Write([]byte(name))
   529	}
   530	
   531	func writeUleb128(w io.ByteWriter, v uint64) {
   532		more := true
   533		for more {
   534			c := uint8(v & 0x7f)
   535			v >>= 7
   536			more = v != 0
   537			if more {
   538				c |= 0x80
   539			}
   540			w.WriteByte(c)
   541		}
   542	}
   543	
   544	func writeUleb128FixedLength(w io.ByteWriter, v uint64, length int) {
   545		for i := 0; i < length; i++ {
   546			c := uint8(v & 0x7f)
   547			v >>= 7
   548			if i < length-1 {
   549				c |= 0x80
   550			}
   551			w.WriteByte(c)
   552		}
   553		if v != 0 {
   554			panic("writeUleb128FixedLength: length too small")
   555		}
   556	}
   557	
   558	func writeSleb128(w io.ByteWriter, v int64) {
   559		more := true
   560		for more {
   561			c := uint8(v & 0x7f)
   562			s := uint8(v & 0x40)
   563			v >>= 7
   564			more = !((v == 0 && s == 0) || (v == -1 && s != 0))
   565			if more {
   566				c |= 0x80
   567			}
   568			w.WriteByte(c)
   569		}
   570	}
   571	
View as plain text