Commit 0d7404c8 authored by Cherry Zhang's avatar Cherry Zhang

[dev.link] cmd/link, cmd/internal/goobj2: mmap object file in -newobj mode

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: default avatarThan McIntosh <thanm@google.com>
parent 24950952
......@@ -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
}
......@@ -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)
......
......@@ -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
......
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