1
0
mirror of https://github.com/golang/go synced 2024-09-29 16:14:28 -06:00

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: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
This commit is contained in:
Than McIntosh 2019-04-25 10:35:47 -04:00
parent 4598c23c6c
commit 9ac471a87d
2 changed files with 58 additions and 2 deletions

View 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
}

View 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, '.')