]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.link] cmd/link, cmd/internal/goobj2: mmap object file in -newobj mode
authorCherry Zhang <cherryyz@google.com>
Fri, 27 Sep 2019 18:49:44 +0000 (14:49 -0400)
committerCherry Zhang <cherryyz@google.com>
Wed, 9 Oct 2019 21:00:08 +0000 (21:00 +0000)
With the old object file format, we use mmap (if supported) to
read object files and back symbol data with mapped read-only
memory.

Do the same with the new object file format. This also
significantly reduces number of syscalls made to read object
files.

Currently we still do mmap in object file level, not archive
level. This is probably ok, as there shouldn't be many archives
that contain more than one object. If this is a problem we can
change that later.

Change-Id: Icae3ef14d8ed6adbee1b5b48d420e2af22fd9604
Reviewed-on: https://go-review.googlesource.com/c/go/+/197797
Run-TryBot: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
src/cmd/internal/goobj2/objfile.go
src/cmd/link/internal/ld/lib.go
src/cmd/link/internal/objfile/objfile2.go

index eb9290a699953a03dc97840b491ff12d76ef75e6..4c1bbe83f0a5163d09de82f6056ce8788f10f9f5 100644 (file)
@@ -7,11 +7,13 @@
 package goobj2 // TODO: replace the goobj package?
 
 import (
+       "bytes"
        "cmd/internal/bio"
        "encoding/binary"
        "errors"
        "fmt"
        "io"
+       "unsafe"
 )
 
 // New object file format.
@@ -354,6 +356,9 @@ func (w *Writer) Offset() uint32 {
 }
 
 type Reader struct {
+       b        []byte // mmapped bytes, if not nil
+       readonly bool   // whether b is backed with read-only memory
+
        rd    io.ReaderAt
        start uint32
        h     Header // keep block offsets
@@ -368,10 +373,25 @@ func NewReader(rd io.ReaderAt, off uint32) *Reader {
        return r
 }
 
+func NewReaderFromBytes(b []byte, readonly bool) *Reader {
+       r := &Reader{b: b, readonly: readonly, rd: bytes.NewReader(b), start: 0}
+       err := r.h.Read(r)
+       if err != nil {
+               return nil
+       }
+       return r
+}
+
 func (r *Reader) BytesAt(off uint32, len int) []byte {
-       // TODO: read from mapped memory
+       if len == 0 {
+               return nil
+       }
+       if r.b != nil {
+               end := int(off) + len
+               return r.b[int(off):end:end]
+       }
        b := make([]byte, len)
-       _, err := r.rd.ReadAt(b[:], int64(r.start+off))
+       _, err := r.rd.ReadAt(b, int64(r.start+off))
        if err != nil {
                panic("corrupted input")
        }
@@ -379,12 +399,8 @@ func (r *Reader) BytesAt(off uint32, len int) []byte {
 }
 
 func (r *Reader) uint64At(off uint32) uint64 {
-       var b [8]byte
-       n, err := r.rd.ReadAt(b[:], int64(r.start+off))
-       if n != 8 || err != nil {
-               panic("corrupted input")
-       }
-       return binary.LittleEndian.Uint64(b[:])
+       b := r.BytesAt(off, 8)
+       return binary.LittleEndian.Uint64(b)
 }
 
 func (r *Reader) int64At(off uint32) int64 {
@@ -392,12 +408,8 @@ func (r *Reader) int64At(off uint32) int64 {
 }
 
 func (r *Reader) uint32At(off uint32) uint32 {
-       var b [4]byte
-       n, err := r.rd.ReadAt(b[:], int64(r.start+off))
-       if n != 4 || err != nil {
-               panic("corrupted input")
-       }
-       return binary.LittleEndian.Uint32(b[:])
+       b := r.BytesAt(off, 4)
+       return binary.LittleEndian.Uint32(b)
 }
 
 func (r *Reader) int32At(off uint32) int32 {
@@ -405,26 +417,24 @@ func (r *Reader) int32At(off uint32) int32 {
 }
 
 func (r *Reader) uint16At(off uint32) uint16 {
-       var b [2]byte
-       n, err := r.rd.ReadAt(b[:], int64(r.start+off))
-       if n != 2 || err != nil {
-               panic("corrupted input")
-       }
-       return binary.LittleEndian.Uint16(b[:])
+       b := r.BytesAt(off, 2)
+       return binary.LittleEndian.Uint16(b)
 }
 
 func (r *Reader) uint8At(off uint32) uint8 {
-       var b [1]byte
-       n, err := r.rd.ReadAt(b[:], int64(r.start+off))
-       if n != 1 || err != nil {
-               panic("corrupted input")
-       }
+       b := r.BytesAt(off, 1)
        return b[0]
 }
 
 func (r *Reader) StringAt(off uint32) string {
-       // TODO: have some way to construct a string without copy
        l := r.uint32At(off)
+       if r.b != nil {
+               b := r.b[off+4 : off+4+l]
+               if r.readonly {
+                       return toString(b) // backed by RO memory, ok to make unsafe string
+               }
+               return string(b)
+       }
        b := make([]byte, l)
        n, err := r.rd.ReadAt(b, int64(r.start+off+4))
        if n != int(l) || err != nil {
@@ -433,6 +443,20 @@ func (r *Reader) StringAt(off uint32) string {
        return string(b)
 }
 
+func toString(b []byte) string {
+       type stringHeader struct {
+               str unsafe.Pointer
+               len int
+       }
+
+       if len(b) == 0 {
+               return ""
+       }
+       ss := stringHeader{str: unsafe.Pointer(&b[0]), len: len(b)}
+       s := *(*string)(unsafe.Pointer(&ss))
+       return s
+}
+
 func (r *Reader) StringRef(off uint32) string {
        return r.StringAt(r.uint32At(off))
 }
@@ -511,3 +535,8 @@ func (r *Reader) DataSize(i int) int {
 func (r *Reader) PcdataBase() uint32 {
        return r.h.Offsets[BlkPcdata]
 }
+
+// ReadOnly returns whether r.BytesAt returns read-only bytes.
+func (r *Reader) ReadOnly() bool {
+       return r.readonly
+}
index 2ebd5d333c594a01837bdefcd8fe63eaed2fc7f2..d10933ae43357a6b226384126f08dbdfe30ac2b3 100644 (file)
@@ -835,7 +835,7 @@ func loadobjfile(ctxt *Link, lib *sym.Library) {
        if err != nil {
                Exitf("cannot open file %s: %v", lib.File, err)
        }
-       //defer f.Close()
+       defer f.Close()
        defer func() {
                if pkg == "main" && !lib.Main {
                        Exitf("%s: not package main", lib.File)
index a5bd91d3abca5aba6e4b22b33233598b3707bbf9..252615febc005231fddd18e9102569871c455ef7 100644 (file)
@@ -124,8 +124,11 @@ func (l *Loader) Lookup(name string, ver int) int {
 // Preload a package: add autolibs, add symbols to the symbol table.
 // Does not read symbol data yet.
 func LoadNew(l *Loader, arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, lib *sym.Library, unit *sym.CompilationUnit, length int64, pn string, flags int) {
-       start := f.Offset()
-       r := goobj2.NewReader(f.File(), uint32(start))
+       roObject, readonly, err := f.Slice(uint64(length))
+       if err != nil {
+               log.Fatal("cannot read object file:", err)
+       }
+       r := goobj2.NewReaderFromBytes(roObject, readonly)
        if r == nil {
                panic("cannot read object file")
        }
@@ -314,6 +317,7 @@ func LoadReloc(l *Loader, r *goobj2.Reader, lib *sym.Library, localSymVersion in
                // XXX deadcode needs symbol data for type symbols. Read it now.
                if strings.HasPrefix(name, "type.") {
                        s.P = r.BytesAt(r.DataOff(i), r.DataSize(i))
+                       s.Attr.Set(sym.AttrReadOnly, r.ReadOnly())
                        s.Size = int64(osym.Siz)
                }
 
@@ -422,6 +426,7 @@ func LoadFull(l *Loader, r *goobj2.Reader, lib *sym.Library, localSymVersion int
 
                // Symbol data
                s.P = r.BytesAt(r.DataOff(i), datasize)
+               s.Attr.Set(sym.AttrReadOnly, r.ReadOnly())
 
                // Aux symbol info
                isym := -1