mirror of
https://github.com/golang/go
synced 2024-11-11 22:30:21 -07:00
cmd/link: add wasm architecture
This commit adds the wasm architecture to the link command. Design doc: https://docs.google.com/document/d/131vjr4DH6JFnb-blm_uRdaC0_Nv3OUwjEY5qVCxCup4 Updates #18892 Change-Id: I5aef29954984537f2979679b5d393209e462f564 Reviewed-on: https://go-review.googlesource.com/103795 Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Cherry Zhang <cherryyz@google.com>
This commit is contained in:
parent
406886b51f
commit
f41dc711d8
@ -145,7 +145,7 @@ async function compile(source) {
|
|||||||
|
|
||||||
async function run() {
|
async function run() {
|
||||||
let importObject = {
|
let importObject = {
|
||||||
js: {
|
go: {
|
||||||
// func wasmexit(code int32)
|
// func wasmexit(code int32)
|
||||||
"runtime.wasmexit": function (sp) {
|
"runtime.wasmexit": function (sp) {
|
||||||
process.exit(mem().getInt32(sp + 8, true));
|
process.exit(mem().getInt32(sp + 8, true));
|
||||||
|
2
src/cmd/dist/build.go
vendored
2
src/cmd/dist/build.go
vendored
@ -68,12 +68,14 @@ var okgoarch = []string{
|
|||||||
"ppc64",
|
"ppc64",
|
||||||
"ppc64le",
|
"ppc64le",
|
||||||
"s390x",
|
"s390x",
|
||||||
|
"wasm",
|
||||||
}
|
}
|
||||||
|
|
||||||
// The known operating systems.
|
// The known operating systems.
|
||||||
var okgoos = []string{
|
var okgoos = []string{
|
||||||
"darwin",
|
"darwin",
|
||||||
"dragonfly",
|
"dragonfly",
|
||||||
|
"js",
|
||||||
"linux",
|
"linux",
|
||||||
"android",
|
"android",
|
||||||
"solaris",
|
"solaris",
|
||||||
|
1
src/cmd/dist/buildtool.go
vendored
1
src/cmd/dist/buildtool.go
vendored
@ -80,6 +80,7 @@ var bootstrapDirs = []string{
|
|||||||
"cmd/link/internal/s390x",
|
"cmd/link/internal/s390x",
|
||||||
"cmd/link/internal/sym",
|
"cmd/link/internal/sym",
|
||||||
"cmd/link/internal/x86",
|
"cmd/link/internal/x86",
|
||||||
|
"cmd/link/internal/wasm",
|
||||||
"container/heap",
|
"container/heap",
|
||||||
"debug/dwarf",
|
"debug/dwarf",
|
||||||
"debug/elf",
|
"debug/elf",
|
||||||
|
@ -1852,6 +1852,10 @@ func (ctxt *Link) textaddress() {
|
|||||||
// Note: once we have trampoline insertion support for external linking, this function
|
// Note: once we have trampoline insertion support for external linking, this function
|
||||||
// will not need to create new text sections, and so no need to return sect and n.
|
// will not need to create new text sections, and so no need to return sect and n.
|
||||||
func assignAddress(ctxt *Link, sect *sym.Section, n int, s *sym.Symbol, va uint64, isTramp bool) (*sym.Section, int, uint64) {
|
func assignAddress(ctxt *Link, sect *sym.Section, n int, s *sym.Symbol, va uint64, isTramp bool) (*sym.Section, int, uint64) {
|
||||||
|
if thearch.AssignAddress != nil {
|
||||||
|
return thearch.AssignAddress(ctxt, sect, n, s, va, isTramp)
|
||||||
|
}
|
||||||
|
|
||||||
s.Sect = sect
|
s.Sect = sect
|
||||||
if s.Attr.SubSymbol() {
|
if s.Attr.SubSymbol() {
|
||||||
return sect, n, va
|
return sect, n, va
|
||||||
|
@ -1675,7 +1675,7 @@ func dwarfgeneratedebugsyms(ctxt *Link) {
|
|||||||
if *FlagS && ctxt.HeadType != objabi.Hdarwin {
|
if *FlagS && ctxt.HeadType != objabi.Hdarwin {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if ctxt.HeadType == objabi.Hplan9 {
|
if ctxt.HeadType == objabi.Hplan9 || ctxt.HeadType == objabi.Hjs {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,6 +121,9 @@ type Arch struct {
|
|||||||
// symbol in an executable, which is typical when internally
|
// symbol in an executable, which is typical when internally
|
||||||
// linking PIE binaries.
|
// linking PIE binaries.
|
||||||
TLSIEtoLE func(s *sym.Symbol, off, size int)
|
TLSIEtoLE func(s *sym.Symbol, off, size int)
|
||||||
|
|
||||||
|
// optional override for assignAddress
|
||||||
|
AssignAddress func(ctxt *Link, sect *sym.Section, n int, s *sym.Symbol, va uint64, isTramp bool) (*sym.Section, int, uint64)
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -60,6 +60,12 @@ func (out *OutBuf) Write8(v uint8) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WriteByte is an alias for Write8 to fulfill the io.ByteWriter interface.
|
||||||
|
func (out *OutBuf) WriteByte(v byte) error {
|
||||||
|
out.Write8(v)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (out *OutBuf) Write16(v uint16) {
|
func (out *OutBuf) Write16(v uint16) {
|
||||||
out.arch.ByteOrder.PutUint16(out.encbuf[:], v)
|
out.arch.ByteOrder.PutUint16(out.encbuf[:], v)
|
||||||
out.Write(out.encbuf[:2])
|
out.Write(out.encbuf[:2])
|
||||||
|
@ -66,7 +66,7 @@ func (ctxt *Link) computeTLSOffset() {
|
|||||||
default:
|
default:
|
||||||
log.Fatalf("unknown thread-local storage offset for %v", ctxt.HeadType)
|
log.Fatalf("unknown thread-local storage offset for %v", ctxt.HeadType)
|
||||||
|
|
||||||
case objabi.Hplan9, objabi.Hwindows:
|
case objabi.Hplan9, objabi.Hwindows, objabi.Hjs:
|
||||||
break
|
break
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
485
src/cmd/link/internal/wasm/asm.go
Normal file
485
src/cmd/link/internal/wasm/asm.go
Normal file
@ -0,0 +1,485 @@
|
|||||||
|
// Copyright 2018 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package wasm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"cmd/internal/objabi"
|
||||||
|
"cmd/link/internal/ld"
|
||||||
|
"cmd/link/internal/sym"
|
||||||
|
"io"
|
||||||
|
"regexp"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
I32 = 0x7F
|
||||||
|
I64 = 0x7E
|
||||||
|
F32 = 0x7D
|
||||||
|
F64 = 0x7C
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
sectionCustom = 0
|
||||||
|
sectionType = 1
|
||||||
|
sectionImport = 2
|
||||||
|
sectionFunction = 3
|
||||||
|
sectionTable = 4
|
||||||
|
sectionMemory = 5
|
||||||
|
sectionGlobal = 6
|
||||||
|
sectionExport = 7
|
||||||
|
sectionStart = 8
|
||||||
|
sectionElement = 9
|
||||||
|
sectionCode = 10
|
||||||
|
sectionData = 11
|
||||||
|
)
|
||||||
|
|
||||||
|
// funcValueOffset is the offset between the PC_F value of a function and the index of the function in WebAssembly
|
||||||
|
const funcValueOffset = 0x1000 // TODO(neelance): make function addresses play nice with heap addresses
|
||||||
|
|
||||||
|
func gentext(ctxt *ld.Link) {
|
||||||
|
}
|
||||||
|
|
||||||
|
type wasmFunc struct {
|
||||||
|
Name string
|
||||||
|
Type uint32
|
||||||
|
Code []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type wasmFuncType struct {
|
||||||
|
Params []byte
|
||||||
|
Results []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
var wasmFuncTypes = map[string]*wasmFuncType{
|
||||||
|
"_rt0_wasm_js": &wasmFuncType{Params: []byte{I32, I32}}, // argc, argv
|
||||||
|
"runtime.wasmMove": &wasmFuncType{Params: []byte{I32, I32, I32}}, // dst, src, len
|
||||||
|
"runtime.wasmZero": &wasmFuncType{Params: []byte{I32, I32}}, // ptr, len
|
||||||
|
"runtime.wasmDiv": &wasmFuncType{Params: []byte{I64, I64}, Results: []byte{I64}}, // x, y -> x/y
|
||||||
|
"runtime.wasmTruncS": &wasmFuncType{Params: []byte{F64}, Results: []byte{I64}}, // x -> int(x)
|
||||||
|
"runtime.wasmTruncU": &wasmFuncType{Params: []byte{F64}, Results: []byte{I64}}, // x -> uint(x)
|
||||||
|
"runtime.gcWriteBarrier": &wasmFuncType{Params: []byte{I64, I64}}, // ptr, val
|
||||||
|
"cmpbody": &wasmFuncType{Params: []byte{I64, I64, I64, I64}, Results: []byte{I64}}, // a, alen, b, blen -> -1/0/1
|
||||||
|
"memeqbody": &wasmFuncType{Params: []byte{I64, I64, I64}, Results: []byte{I64}}, // a, b, len -> 0/1
|
||||||
|
"memcmp": &wasmFuncType{Params: []byte{I32, I32, I32}, Results: []byte{I32}}, // a, b, len -> <0/0/>0
|
||||||
|
"memchr": &wasmFuncType{Params: []byte{I32, I32, I32}, Results: []byte{I32}}, // s, c, len -> index
|
||||||
|
}
|
||||||
|
|
||||||
|
func assignAddress(ctxt *ld.Link, sect *sym.Section, n int, s *sym.Symbol, va uint64, isTramp bool) (*sym.Section, int, uint64) {
|
||||||
|
// WebAssembly functions do not live in the same address space as the linear memory.
|
||||||
|
// Instead, WebAssembly automatically assigns indices. Imported functions (section "import")
|
||||||
|
// have indices 0 to n. They are followed by native functions (sections "function" and "code")
|
||||||
|
// with indices n+1 and following.
|
||||||
|
//
|
||||||
|
// The following rules describe how wasm handles function indices and addresses:
|
||||||
|
// PC_F = funcValueOffset + WebAssembly function index (not including the imports)
|
||||||
|
// s.Value = PC = PC_F<<16 + PC_B
|
||||||
|
//
|
||||||
|
// The funcValueOffset is necessary to avoid conflicts with expectations
|
||||||
|
// that the Go runtime has about function addresses.
|
||||||
|
// The field "s.Value" corresponds to the concept of PC at runtime.
|
||||||
|
// However, there is no PC register, only PC_F and PC_B. PC_F denotes the function,
|
||||||
|
// PC_B the resume point inside of that function. The entry of the function has PC_B = 0.
|
||||||
|
s.Sect = sect
|
||||||
|
s.Value = int64(funcValueOffset+va/ld.MINFUNC) << 16 // va starts at zero
|
||||||
|
va += uint64(ld.MINFUNC)
|
||||||
|
return sect, n, va
|
||||||
|
}
|
||||||
|
|
||||||
|
// asmb writes the final WebAssembly module binary.
|
||||||
|
// Spec: http://webassembly.github.io/spec/core/binary/modules.html
|
||||||
|
func asmb(ctxt *ld.Link) {
|
||||||
|
if ctxt.Debugvlog != 0 {
|
||||||
|
ctxt.Logf("%5.2f asmb\n", ld.Cputime())
|
||||||
|
}
|
||||||
|
|
||||||
|
types := []*wasmFuncType{
|
||||||
|
// For normal Go functions the return value is
|
||||||
|
// 0 if the function returned normally or
|
||||||
|
// 1 if the stack needs to be unwound.
|
||||||
|
&wasmFuncType{Results: []byte{I32}},
|
||||||
|
}
|
||||||
|
|
||||||
|
// collect host imports (functions that get imported from the WebAssembly host, usually JavaScript)
|
||||||
|
hostImports := []*wasmFunc{
|
||||||
|
&wasmFunc{
|
||||||
|
Name: "debug",
|
||||||
|
Type: lookupType(&wasmFuncType{Params: []byte{I32}}, &types),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
hostImportMap := make(map[*sym.Symbol]int64)
|
||||||
|
for _, fn := range ctxt.Textp {
|
||||||
|
for _, r := range fn.R {
|
||||||
|
if r.Type == objabi.R_WASMIMPORT {
|
||||||
|
hostImportMap[r.Sym] = int64(len(hostImports))
|
||||||
|
hostImports = append(hostImports, &wasmFunc{
|
||||||
|
Name: r.Sym.Name,
|
||||||
|
Type: lookupType(&wasmFuncType{Params: []byte{I32}}, &types),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// collect functions with WebAssembly body
|
||||||
|
fns := make([]*wasmFunc, len(ctxt.Textp))
|
||||||
|
for i, fn := range ctxt.Textp {
|
||||||
|
wfn := new(bytes.Buffer)
|
||||||
|
if fn.Name == "go.buildid" {
|
||||||
|
writeUleb128(wfn, 0) // number of sets of locals
|
||||||
|
writeI32Const(wfn, 0)
|
||||||
|
wfn.WriteByte(0x0b) // end
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Relocations have variable length, handle them here.
|
||||||
|
off := int32(0)
|
||||||
|
for _, r := range fn.R {
|
||||||
|
wfn.Write(fn.P[off:r.Off])
|
||||||
|
off = r.Off
|
||||||
|
switch r.Type {
|
||||||
|
case objabi.R_ADDR:
|
||||||
|
writeSleb128(wfn, r.Sym.Value+r.Add)
|
||||||
|
case objabi.R_CALL:
|
||||||
|
writeSleb128(wfn, int64(len(hostImports))+r.Sym.Value>>16-funcValueOffset)
|
||||||
|
case objabi.R_WASMIMPORT:
|
||||||
|
writeSleb128(wfn, hostImportMap[r.Sym])
|
||||||
|
default:
|
||||||
|
ld.Errorf(fn, "bad reloc type %d (%s)", r.Type, sym.RelocName(ctxt.Arch, r.Type))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wfn.Write(fn.P[off:])
|
||||||
|
}
|
||||||
|
|
||||||
|
typ := uint32(0)
|
||||||
|
if sig, ok := wasmFuncTypes[fn.Name]; ok {
|
||||||
|
typ = lookupType(sig, &types)
|
||||||
|
}
|
||||||
|
|
||||||
|
name := nameRegexp.ReplaceAllString(fn.Name, "_")
|
||||||
|
fns[i] = &wasmFunc{Name: name, Type: typ, Code: wfn.Bytes()}
|
||||||
|
}
|
||||||
|
|
||||||
|
// look up program entry point
|
||||||
|
rt0 := uint32(len(hostImports)) + uint32(ctxt.Syms.ROLookup("_rt0_wasm_js", 0).Value>>16) - funcValueOffset
|
||||||
|
|
||||||
|
ctxt.Out.Write([]byte{0x00, 0x61, 0x73, 0x6d}) // magic
|
||||||
|
ctxt.Out.Write([]byte{0x01, 0x00, 0x00, 0x00}) // version
|
||||||
|
|
||||||
|
writeTypeSec(ctxt, types)
|
||||||
|
writeImportSec(ctxt, hostImports)
|
||||||
|
writeFunctionSec(ctxt, fns)
|
||||||
|
writeTableSec(ctxt, fns)
|
||||||
|
writeMemorySec(ctxt)
|
||||||
|
writeGlobalSec(ctxt)
|
||||||
|
writeExportSec(ctxt, rt0)
|
||||||
|
writeElementSec(ctxt, uint64(len(hostImports)), uint64(len(fns)))
|
||||||
|
writeCodeSec(ctxt, fns)
|
||||||
|
writeDataSec(ctxt)
|
||||||
|
if !*ld.FlagS {
|
||||||
|
writeNameSec(ctxt, append(hostImports, fns...))
|
||||||
|
}
|
||||||
|
|
||||||
|
ctxt.Out.Flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupType(sig *wasmFuncType, types *[]*wasmFuncType) uint32 {
|
||||||
|
for i, t := range *types {
|
||||||
|
if bytes.Equal(sig.Params, t.Params) && bytes.Equal(sig.Results, t.Results) {
|
||||||
|
return uint32(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*types = append(*types, sig)
|
||||||
|
return uint32(len(*types) - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeSecHeader(ctxt *ld.Link, id uint8) int64 {
|
||||||
|
ctxt.Out.WriteByte(id)
|
||||||
|
sizeOffset := ctxt.Out.Offset()
|
||||||
|
ctxt.Out.Write(make([]byte, 5)) // placeholder for length
|
||||||
|
return sizeOffset
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeSecSize(ctxt *ld.Link, sizeOffset int64) {
|
||||||
|
endOffset := ctxt.Out.Offset()
|
||||||
|
ctxt.Out.SeekSet(sizeOffset)
|
||||||
|
writeUleb128FixedLength(ctxt.Out, uint64(endOffset-sizeOffset-5), 5)
|
||||||
|
ctxt.Out.SeekSet(endOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeTypeSec writes the section that declares all function types
|
||||||
|
// so they can be referenced by index.
|
||||||
|
func writeTypeSec(ctxt *ld.Link, types []*wasmFuncType) {
|
||||||
|
sizeOffset := writeSecHeader(ctxt, sectionType)
|
||||||
|
|
||||||
|
writeUleb128(ctxt.Out, uint64(len(types)))
|
||||||
|
|
||||||
|
for _, t := range types {
|
||||||
|
ctxt.Out.WriteByte(0x60) // functype
|
||||||
|
writeUleb128(ctxt.Out, uint64(len(t.Params)))
|
||||||
|
for _, v := range t.Params {
|
||||||
|
ctxt.Out.WriteByte(byte(v))
|
||||||
|
}
|
||||||
|
writeUleb128(ctxt.Out, uint64(len(t.Results)))
|
||||||
|
for _, v := range t.Results {
|
||||||
|
ctxt.Out.WriteByte(byte(v))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
writeSecSize(ctxt, sizeOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeImportSec writes the section that lists the functions that get
|
||||||
|
// imported from the WebAssembly host, usually JavaScript.
|
||||||
|
func writeImportSec(ctxt *ld.Link, hostImports []*wasmFunc) {
|
||||||
|
sizeOffset := writeSecHeader(ctxt, sectionImport)
|
||||||
|
|
||||||
|
writeUleb128(ctxt.Out, uint64(len(hostImports))) // number of imports
|
||||||
|
for _, fn := range hostImports {
|
||||||
|
writeName(ctxt.Out, "go") // provided by the import object in wasm_exec.js
|
||||||
|
writeName(ctxt.Out, fn.Name)
|
||||||
|
ctxt.Out.WriteByte(0x00) // func import
|
||||||
|
writeUleb128(ctxt.Out, uint64(fn.Type))
|
||||||
|
}
|
||||||
|
|
||||||
|
writeSecSize(ctxt, sizeOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeFunctionSec writes the section that declares the types of functions.
|
||||||
|
// The bodies of these functions will later be provided in the "code" section.
|
||||||
|
func writeFunctionSec(ctxt *ld.Link, fns []*wasmFunc) {
|
||||||
|
sizeOffset := writeSecHeader(ctxt, sectionFunction)
|
||||||
|
|
||||||
|
writeUleb128(ctxt.Out, uint64(len(fns)))
|
||||||
|
for _, fn := range fns {
|
||||||
|
writeUleb128(ctxt.Out, uint64(fn.Type))
|
||||||
|
}
|
||||||
|
|
||||||
|
writeSecSize(ctxt, sizeOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeTableSec writes the section that declares tables. Currently there is only a single table
|
||||||
|
// that is used by the CallIndirect operation to dynamically call any function.
|
||||||
|
// The contents of the table get initialized by the "element" section.
|
||||||
|
func writeTableSec(ctxt *ld.Link, fns []*wasmFunc) {
|
||||||
|
sizeOffset := writeSecHeader(ctxt, sectionTable)
|
||||||
|
|
||||||
|
numElements := uint64(funcValueOffset + len(fns))
|
||||||
|
writeUleb128(ctxt.Out, 1) // number of tables
|
||||||
|
ctxt.Out.WriteByte(0x70) // type: anyfunc
|
||||||
|
ctxt.Out.WriteByte(0x00) // no max
|
||||||
|
writeUleb128(ctxt.Out, numElements) // min
|
||||||
|
|
||||||
|
writeSecSize(ctxt, sizeOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeMemorySec writes the section that declares linear memories. Currently one linear memory is being used.
|
||||||
|
func writeMemorySec(ctxt *ld.Link) {
|
||||||
|
sizeOffset := writeSecHeader(ctxt, sectionMemory)
|
||||||
|
|
||||||
|
// Linear memory always starts at address zero.
|
||||||
|
// The unit of the sizes is "WebAssembly page size", which is 64Ki.
|
||||||
|
// The minimum is currently set to 1GB, which is a lot.
|
||||||
|
// More memory can be requested with the grow_memory instruction,
|
||||||
|
// but this operation currently is rather slow, so we avoid it for now.
|
||||||
|
// TODO(neelance): Use lower initial memory size.
|
||||||
|
writeUleb128(ctxt.Out, 1) // number of memories
|
||||||
|
ctxt.Out.WriteByte(0x00) // no maximum memory size
|
||||||
|
writeUleb128(ctxt.Out, 1024*16) // minimum (initial) memory size
|
||||||
|
|
||||||
|
writeSecSize(ctxt, sizeOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeGlobalSec writes the section that declares global variables.
|
||||||
|
func writeGlobalSec(ctxt *ld.Link) {
|
||||||
|
sizeOffset := writeSecHeader(ctxt, sectionGlobal)
|
||||||
|
|
||||||
|
globalRegs := []byte{
|
||||||
|
I32, // 0: PC_F
|
||||||
|
I32, // 1: PC_B
|
||||||
|
I32, // 2: SP
|
||||||
|
I64, // 3: CTXT
|
||||||
|
I64, // 4: g
|
||||||
|
I64, // 5: RET0
|
||||||
|
I64, // 6: RET1
|
||||||
|
I64, // 7: RET2
|
||||||
|
I64, // 8: RET3
|
||||||
|
}
|
||||||
|
|
||||||
|
writeUleb128(ctxt.Out, uint64(len(globalRegs))) // number of globals
|
||||||
|
|
||||||
|
for _, typ := range globalRegs {
|
||||||
|
ctxt.Out.WriteByte(typ)
|
||||||
|
ctxt.Out.WriteByte(0x01) // var
|
||||||
|
switch typ {
|
||||||
|
case I32:
|
||||||
|
writeI32Const(ctxt.Out, 0)
|
||||||
|
case I64:
|
||||||
|
writeI64Const(ctxt.Out, 0)
|
||||||
|
}
|
||||||
|
ctxt.Out.WriteByte(0x0b) // end
|
||||||
|
}
|
||||||
|
|
||||||
|
writeSecSize(ctxt, sizeOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeExportSec writes the section that declares exports.
|
||||||
|
// Exports can be accessed by the WebAssembly host, usually JavaScript.
|
||||||
|
// Currently _rt0_wasm_js (program entry point) and the linear memory get exported.
|
||||||
|
func writeExportSec(ctxt *ld.Link, rt0 uint32) {
|
||||||
|
sizeOffset := writeSecHeader(ctxt, sectionExport)
|
||||||
|
|
||||||
|
writeUleb128(ctxt.Out, 2) // number of exports
|
||||||
|
|
||||||
|
writeName(ctxt.Out, "run") // inst.exports.run in wasm_exec.js
|
||||||
|
ctxt.Out.WriteByte(0x00) // func export
|
||||||
|
writeUleb128(ctxt.Out, uint64(rt0)) // funcidx
|
||||||
|
|
||||||
|
writeName(ctxt.Out, "mem") // inst.exports.mem in wasm_exec.js
|
||||||
|
ctxt.Out.WriteByte(0x02) // mem export
|
||||||
|
writeUleb128(ctxt.Out, 0) // memidx
|
||||||
|
|
||||||
|
writeSecSize(ctxt, sizeOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeElementSec writes the section that initializes the tables declared by the "table" section.
|
||||||
|
// The table for CallIndirect gets initialized in a very simple way so that each table index (PC_F value)
|
||||||
|
// maps linearly to the function index (numImports + PC_F).
|
||||||
|
func writeElementSec(ctxt *ld.Link, numImports, numFns uint64) {
|
||||||
|
sizeOffset := writeSecHeader(ctxt, sectionElement)
|
||||||
|
|
||||||
|
writeUleb128(ctxt.Out, 1) // number of element segments
|
||||||
|
|
||||||
|
writeUleb128(ctxt.Out, 0) // tableidx
|
||||||
|
writeI32Const(ctxt.Out, funcValueOffset)
|
||||||
|
ctxt.Out.WriteByte(0x0b) // end
|
||||||
|
|
||||||
|
writeUleb128(ctxt.Out, numFns) // number of entries
|
||||||
|
for i := uint64(0); i < numFns; i++ {
|
||||||
|
writeUleb128(ctxt.Out, numImports+i)
|
||||||
|
}
|
||||||
|
|
||||||
|
writeSecSize(ctxt, sizeOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeElementSec writes the section that provides the function bodies for the functions
|
||||||
|
// declared by the "func" section.
|
||||||
|
func writeCodeSec(ctxt *ld.Link, fns []*wasmFunc) {
|
||||||
|
sizeOffset := writeSecHeader(ctxt, sectionCode)
|
||||||
|
|
||||||
|
writeUleb128(ctxt.Out, uint64(len(fns))) // number of code entries
|
||||||
|
for _, fn := range fns {
|
||||||
|
writeUleb128(ctxt.Out, uint64(len(fn.Code)))
|
||||||
|
ctxt.Out.Write(fn.Code)
|
||||||
|
}
|
||||||
|
|
||||||
|
writeSecSize(ctxt, sizeOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// writeDataSec writes the section that provides data that will be used to initialize the linear memory.
|
||||||
|
func writeDataSec(ctxt *ld.Link) {
|
||||||
|
sizeOffset := writeSecHeader(ctxt, sectionData)
|
||||||
|
|
||||||
|
sections := []*sym.Section{
|
||||||
|
ctxt.Syms.Lookup("runtime.rodata", 0).Sect,
|
||||||
|
ctxt.Syms.Lookup("runtime.typelink", 0).Sect,
|
||||||
|
ctxt.Syms.Lookup("runtime.itablink", 0).Sect,
|
||||||
|
ctxt.Syms.Lookup("runtime.symtab", 0).Sect,
|
||||||
|
ctxt.Syms.Lookup("runtime.pclntab", 0).Sect,
|
||||||
|
ctxt.Syms.Lookup("runtime.noptrdata", 0).Sect,
|
||||||
|
ctxt.Syms.Lookup("runtime.data", 0).Sect,
|
||||||
|
}
|
||||||
|
|
||||||
|
writeUleb128(ctxt.Out, uint64(len(sections))) // number of data entries
|
||||||
|
|
||||||
|
for _, sec := range sections {
|
||||||
|
writeUleb128(ctxt.Out, 0) // memidx
|
||||||
|
writeI32Const(ctxt.Out, int32(sec.Vaddr))
|
||||||
|
ctxt.Out.WriteByte(0x0b) // end
|
||||||
|
writeUleb128(ctxt.Out, uint64(sec.Length))
|
||||||
|
ld.Datblk(ctxt, int64(sec.Vaddr), int64(sec.Length))
|
||||||
|
}
|
||||||
|
|
||||||
|
writeSecSize(ctxt, sizeOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
var nameRegexp = regexp.MustCompile(`[^\w\.]`)
|
||||||
|
|
||||||
|
// writeNameSec writes an optional section that assigns names to the functions declared by the "func" section.
|
||||||
|
// The names are only used by WebAssembly stack traces, debuggers and decompilers.
|
||||||
|
// TODO(neelance): add symbol table of DATA symbols
|
||||||
|
func writeNameSec(ctxt *ld.Link, fns []*wasmFunc) {
|
||||||
|
sizeOffset := writeSecHeader(ctxt, sectionCustom)
|
||||||
|
writeName(ctxt.Out, "name")
|
||||||
|
|
||||||
|
sizeOffset2 := writeSecHeader(ctxt, 0x01) // function names
|
||||||
|
writeUleb128(ctxt.Out, uint64(len(fns)))
|
||||||
|
for i, fn := range fns {
|
||||||
|
writeUleb128(ctxt.Out, uint64(i))
|
||||||
|
writeName(ctxt.Out, fn.Name)
|
||||||
|
}
|
||||||
|
writeSecSize(ctxt, sizeOffset2)
|
||||||
|
|
||||||
|
writeSecSize(ctxt, sizeOffset)
|
||||||
|
}
|
||||||
|
|
||||||
|
type nameWriter interface {
|
||||||
|
io.ByteWriter
|
||||||
|
io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeI32Const(w io.ByteWriter, v int32) {
|
||||||
|
w.WriteByte(0x41) // i32.const
|
||||||
|
writeSleb128(w, int64(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeI64Const(w io.ByteWriter, v int64) {
|
||||||
|
w.WriteByte(0x42) // i64.const
|
||||||
|
writeSleb128(w, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeName(w nameWriter, name string) {
|
||||||
|
writeUleb128(w, uint64(len(name)))
|
||||||
|
w.Write([]byte(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeUleb128(w io.ByteWriter, v uint64) {
|
||||||
|
more := true
|
||||||
|
for more {
|
||||||
|
c := uint8(v & 0x7f)
|
||||||
|
v >>= 7
|
||||||
|
more = v != 0
|
||||||
|
if more {
|
||||||
|
c |= 0x80
|
||||||
|
}
|
||||||
|
w.WriteByte(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeUleb128FixedLength(w io.ByteWriter, v uint64, length int) {
|
||||||
|
for i := 0; i < length; i++ {
|
||||||
|
c := uint8(v & 0x7f)
|
||||||
|
v >>= 7
|
||||||
|
if i < length-1 {
|
||||||
|
c |= 0x80
|
||||||
|
}
|
||||||
|
w.WriteByte(c)
|
||||||
|
}
|
||||||
|
if v != 0 {
|
||||||
|
panic("writeUleb128FixedLength: length too small")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeSleb128(w io.ByteWriter, v int64) {
|
||||||
|
more := true
|
||||||
|
for more {
|
||||||
|
c := uint8(v & 0x7f)
|
||||||
|
s := uint8(v & 0x40)
|
||||||
|
v >>= 7
|
||||||
|
more = !((v == 0 && s == 0) || (v == -1 && s != 0))
|
||||||
|
if more {
|
||||||
|
c |= 0x80
|
||||||
|
}
|
||||||
|
w.WriteByte(c)
|
||||||
|
}
|
||||||
|
}
|
34
src/cmd/link/internal/wasm/obj.go
Normal file
34
src/cmd/link/internal/wasm/obj.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// Copyright 2018 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package wasm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"cmd/internal/sys"
|
||||||
|
"cmd/link/internal/ld"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Init() (*sys.Arch, ld.Arch) {
|
||||||
|
theArch := ld.Arch{
|
||||||
|
Funcalign: 16,
|
||||||
|
Maxalign: 32,
|
||||||
|
Minalign: 1,
|
||||||
|
|
||||||
|
Archinit: archinit,
|
||||||
|
AssignAddress: assignAddress,
|
||||||
|
Asmb: asmb,
|
||||||
|
Gentext: gentext,
|
||||||
|
}
|
||||||
|
|
||||||
|
return sys.ArchWasm, theArch
|
||||||
|
}
|
||||||
|
|
||||||
|
func archinit(ctxt *ld.Link) {
|
||||||
|
if *ld.FlagRound == -1 {
|
||||||
|
*ld.FlagRound = 4096
|
||||||
|
}
|
||||||
|
if *ld.FlagTextAddr == -1 {
|
||||||
|
*ld.FlagTextAddr = 0
|
||||||
|
}
|
||||||
|
}
|
@ -15,6 +15,7 @@ import (
|
|||||||
"cmd/link/internal/mips64"
|
"cmd/link/internal/mips64"
|
||||||
"cmd/link/internal/ppc64"
|
"cmd/link/internal/ppc64"
|
||||||
"cmd/link/internal/s390x"
|
"cmd/link/internal/s390x"
|
||||||
|
"cmd/link/internal/wasm"
|
||||||
"cmd/link/internal/x86"
|
"cmd/link/internal/x86"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
@ -58,6 +59,8 @@ func main() {
|
|||||||
arch, theArch = ppc64.Init()
|
arch, theArch = ppc64.Init()
|
||||||
case "s390x":
|
case "s390x":
|
||||||
arch, theArch = s390x.Init()
|
arch, theArch = s390x.Init()
|
||||||
|
case "wasm":
|
||||||
|
arch, theArch = wasm.Init()
|
||||||
}
|
}
|
||||||
ld.Main(arch, theArch)
|
ld.Main(arch, theArch)
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,9 @@ GLOBL runtime·memstats(SB), NOPTR, $0
|
|||||||
#ifdef GOARCH_amd64p32
|
#ifdef GOARCH_amd64p32
|
||||||
#define SKIP4 BYTE $0x90; BYTE $0x90; BYTE $0x90; BYTE $0x90
|
#define SKIP4 BYTE $0x90; BYTE $0x90; BYTE $0x90; BYTE $0x90
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef GOARCH_wasm
|
||||||
|
#define SKIP4 UNDEF; UNDEF; UNDEF; UNDEF
|
||||||
|
#endif
|
||||||
#ifndef SKIP4
|
#ifndef SKIP4
|
||||||
#define SKIP4 WORD $0
|
#define SKIP4 WORD $0
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user