Commit 9ac471a8 authored by Than McIntosh's avatar Than McIntosh

cmd/link: use read-only mmap to back selected symbol name strings

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: default avatarBrad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: default avatarCherry Zhang <cherryyz@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
parent 4598c23c
...@@ -129,3 +129,15 @@ func (r *Reader) Slice(length uint64) ([]byte, bool, error) { ...@@ -129,3 +129,15 @@ func (r *Reader) Slice(length uint64) ([]byte, bool, error) {
} }
return data, false, nil 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
}
...@@ -23,6 +23,7 @@ import ( ...@@ -23,6 +23,7 @@ import (
"os" "os"
"strconv" "strconv"
"strings" "strings"
"unsafe"
) )
const ( const (
...@@ -58,6 +59,9 @@ type objReader struct { ...@@ -58,6 +59,9 @@ type objReader struct {
funcdataoff []int64 funcdataoff []int64
file []*sym.Symbol 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 dataReadOnly bool // whether data is backed by read-only memory
} }
...@@ -78,6 +82,10 @@ const ( ...@@ -78,6 +82,10 @@ const (
// The symbols loaded are added to syms. // 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 { func Load(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, lib *sym.Library, length int64, pn string, flags int) int {
start := f.Offset() start := f.Offset()
roObject := f.SliceRO(uint64(length))
if roObject != nil {
f.Seek(int64(-length), os.SEEK_CUR)
}
r := &objReader{ r := &objReader{
rd: f, rd: f,
lib: lib, lib: lib,
...@@ -87,6 +95,8 @@ func Load(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, lib *sym.Library, le ...@@ -87,6 +95,8 @@ func Load(arch *sys.Arch, syms *sym.Symbols, f *bio.Reader, lib *sym.Library, le
dupSym: &sym.Symbol{Name: ".dup"}, dupSym: &sym.Symbol{Name: ".dup"},
localSymVersion: syms.IncVersion(), localSymVersion: syms.IncVersion(),
flags: flags, flags: flags,
roObject: roObject,
objFileOffset: start,
} }
r.loadObjFile() r.loadObjFile()
if f.Offset() != start+length { if f.Offset() != start+length {
...@@ -136,7 +146,7 @@ func (r *objReader) loadObjFile() { ...@@ -136,7 +146,7 @@ func (r *objReader) loadObjFile() {
r.readSlices() r.readSlices()
// Data section // Data section
r.data, r.dataReadOnly, err = r.rd.Slice(uint64(r.dataSize)) err = r.readDataSection()
if err != nil { if err != nil {
log.Fatalf("%s: error reading %s", r.pn, err) log.Fatalf("%s: error reading %s", r.pn, err)
} }
...@@ -176,6 +186,18 @@ func (r *objReader) readSlices() { ...@@ -176,6 +186,18 @@ func (r *objReader) readSlices() {
r.file = make([]*sym.Symbol, n) 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. // Symbols are prefixed so their content doesn't get confused with the magic footer.
const symPrefix = 0xfe const symPrefix = 0xfe
...@@ -544,6 +566,20 @@ func (r *objReader) readData() []byte { ...@@ -544,6 +566,20 @@ func (r *objReader) readData() []byte {
return p 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. // readSymName reads a symbol name, replacing all "". with pkg.
func (r *objReader) readSymName() string { func (r *objReader) readSymName() string {
pkg := objabi.PathToPrefix(r.lib.Pkg) pkg := objabi.PathToPrefix(r.lib.Pkg)
...@@ -555,6 +591,7 @@ func (r *objReader) readSymName() string { ...@@ -555,6 +591,7 @@ func (r *objReader) readSymName() string {
if cap(r.rdBuf) < n { if cap(r.rdBuf) < n {
r.rdBuf = make([]byte, 2*n) r.rdBuf = make([]byte, 2*n)
} }
sOffset := r.rd.Offset() - r.objFileOffset
origName, err := r.rd.Peek(n) origName, err := r.rd.Peek(n)
if err == bufio.ErrBufferFull { if err == bufio.ErrBufferFull {
// Long symbol names are rare but exist. One source is type // Long symbol names are rare but exist. One source is type
...@@ -565,10 +602,16 @@ func (r *objReader) readSymName() string { ...@@ -565,10 +602,16 @@ func (r *objReader) readSymName() string {
log.Fatalf("%s: error reading symbol: %v", r.pn, err) log.Fatalf("%s: error reading symbol: %v", r.pn, err)
} }
adjName := r.rdBuf[:0] adjName := r.rdBuf[:0]
nPkgRefs := 0
for { for {
i := bytes.Index(origName, emptyPkg) i := bytes.Index(origName, emptyPkg)
if i == -1 { 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, // Read past the peeked origName, now that we're done with it,
// using the rfBuf (also no longer used) as the scratch space. // using the rfBuf (also no longer used) as the scratch space.
// TODO: use bufio.Reader.Discard if available instead? // TODO: use bufio.Reader.Discard if available instead?
...@@ -578,6 +621,7 @@ func (r *objReader) readSymName() string { ...@@ -578,6 +621,7 @@ func (r *objReader) readSymName() string {
r.rdBuf = adjName[:0] // in case 2*n wasn't enough r.rdBuf = adjName[:0] // in case 2*n wasn't enough
return s return s
} }
nPkgRefs++
adjName = append(adjName, origName[:i]...) adjName = append(adjName, origName[:i]...)
adjName = append(adjName, pkg...) adjName = append(adjName, pkg...)
adjName = append(adjName, '.') adjName = append(adjName, '.')
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment