diff --git a/src/cmd/link/internal/amd64/asm.go b/src/cmd/link/internal/amd64/asm.go index fca4877a45a5d57589d8b743d9a5eaf902486d25..7dbe99c581d18dc8075048e2c95dd898ed926e24 100644 --- a/src/cmd/link/internal/amd64/asm.go +++ b/src/cmd/link/internal/amd64/asm.go @@ -704,7 +704,9 @@ func asmb(ctxt *ld.Link) { 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) diff --git a/src/cmd/link/internal/amd64/obj.go b/src/cmd/link/internal/amd64/obj.go index eeeed1ab1a7d40b73f54259edd16153d83e9f01e..23741eb4f65af060b737252402b6b98b9872dbde 100644 --- a/src/cmd/link/internal/amd64/obj.go +++ b/src/cmd/link/internal/amd64/obj.go @@ -54,6 +54,7 @@ func Init() (*sys.Arch, ld.Arch) { Archreloc: archreloc, Archrelocvariant: archrelocvariant, Asmb: asmb, + Asmb2: asmb2, Elfreloc1: elfreloc1, Elfsetupplt: elfsetupplt, Gentext: gentext, diff --git a/src/cmd/link/internal/arm/asm.go b/src/cmd/link/internal/arm/asm.go index 7ea1fe5f8f9c07d273380ee00c7581ede46126d9..43d387c862d5cef0c839c7d32af7529e3930805a 100644 --- a/src/cmd/link/internal/arm/asm.go +++ b/src/cmd/link/internal/arm/asm.go @@ -800,7 +800,9 @@ func asmb(ctxt *ld.Link) { 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)) diff --git a/src/cmd/link/internal/arm/obj.go b/src/cmd/link/internal/arm/obj.go index ea91711df01dd84a11dbc070486d9909528af74a..45a406ec063981e7a6bcdf2fa1753715bbab91b3 100644 --- a/src/cmd/link/internal/arm/obj.go +++ b/src/cmd/link/internal/arm/obj.go @@ -52,6 +52,7 @@ func Init() (*sys.Arch, ld.Arch) { Archrelocvariant: archrelocvariant, Trampoline: trampoline, Asmb: asmb, + Asmb2: asmb2, Elfreloc1: elfreloc1, Elfsetupplt: elfsetupplt, Gentext: gentext, diff --git a/src/cmd/link/internal/arm64/asm.go b/src/cmd/link/internal/arm64/asm.go index 5ba038d14772cf29891d67939ad6b219a709e7c5..c83209972680903bf8ad0813cc5d45ee8d36d438 100644 --- a/src/cmd/link/internal/arm64/asm.go +++ b/src/cmd/link/internal/arm64/asm.go @@ -426,7 +426,9 @@ func asmb(ctxt *ld.Link) { 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)) diff --git a/src/cmd/link/internal/arm64/obj.go b/src/cmd/link/internal/arm64/obj.go index 04202012ee2e7a13d488caebb42359b30c35742d..2f8a141139062ada3425b0828b49a363c23f679d 100644 --- a/src/cmd/link/internal/arm64/obj.go +++ b/src/cmd/link/internal/arm64/obj.go @@ -51,6 +51,7 @@ func Init() (*sys.Arch, ld.Arch) { Archreloc: archreloc, Archrelocvariant: archrelocvariant, Asmb: asmb, + Asmb2: asmb2, Elfreloc1: elfreloc1, Elfsetupplt: elfsetupplt, Gentext: gentext, diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index cb74b9a723de687342efcb22c7a486b61ba5437f..a6f75b74e1f43d49f5ed7c562f811588744593b6 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -792,6 +792,7 @@ func Datblk(ctxt *Link, addr int64, size int64) { 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)} @@ -2319,7 +2320,8 @@ func (ctxt *Link) address() []*sym.Segment { } // 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 { @@ -2348,7 +2350,7 @@ func (ctxt *Link) layout(order []*sym.Segment) { } prev = seg } - + return prev.Fileoff + prev.Filelen } // add a trampoline with symbol s (to be laid down after the current function) diff --git a/src/cmd/link/internal/ld/lib.go b/src/cmd/link/internal/ld/lib.go index 62f24533584d3b8c06c328fd05692d16430ffb44..c4748781911aa4ba83c7cf700908e540f7c1f80c 100644 --- a/src/cmd/link/internal/ld/lib.go +++ b/src/cmd/link/internal/ld/lib.go @@ -127,8 +127,15 @@ type Arch struct { // 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) @@ -261,7 +268,7 @@ func libinit(ctxt *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) } @@ -1014,7 +1021,7 @@ func hostlinksetup(ctxt *Link) { 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) } diff --git a/src/cmd/link/internal/ld/main.go b/src/cmd/link/internal/ld/main.go index 48a99538937d752376b4205ed262aea96edc6c47..1b2d376fd4a9d3a166fae5f540dd0420b48fbe84 100644 --- a/src/cmd/link/internal/ld/main.go +++ b/src/cmd/link/internal/ld/main.go @@ -241,8 +241,29 @@ func Main(arch *sys.Arch, theArch Arch) { 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() diff --git a/src/cmd/link/internal/ld/outbuf.go b/src/cmd/link/internal/ld/outbuf.go index 5df2be43014fb90fb28f2516e9571e1c154de316..f1b5d7495c546ca4bff64269a28a8355a4fb3f8a 100644 --- a/src/cmd/link/internal/ld/outbuf.go +++ b/src/cmd/link/internal/ld/outbuf.go @@ -8,6 +8,7 @@ import ( "bufio" "cmd/internal/sys" "encoding/binary" + "log" "os" ) @@ -20,10 +21,18 @@ import ( // // 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 } @@ -32,9 +41,11 @@ func (out *OutBuf) SeekSet(p int64) { 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 } @@ -49,12 +60,22 @@ func (out *OutBuf) Offset() int64 { // 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++ } @@ -92,6 +113,14 @@ func (out *OutBuf) Write64b(v uint64) { } 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) } @@ -120,7 +149,13 @@ func (out *OutBuf) WriteStringPad(s string, n int, pad []byte) { } 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) } } diff --git a/src/cmd/link/internal/ld/outbuf_mmap.go b/src/cmd/link/internal/ld/outbuf_mmap.go new file mode 100644 index 0000000000000000000000000000000000000000..4075141171d2c4d2e4520b714f961081ed3851ed --- /dev/null +++ b/src/cmd/link/internal/ld/outbuf_mmap.go @@ -0,0 +1,44 @@ +// 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 +} diff --git a/src/cmd/link/internal/ld/outbuf_nommap.go b/src/cmd/link/internal/ld/outbuf_nommap.go new file mode 100644 index 0000000000000000000000000000000000000000..36a328609928c54be4e88b8e107b162d6ca4a69b --- /dev/null +++ b/src/cmd/link/internal/ld/outbuf_nommap.go @@ -0,0 +1,15 @@ +// 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") } diff --git a/src/cmd/link/internal/mips/asm.go b/src/cmd/link/internal/mips/asm.go index 8409e43afcbcf6a959fdf4a09cb3a4edbd9bb510..f05455e5200eb2aee3dbeb175e3e3c2b00bf4637 100644 --- a/src/cmd/link/internal/mips/asm.go +++ b/src/cmd/link/internal/mips/asm.go @@ -197,7 +197,9 @@ func asmb(ctxt *ld.Link) { 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 diff --git a/src/cmd/link/internal/mips/obj.go b/src/cmd/link/internal/mips/obj.go index 3c71e23497e199e6677e060cc8f24d15c474c1df..231e1ff322a28c4a9ff6a1f30141f90dcb942d4a 100644 --- a/src/cmd/link/internal/mips/obj.go +++ b/src/cmd/link/internal/mips/obj.go @@ -54,6 +54,7 @@ func Init() (*sys.Arch, ld.Arch) { Archreloc: archreloc, Archrelocvariant: archrelocvariant, Asmb: asmb, + Asmb2: asmb2, Elfreloc1: elfreloc1, Elfsetupplt: elfsetupplt, Gentext: gentext, diff --git a/src/cmd/link/internal/mips64/asm.go b/src/cmd/link/internal/mips64/asm.go index 51eba596dc796f1a99b02fc884694f3d57585bc7..25a1d94dcc0b7d20832046923b6a541e6e87a92d 100644 --- a/src/cmd/link/internal/mips64/asm.go +++ b/src/cmd/link/internal/mips64/asm.go @@ -209,7 +209,9 @@ func asmb(ctxt *ld.Link) { 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 diff --git a/src/cmd/link/internal/mips64/obj.go b/src/cmd/link/internal/mips64/obj.go index b01746e59ac9e36555b678c01da445b466624be3..96042083f6fcdbd54c6285af310d71b3521404b3 100644 --- a/src/cmd/link/internal/mips64/obj.go +++ b/src/cmd/link/internal/mips64/obj.go @@ -53,6 +53,7 @@ func Init() (*sys.Arch, ld.Arch) { Archreloc: archreloc, Archrelocvariant: archrelocvariant, Asmb: asmb, + Asmb2: asmb2, Elfreloc1: elfreloc1, Elfsetupplt: elfsetupplt, Gentext: gentext, diff --git a/src/cmd/link/internal/ppc64/asm.go b/src/cmd/link/internal/ppc64/asm.go index d376c4de58709f5e6678c35251b710cc3b87dddc..365a45ec135c40d4afb6925601fb34bf7f75399a 100644 --- a/src/cmd/link/internal/ppc64/asm.go +++ b/src/cmd/link/internal/ppc64/asm.go @@ -1103,7 +1103,9 @@ func asmb(ctxt *ld.Link) { 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 diff --git a/src/cmd/link/internal/ppc64/obj.go b/src/cmd/link/internal/ppc64/obj.go index bd85856c97c6b49c3d6ef5b826a28f2413978ba6..51d1791f21a4654ea86d75f863f229f1c04d250c 100644 --- a/src/cmd/link/internal/ppc64/obj.go +++ b/src/cmd/link/internal/ppc64/obj.go @@ -54,6 +54,7 @@ func Init() (*sys.Arch, ld.Arch) { Archreloc: archreloc, Archrelocvariant: archrelocvariant, Asmb: asmb, + Asmb2: asmb2, Elfreloc1: elfreloc1, Elfsetupplt: elfsetupplt, Gentext: gentext, diff --git a/src/cmd/link/internal/s390x/asm.go b/src/cmd/link/internal/s390x/asm.go index 46a6ffef828e4fbb591f7f6f0b14062180c653eb..85403774004fef435b7e26f8002811da91e8963a 100644 --- a/src/cmd/link/internal/s390x/asm.go +++ b/src/cmd/link/internal/s390x/asm.go @@ -540,7 +540,9 @@ func asmb(ctxt *ld.Link) { 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 diff --git a/src/cmd/link/internal/s390x/obj.go b/src/cmd/link/internal/s390x/obj.go index a7e30e2d6578eeddf0d1cd6f3ef7b5f5087d7957..3454476b0f6fd85098a6e8a5951c6b46eee2ee6c 100644 --- a/src/cmd/link/internal/s390x/obj.go +++ b/src/cmd/link/internal/s390x/obj.go @@ -50,7 +50,8 @@ func Init() (*sys.Arch, ld.Arch) { 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, diff --git a/src/cmd/link/internal/wasm/asm.go b/src/cmd/link/internal/wasm/asm.go index ea6b406c7e217dcd064ca5878468c5bd5504c22b..8ab58b200f3d3aef89ad5519b9f2cd049b1ca88c 100644 --- a/src/cmd/link/internal/wasm/asm.go +++ b/src/cmd/link/internal/wasm/asm.go @@ -92,9 +92,11 @@ func assignAddress(ctxt *ld.Link, sect *sym.Section, n int, s *sym.Symbol, va ui 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()) } diff --git a/src/cmd/link/internal/wasm/obj.go b/src/cmd/link/internal/wasm/obj.go index 55f34e335b6fff6accbf19691a91e1f65e61d9bf..f8090a3551cdbcb6c4edf3fd5d74840ac6f448b9 100644 --- a/src/cmd/link/internal/wasm/obj.go +++ b/src/cmd/link/internal/wasm/obj.go @@ -18,6 +18,7 @@ func Init() (*sys.Arch, ld.Arch) { Archinit: archinit, AssignAddress: assignAddress, Asmb: asmb, + Asmb2: asmb2, Gentext: gentext, } diff --git a/src/cmd/link/internal/x86/asm.go b/src/cmd/link/internal/x86/asm.go index 9472f5516d071930dac0abb07a5ab01ec3cc6636..427ccaf6297f7ad188d08bfe0a4f4e85492d5336 100644 --- a/src/cmd/link/internal/x86/asm.go +++ b/src/cmd/link/internal/x86/asm.go @@ -662,7 +662,9 @@ func asmb(ctxt *ld.Link) { 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)) diff --git a/src/cmd/link/internal/x86/obj.go b/src/cmd/link/internal/x86/obj.go index dbb31263a89f272f516dfd7c4a8d4b2c2d7e5496..f1fad20081b6a592dd55715703394c6c15ed5d4a 100644 --- a/src/cmd/link/internal/x86/obj.go +++ b/src/cmd/link/internal/x86/obj.go @@ -51,6 +51,7 @@ func Init() (*sys.Arch, ld.Arch) { Archreloc: archreloc, Archrelocvariant: archrelocvariant, Asmb: asmb, + Asmb2: asmb2, Elfreloc1: elfreloc1, Elfsetupplt: elfsetupplt, Gentext: gentext,