]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/link: add wasm architecture
authorRichard Musiol <mail@richard-musiol.de>
Sun, 4 Mar 2018 11:59:15 +0000 (12:59 +0100)
committerBrad Fitzpatrick <bradfitz@golang.org>
Mon, 7 May 2018 23:29:33 +0000 (23:29 +0000)
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>
12 files changed:
misc/wasm/wasm_exec.js
src/cmd/dist/build.go
src/cmd/dist/buildtool.go
src/cmd/link/internal/ld/data.go
src/cmd/link/internal/ld/dwarf.go
src/cmd/link/internal/ld/lib.go
src/cmd/link/internal/ld/outbuf.go
src/cmd/link/internal/ld/sym.go
src/cmd/link/internal/wasm/asm.go [new file with mode: 0644]
src/cmd/link/internal/wasm/obj.go [new file with mode: 0644]
src/cmd/link/main.go
src/runtime/asm.s

index 05522f223688f3a52e4d13f2357c726693728454..372cd4195e19f9f96bf0e505a0b62f4afd02298c 100755 (executable)
@@ -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));
index 163fdae1198afa32612ca825419e4b422303b8d2..99d1db5909590dc69b4746c1f6f75d6c2130f56f 100644 (file)
@@ -68,12 +68,14 @@ var okgoarch = []string{
        "ppc64",
        "ppc64le",
        "s390x",
+       "wasm",
 }
 
 // The known operating systems.
 var okgoos = []string{
        "darwin",
        "dragonfly",
+       "js",
        "linux",
        "android",
        "solaris",
index 880b76f32ddde8471a2140653e571fd9fdb7f21d..889fd02aafc6e5e652fd828d5ccd6d37a8478bcb 100644 (file)
@@ -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",
index 0bb141bdd14f1afe0774e8beb438c2828d286b6e..6cc5c544f5a0113ea550219b2e5ca29b245150a6 100644 (file)
@@ -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
index e06d1493e02c3f9a05c53058b34033c29b51d2a1..6232ab6e2fd2074cadc321559dbbf20d44b98f59 100644 (file)
@@ -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
        }
 
index dd4d65c0cafa31fff36d4605417220c8ae16d30b..d8c474f52c74f15801a31a60739c7d412de7ce07 100644 (file)
@@ -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 (
index 580435ad04ff704d5b2fff7d69bcfbec7910d8c9..5df2be43014fb90fb28f2516e9571e1c154de316 100644 (file)
@@ -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])
index 7bc57eff8d931117c2d186537f63308ac1729ce6..3aa90c17dc8a1a6fb3f74a4f78a221f76d5cef57 100644 (file)
@@ -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 (file)
index 0000000..235a5a2
--- /dev/null
@@ -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 (file)
index 0000000..55f34e3
--- /dev/null
@@ -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
+       }
+}
index b1a66f545405a4f47bd5fc20788222145ff2678c..4b8df11451f71b834ce72336b652f839272c157e 100644 (file)
@@ -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)
 }
index 26461724419a675a741ec6a2dcf9fb7171ba6dfa..6b209b2d1f463a52e4289325fbf88e7770164472 100644 (file)
@@ -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