ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff))
ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
+}
+func asmb2(ctxt *ld.Link) {
machlink := int64(0)
if ctxt.HeadType == objabi.Hdarwin {
machlink = ld.Domacholink(ctxt)
Archreloc: archreloc,
Archrelocvariant: archrelocvariant,
Asmb: asmb,
+ Asmb2: asmb2,
Elfreloc1: elfreloc1,
Elfsetupplt: elfsetupplt,
Gentext: gentext,
ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff))
ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
+}
+func asmb2(ctxt *ld.Link) {
machlink := uint32(0)
if ctxt.HeadType == objabi.Hdarwin {
machlink = uint32(ld.Domacholink(ctxt))
Archrelocvariant: archrelocvariant,
Trampoline: trampoline,
Asmb: asmb,
+ Asmb2: asmb2,
Elfreloc1: elfreloc1,
Elfsetupplt: elfsetupplt,
Gentext: gentext,
ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff))
ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
+}
+func asmb2(ctxt *ld.Link) {
machlink := uint32(0)
if ctxt.HeadType == objabi.Hdarwin {
machlink = uint32(ld.Domacholink(ctxt))
Archreloc: archreloc,
Archrelocvariant: archrelocvariant,
Asmb: asmb,
+ Asmb2: asmb2,
Elfreloc1: elfreloc1,
Elfsetupplt: elfsetupplt,
Gentext: gentext,
writeDatblkToOutBuf(ctxt, ctxt.Out, addr, size)
}
+// Used only on Wasm for now.
func DatblkBytes(ctxt *Link, addr int64, size int64) []byte {
buf := bytes.NewBuffer(make([]byte, 0, size))
out := &OutBuf{w: bufio.NewWriter(buf)}
}
// layout assigns file offsets and lengths to the segments in order.
-func (ctxt *Link) layout(order []*sym.Segment) {
+// Returns the file size containing all the segments.
+func (ctxt *Link) layout(order []*sym.Segment) uint64 {
var prev *sym.Segment
for _, seg := range order {
if prev == nil {
}
prev = seg
}
-
+ return prev.Fileoff + prev.Filelen
}
// add a trampoline with symbol s (to be laid down after the current function)
// offset value.
Archrelocvariant func(link *Link, rel *sym.Reloc, sym *sym.Symbol,
offset int64) (relocatedOffset int64)
- Trampoline func(*Link, *sym.Reloc, *sym.Symbol)
- Asmb func(*Link)
+ Trampoline func(*Link, *sym.Reloc, *sym.Symbol)
+
+ // Asmb and Asmb2 are arch-specific routines that write the output
+ // file. Typically, Asmb writes most of the content (sections and
+ // segments), for which we have computed the size and offset. Asmb2
+ // writes the rest.
+ Asmb func(*Link)
+ Asmb2 func(*Link)
+
Elfreloc1 func(*Link, *sym.Reloc, int64) bool
Elfsetupplt func(*Link)
Gentext func(*Link)
Lflag(ctxt, filepath.Join(objabi.GOROOT, "pkg", fmt.Sprintf("%s_%s%s%s", objabi.GOOS, objabi.GOARCH, suffixsep, suffix)))
mayberemoveoutfile()
- f, err := os.OpenFile(*flagOutfile, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0775)
+ f, err := os.OpenFile(*flagOutfile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0775)
if err != nil {
Exitf("cannot create %s: %v", *flagOutfile, err)
}
p := filepath.Join(*flagTmpdir, "go.o")
var err error
- f, err := os.OpenFile(p, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0775)
+ f, err := os.OpenFile(p, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0775)
if err != nil {
Exitf("cannot create %s: %v", p, err)
}
order := ctxt.address()
ctxt.reloc()
dwarfcompress(ctxt)
- ctxt.layout(order)
- thearch.Asmb(ctxt)
+ filesize := ctxt.layout(order)
+
+ // Write out the output file.
+ // It is split into two parts (Asmb and Asmb2). The first
+ // part writes most of the content (sections and segments),
+ // for which we have computed the size and offset, in a
+ // mmap'd region. The second part writes more content, for
+ // which we don't know the size.
+ var outputMmapped bool
+ if ctxt.Arch.Family != sys.Wasm {
+ // Don't mmap if we're building for Wasm. Wasm file
+ // layout is very different so filesize is meaningless.
+ err := ctxt.Out.Mmap(filesize)
+ outputMmapped = err == nil
+ }
+ if outputMmapped {
+ thearch.Asmb(ctxt)
+ ctxt.Out.Munmap()
+ } else {
+ thearch.Asmb(ctxt)
+ }
+ thearch.Asmb2(ctxt)
+
ctxt.undef()
ctxt.hostlink()
ctxt.archive()
"bufio"
"cmd/internal/sys"
"encoding/binary"
+ "log"
"os"
)
//
// Second, it provides a very cheap offset counter that doesn't require
// any system calls to read the value.
+//
+// It also mmaps the output file (if available). The intended usage is:
+// - Mmap the output file
+// - Write the content
+// - possibly apply any edits in the output buffer
+// - Munmap the output file
+// - possibly write more content to the file, which will not be edited later.
type OutBuf struct {
arch *sys.Arch
off int64
w *bufio.Writer
+ buf []byte // backing store of mmap'd output file
f *os.File
encbuf [8]byte // temp buffer used by WriteN methods
}
if p == out.off {
return
}
- out.Flush()
- if _, err := out.f.Seek(p, 0); err != nil {
- Exitf("seeking to %d in %s: %v", p, out.f.Name(), err)
+ if out.buf == nil {
+ out.Flush()
+ if _, err := out.f.Seek(p, 0); err != nil {
+ Exitf("seeking to %d in %s: %v", p, out.f.Name(), err)
+ }
}
out.off = p
}
// to explicitly handle the returned error as long as Flush is
// eventually called.
func (out *OutBuf) Write(v []byte) (int, error) {
+ if out.buf != nil {
+ n := copy(out.buf[out.off:], v)
+ out.off += int64(n)
+ return n, nil
+ }
n, err := out.w.Write(v)
out.off += int64(n)
return n, err
}
func (out *OutBuf) Write8(v uint8) {
+ if out.buf != nil {
+ out.buf[out.off] = v
+ out.off++
+ return
+ }
if err := out.w.WriteByte(v); err == nil {
out.off++
}
}
func (out *OutBuf) WriteString(s string) {
+ if out.buf != nil {
+ n := copy(out.buf[out.off:], s)
+ if n != len(s) {
+ log.Fatalf("WriteString truncated. buffer size: %d, offset: %d, len(s)=%d", len(out.buf), out.off, len(s))
+ }
+ out.off += int64(n)
+ return
+ }
n, _ := out.w.WriteString(s)
out.off += int64(n)
}
}
func (out *OutBuf) Flush() {
- if err := out.w.Flush(); err != nil {
+ var err error
+ if out.buf != nil {
+ err = out.Msync()
+ } else {
+ err = out.w.Flush()
+ }
+ if err != nil {
Exitf("flushing %s: %v", out.f.Name(), err)
}
}
--- /dev/null
+// Copyright 2019 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.
+
+// +build darwin dragonfly freebsd linux openbsd
+
+package ld
+
+import (
+ "syscall"
+ "unsafe"
+)
+
+func (out *OutBuf) Mmap(filesize uint64) error {
+ err := out.f.Truncate(int64(filesize))
+ if err != nil {
+ Exitf("resize output file failed: %v", err)
+ }
+ out.buf, err = syscall.Mmap(int(out.f.Fd()), 0, int(filesize), syscall.PROT_READ|syscall.PROT_WRITE, syscall.MAP_SHARED|syscall.MAP_FILE)
+ return err
+}
+
+func (out *OutBuf) Munmap() {
+ err := out.Msync()
+ if err != nil {
+ Exitf("msync output file failed: %v", err)
+ }
+ syscall.Munmap(out.buf)
+ out.buf = nil
+ _, err = out.f.Seek(out.off, 0)
+ if err != nil {
+ Exitf("seek output file failed: %v", err)
+ }
+}
+
+func (out *OutBuf) Msync() error {
+ // TODO: netbsd supports mmap and msync, but the syscall package doesn't define MSYNC.
+ // It is excluded from the build tag for now.
+ _, _, errno := syscall.Syscall(syscall.SYS_MSYNC, uintptr(unsafe.Pointer(&out.buf[0])), uintptr(len(out.buf)), syscall.MS_SYNC)
+ if errno != 0 {
+ return errno
+ }
+ return nil
+}
--- /dev/null
+// Copyright 2019 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.
+
+// +build !darwin,!dragonfly,!freebsd,!linux,!openbsd
+
+package ld
+
+import "errors"
+
+var errNotSupported = errors.New("mmap not supported")
+
+func (out *OutBuf) Mmap(filesize uint64) error { return errNotSupported }
+func (out *OutBuf) Munmap() { panic("unreachable") }
+func (out *OutBuf) Msync() error { panic("unreachable") }
ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff))
ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
+}
+func asmb2(ctxt *ld.Link) {
/* output symbol table */
ld.Symsize = 0
Archreloc: archreloc,
Archrelocvariant: archrelocvariant,
Asmb: asmb,
+ Asmb2: asmb2,
Elfreloc1: elfreloc1,
Elfsetupplt: elfsetupplt,
Gentext: gentext,
ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff))
ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
+}
+func asmb2(ctxt *ld.Link) {
/* output symbol table */
ld.Symsize = 0
Archreloc: archreloc,
Archrelocvariant: archrelocvariant,
Asmb: asmb,
+ Asmb2: asmb2,
Elfreloc1: elfreloc1,
Elfsetupplt: elfsetupplt,
Gentext: gentext,
ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff))
ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
+}
+func asmb2(ctxt *ld.Link) {
/* output symbol table */
ld.Symsize = 0
Archreloc: archreloc,
Archrelocvariant: archrelocvariant,
Asmb: asmb,
+ Asmb2: asmb2,
Elfreloc1: elfreloc1,
Elfsetupplt: elfsetupplt,
Gentext: gentext,
ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff))
ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
+}
+func asmb2(ctxt *ld.Link) {
/* output symbol table */
ld.Symsize = 0
Archinit: archinit,
Archreloc: archreloc,
Archrelocvariant: archrelocvariant,
- Asmb: asmb, // in asm.go
+ Asmb: asmb, // in asm.go
+ Asmb2: asmb2, // in asm.go
Elfreloc1: elfreloc1,
Elfsetupplt: elfsetupplt,
Gentext: gentext,
return sect, n, va
}
+func asmb(ctxt *ld.Link) {} // dummy
+
// asmb writes the final WebAssembly module binary.
// Spec: https://webassembly.github.io/spec/core/binary/modules.html
-func asmb(ctxt *ld.Link) {
+func asmb2(ctxt *ld.Link) {
if ctxt.Debugvlog != 0 {
ctxt.Logf("%5.2f asmb\n", ld.Cputime())
}
Archinit: archinit,
AssignAddress: assignAddress,
Asmb: asmb,
+ Asmb2: asmb2,
Gentext: gentext,
}
ctxt.Out.SeekSet(int64(ld.Segdwarf.Fileoff))
ld.Dwarfblk(ctxt, int64(ld.Segdwarf.Vaddr), int64(ld.Segdwarf.Filelen))
+}
+func asmb2(ctxt *ld.Link) {
machlink := uint32(0)
if ctxt.HeadType == objabi.Hdarwin {
machlink = uint32(ld.Domacholink(ctxt))
Archreloc: archreloc,
Archrelocvariant: archrelocvariant,
Asmb: asmb,
+ Asmb2: asmb2,
Elfreloc1: elfreloc1,
Elfsetupplt: elfsetupplt,
Gentext: gentext,