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