From f41dc711d80cf750a6df994a49608ffd3786bb19 Mon Sep 17 00:00:00 2001 From: Richard Musiol Date: Sun, 4 Mar 2018 12:59:15 +0100 Subject: [PATCH] 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 TryBot-Result: Gobot Gobot Reviewed-by: Cherry Zhang --- misc/wasm/wasm_exec.js | 2 +- src/cmd/dist/build.go | 2 + src/cmd/dist/buildtool.go | 1 + src/cmd/link/internal/ld/data.go | 4 + src/cmd/link/internal/ld/dwarf.go | 2 +- src/cmd/link/internal/ld/lib.go | 3 + src/cmd/link/internal/ld/outbuf.go | 6 + src/cmd/link/internal/ld/sym.go | 2 +- src/cmd/link/internal/wasm/asm.go | 485 +++++++++++++++++++++++++++++ src/cmd/link/internal/wasm/obj.go | 34 ++ src/cmd/link/main.go | 3 + src/runtime/asm.s | 3 + 12 files changed, 544 insertions(+), 3 deletions(-) create mode 100644 src/cmd/link/internal/wasm/asm.go create mode 100644 src/cmd/link/internal/wasm/obj.go diff --git a/misc/wasm/wasm_exec.js b/misc/wasm/wasm_exec.js index 05522f2236..372cd4195e 100755 --- a/misc/wasm/wasm_exec.js +++ b/misc/wasm/wasm_exec.js @@ -145,7 +145,7 @@ async function compile(source) { async function run() { let importObject = { - js: { + go: { // func wasmexit(code int32) "runtime.wasmexit": function (sp) { process.exit(mem().getInt32(sp + 8, true)); diff --git a/src/cmd/dist/build.go b/src/cmd/dist/build.go index 163fdae119..99d1db5909 100644 --- a/src/cmd/dist/build.go +++ b/src/cmd/dist/build.go @@ -68,12 +68,14 @@ var okgoarch = []string{ "ppc64", "ppc64le", "s390x", + "wasm", } // The known operating systems. var okgoos = []string{ "darwin", "dragonfly", + "js", "linux", "android", "solaris", diff --git a/src/cmd/dist/buildtool.go b/src/cmd/dist/buildtool.go index 880b76f32d..889fd02aaf 100644 --- a/src/cmd/dist/buildtool.go +++ b/src/cmd/dist/buildtool.go @@ -80,6 +80,7 @@ var bootstrapDirs = []string{ "cmd/link/internal/s390x", "cmd/link/internal/sym", "cmd/link/internal/x86", + "cmd/link/internal/wasm", "container/heap", "debug/dwarf", "debug/elf", diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index 0bb141bdd1..6cc5c544f5 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -1852,6 +1852,10 @@ func (ctxt *Link) textaddress() { // 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. 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 if s.Attr.SubSymbol() { return sect, n, va diff --git a/src/cmd/link/internal/ld/dwarf.go b/src/cmd/link/internal/ld/dwarf.go index e06d1493e0..6232ab6e2f 100644 --- a/src/cmd/link/internal/ld/dwarf.go +++ b/src/cmd/link/internal/ld/dwarf.go @@ -1675,7 +1675,7 @@ func dwarfgeneratedebugsyms(ctxt *Link) { if *FlagS && ctxt.HeadType != objabi.Hdarwin { return } - if ctxt.HeadType == objabi.Hplan9 { + if ctxt.HeadType == objabi.Hplan9 || ctxt.HeadType == objabi.Hjs { return } diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index dd4d65c0ca..d8c474f52c 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -121,6 +121,9 @@ type Arch struct { // symbol in an executable, which is typical when internally // linking PIE binaries. 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 ( diff --git a/src/cmd/link/internal/ld/outbuf.go b/src/cmd/link/internal/ld/outbuf.go index 580435ad04..5df2be4301 100644 --- a/src/cmd/link/internal/ld/outbuf.go +++ b/src/cmd/link/internal/ld/outbuf.go @@ -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) { out.arch.ByteOrder.PutUint16(out.encbuf[:], v) out.Write(out.encbuf[:2]) diff --git a/src/cmd/link/internal/ld/sym.go b/src/cmd/link/internal/ld/sym.go index 7bc57eff8d..3aa90c17dc 100644 --- a/src/cmd/link/internal/ld/sym.go +++ b/src/cmd/link/internal/ld/sym.go @@ -66,7 +66,7 @@ func (ctxt *Link) computeTLSOffset() { default: log.Fatalf("unknown thread-local storage offset for %v", ctxt.HeadType) - case objabi.Hplan9, objabi.Hwindows: + case objabi.Hplan9, objabi.Hwindows, objabi.Hjs: break /* diff --git a/src/cmd/link/internal/wasm/asm.go b/src/cmd/link/internal/wasm/asm.go new file mode 100644 index 0000000000..235a5a25d1 --- /dev/null +++ b/src/cmd/link/internal/wasm/asm.go @@ -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) + } +} diff --git a/src/cmd/link/internal/wasm/obj.go b/src/cmd/link/internal/wasm/obj.go new file mode 100644 index 0000000000..55f34e335b --- /dev/null +++ b/src/cmd/link/internal/wasm/obj.go @@ -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 + } +} diff --git a/src/cmd/link/main.go b/src/cmd/link/main.go index b1a66f5454..4b8df11451 100644 --- a/src/cmd/link/main.go +++ b/src/cmd/link/main.go @@ -15,6 +15,7 @@ import ( "cmd/link/internal/mips64" "cmd/link/internal/ppc64" "cmd/link/internal/s390x" + "cmd/link/internal/wasm" "cmd/link/internal/x86" "fmt" "os" @@ -58,6 +59,8 @@ func main() { arch, theArch = ppc64.Init() case "s390x": arch, theArch = s390x.Init() + case "wasm": + arch, theArch = wasm.Init() } ld.Main(arch, theArch) } diff --git a/src/runtime/asm.s b/src/runtime/asm.s index 2646172441..6b209b2d1f 100644 --- a/src/runtime/asm.s +++ b/src/runtime/asm.s @@ -25,6 +25,9 @@ GLOBL runtime·memstats(SB), NOPTR, $0 #ifdef GOARCH_amd64p32 #define SKIP4 BYTE $0x90; BYTE $0x90; BYTE $0x90; BYTE $0x90 #endif +#ifdef GOARCH_wasm +#define SKIP4 UNDEF; UNDEF; UNDEF; UNDEF +#endif #ifndef SKIP4 #define SKIP4 WORD $0 #endif