]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/link: use read-only mmap to back selected symbol name strings
authorThan McIntosh <thanm@google.com>
Thu, 25 Apr 2019 14:35:47 +0000 (10:35 -0400)
committerThan McIntosh <thanm@google.com>
Thu, 25 Apr 2019 23:42:12 +0000 (23:42 +0000)
When reading symbol names from an object file, if a name does not
need fixup (conversion of "". to package path), then generate
a string whose backing store is in read-only memory (from an mmap
of the object file), avoiding the need for an allocation. This
yields a modest reduction in total linker heap use.

Change-Id: I95719c93026b6cc82eb6947a9d14063cf3a6679c
Reviewed-on: https://go-review.googlesource.com/c/go/+/173938
Run-TryBot: Than McIntosh <thanm@google.com>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>

src/cmd/internal/bio/buf.go
src/cmd/link/internal/objfile/objfile.go

index 388105c3c760a48fc4b54521536266963908f090..544f7edca9e38de4dad72ecfa32cb8e14d408cce 100644 (file)
@@ -129,3 +129,15 @@ func (r *Reader) Slice(length uint64) ([]byte, bool, error) {
        }
        return data, false, nil
 }
+
+// SliceRO returns a slice containing the next length bytes of r
+// backed by a read-only mmap'd data. If the mmap cannot be
+// established (limit exceeded, region too small, etc) a nil slice
+// will be returned. If mmap succeeds, it will never be unmapped.
+func (r *Reader) SliceRO(length uint64) []byte {
+       data, ok := r.sliceOS(length)
+       if ok {
+               return data
+       }
+       return nil
+}
index 4f30f58dfc1c3a0f7244e765b8baf35a6bbad7de..7db4ac974bb4ad748beac55af8b377eca91fac4a 100644 (file)
@@ -23,6 +23,7 @@ import (
        "os"
        "strconv"
        "strings"
+       "unsafe"
 )
 
 const (
@@ -58,6 +59,9 @@ type objReader struct {
        funcdataoff []int64
        file        []*sym.Symbol
 
+       roObject      []byte // from read-only mmap of object file
+       objFileOffset int64  // offset of object data from start of file
+
        dataReadOnly bool // whether data is backed by read-only memory
 }
 
@@ -78,6 +82,10 @@ const (
 // The symbols loaded are added to syms.
 func Load(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, lib *sym.Library, length int64, pn string, flags int) int {
        start := f.Offset()
+       roObject := f.SliceRO(uint64(length))
+       if roObject != nil {
+               f.Seek(int64(-length), os.SEEK_CUR)
+       }
        r := &objReader{
                rd:              f,
                lib:             lib,
@@ -87,6 +95,8 @@ func Load(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, lib *sym.Library, le
                dupSym:          &sym.Symbol{Name: ".dup"},
                localSymVersion: syms.IncVersion(),
                flags:           flags,
+               roObject:        roObject,
+               objFileOffset:   start,
        }
        r.loadObjFile()
        if f.Offset() != start+length {
@@ -136,7 +146,7 @@ func (r *objReader) loadObjFile() {
        r.readSlices()
 
        // Data section
-       r.data, r.dataReadOnly, err = r.rd.Slice(uint64(r.dataSize))
+       err = r.readDataSection()
        if err != nil {
                log.Fatalf("%s: error reading %s", r.pn, err)
        }
@@ -176,6 +186,18 @@ func (r *objReader) readSlices() {
        r.file = make([]*sym.Symbol, n)
 }
 
+func (r *objReader) readDataSection() (err error) {
+       if r.roObject != nil {
+               dOffset := r.rd.Offset() - r.objFileOffset
+               r.data, r.dataReadOnly, err =
+                       r.roObject[dOffset:dOffset+int64(r.dataSize)], true, nil
+               r.rd.Seek(int64(r.dataSize), os.SEEK_CUR)
+               return
+       }
+       r.data, r.dataReadOnly, err = r.rd.Slice(uint64(r.dataSize))
+       return
+}
+
 // Symbols are prefixed so their content doesn't get confused with the magic footer.
 const symPrefix = 0xfe
 
@@ -544,6 +566,20 @@ func (r *objReader) readData() []byte {
        return p
 }
 
+type stringHeader struct {
+       str unsafe.Pointer
+       len int
+}
+
+func mkROString(rodata []byte) string {
+       if len(rodata) == 0 {
+               return ""
+       }
+       ss := stringHeader{str: unsafe.Pointer(&rodata[0]), len: len(rodata)}
+       s := *(*string)(unsafe.Pointer(&ss))
+       return s
+}
+
 // readSymName reads a symbol name, replacing all "". with pkg.
 func (r *objReader) readSymName() string {
        pkg := objabi.PathToPrefix(r.lib.Pkg)
@@ -555,6 +591,7 @@ func (r *objReader) readSymName() string {
        if cap(r.rdBuf) < n {
                r.rdBuf = make([]byte, 2*n)
        }
+       sOffset := r.rd.Offset() - r.objFileOffset
        origName, err := r.rd.Peek(n)
        if err == bufio.ErrBufferFull {
                // Long symbol names are rare but exist. One source is type
@@ -565,10 +602,16 @@ func (r *objReader) readSymName() string {
                log.Fatalf("%s: error reading symbol: %v", r.pn, err)
        }
        adjName := r.rdBuf[:0]
+       nPkgRefs := 0
        for {
                i := bytes.Index(origName, emptyPkg)
                if i == -1 {
-                       s := string(append(adjName, origName...))
+                       var s string
+                       if r.roObject != nil && nPkgRefs == 0 {
+                               s = mkROString(r.roObject[sOffset : sOffset+int64(n)])
+                       } else {
+                               s = string(append(adjName, origName...))
+                       }
                        // Read past the peeked origName, now that we're done with it,
                        // using the rfBuf (also no longer used) as the scratch space.
                        // TODO: use bufio.Reader.Discard if available instead?
@@ -578,6 +621,7 @@ func (r *objReader) readSymName() string {
                        r.rdBuf = adjName[:0] // in case 2*n wasn't enough
                        return s
                }
+               nPkgRefs++
                adjName = append(adjName, origName[:i]...)
                adjName = append(adjName, pkg...)
                adjName = append(adjName, '.')