cmd/internal/obj: more idiomatic object writer

Change-Id: I41722ee605ea76a6b52e8a7e1e10f2293cef1a7a
Reviewed-on: https://go-review.googlesource.com/21371
Reviewed-by: David Crawshaw <crawshaw@golang.org>
This commit is contained in:
Shahar Kohanim 2016-03-31 12:59:05 +03:00 committed by Brad Fitzpatrick
parent b91cc53033
commit 014f3e1e09
2 changed files with 243 additions and 221 deletions

View file

@ -658,7 +658,6 @@ type Link struct {
Textp *LSym
Etextp *LSym
Errors int
RefsWritten int // Number of symbol references already written to object file.
// state for writing objects
Text []*LSym

View file

@ -108,6 +108,7 @@
package obj
import (
"bufio"
"fmt"
"log"
"path/filepath"
@ -120,7 +121,7 @@ import (
// does not write out object files.
func Writeobjdirect(ctxt *Link, b *Biobuf) {
Flushplist(ctxt)
Writeobjfile(ctxt, b)
WriteObjFile(ctxt, b)
}
func Flushplist(ctxt *Link) {
@ -309,18 +310,32 @@ func flushplist(ctxt *Link, freeProgs bool) {
}
}
type sectionLengths struct {
data int
reloc int
pcdata int
autom int
funcdata int
file int
// objWriter writes Go object files.
type objWriter struct {
wr *bufio.Writer
ctxt *Link
// Temporary buffer for zigzag int writing.
varintbuf [10]uint8
// Provide the the index of a symbol reference by symbol name.
// One map for versioned symbols and one for unversioned symbols.
// Used for deduplicating the symbol reference list.
refIdx map[string]int
vrefIdx map[string]int
// Number of objects written of each type.
nRefs int
nData int
nReloc int
nPcdata int
nAutom int
nFuncdata int
nFile int
}
func (l *sectionLengths) add(s *LSym) {
l.data += len(s.P)
l.reloc += len(s.R)
func (w *objWriter) addLengths(s *LSym) {
w.nData += len(s.P)
w.nReloc += len(s.R)
if s.Type != STEXT {
return
@ -336,102 +351,106 @@ func (l *sectionLengths) add(s *LSym) {
data += len(pc.Pcdata[i].P)
}
l.data += data
l.pcdata += len(pc.Pcdata)
w.nData += data
w.nPcdata += len(pc.Pcdata)
autom := 0
for a := s.Autom; a != nil; a = a.Link {
autom++
}
l.autom += autom
l.funcdata += len(pc.Funcdataoff)
l.file += len(pc.File)
w.nAutom += autom
w.nFuncdata += len(pc.Funcdataoff)
w.nFile += len(pc.File)
}
func wrlengths(b *Biobuf, sl sectionLengths) {
wrint(b, int64(sl.data))
wrint(b, int64(sl.reloc))
wrint(b, int64(sl.pcdata))
wrint(b, int64(sl.autom))
wrint(b, int64(sl.funcdata))
wrint(b, int64(sl.file))
func (w *objWriter) writeLengths() {
w.writeInt(int64(w.nData))
w.writeInt(int64(w.nReloc))
w.writeInt(int64(w.nPcdata))
w.writeInt(int64(w.nAutom))
w.writeInt(int64(w.nFuncdata))
w.writeInt(int64(w.nFile))
}
func Writeobjfile(ctxt *Link, b *Biobuf) {
// Emit header.
Bputc(b, 0)
Bputc(b, 0)
fmt.Fprintf(b, "go13ld")
Bputc(b, 1) // version
// Emit autolib.
for _, pkg := range ctxt.Imports {
wrstring(b, pkg)
func newObjWriter(ctxt *Link, b *Biobuf) *objWriter {
return &objWriter{
ctxt: ctxt,
wr: b.w,
vrefIdx: make(map[string]int),
refIdx: make(map[string]int),
}
wrstring(b, "")
}
var lengths sectionLengths
func WriteObjFile(ctxt *Link, b *Biobuf) {
w := newObjWriter(ctxt, b)
// Emit symbol references.
// Magic header
w.wr.WriteString("\x00\x00go13ld")
// Version
w.wr.WriteByte(1)
// Autolib
for _, pkg := range ctxt.Imports {
w.writeString(pkg)
}
w.writeString("")
// Symbol references
for _, s := range ctxt.Text {
writerefs(ctxt, b, s)
lengths.add(s)
w.writeRefs(s)
w.addLengths(s)
}
for _, s := range ctxt.Data {
writerefs(ctxt, b, s)
lengths.add(s)
w.writeRefs(s)
w.addLengths(s)
}
Bputc(b, 0xff)
// End symbol references
w.wr.WriteByte(0xff)
wrlengths(b, lengths)
// Lengths
w.writeLengths()
// Write data block
// Data block
for _, s := range ctxt.Text {
b.w.Write(s.P)
w.wr.Write(s.P)
pc := s.Pcln
b.w.Write(pc.Pcsp.P)
b.w.Write(pc.Pcfile.P)
b.w.Write(pc.Pcline.P)
w.wr.Write(pc.Pcsp.P)
w.wr.Write(pc.Pcfile.P)
w.wr.Write(pc.Pcline.P)
for i := 0; i < len(pc.Pcdata); i++ {
b.w.Write(pc.Pcdata[i].P)
w.wr.Write(pc.Pcdata[i].P)
}
}
for _, s := range ctxt.Data {
b.w.Write(s.P)
w.wr.Write(s.P)
}
// Emit symbols.
// Symbols
for _, s := range ctxt.Text {
writesym(ctxt, b, s)
w.writeSym(s)
}
for _, s := range ctxt.Data {
writesym(ctxt, b, s)
w.writeSym(s)
}
// Emit footer.
Bputc(b, 0xff)
Bputc(b, 0xff)
fmt.Fprintf(b, "go13ld")
// Magic footer
w.wr.WriteString("\xff\xffgo13ld")
}
// Provide the the index of a symbol reference by symbol name.
// One map for versioned symbols and one for unversioned symbols.
// Used for deduplicating the symbol reference list.
var refIdx = make(map[string]int)
var vrefIdx = make(map[string]int)
// Symbols are prefixed so their content doesn't get confused with the magic footer.
const symPrefix = 0xfe
func wrref(ctxt *Link, b *Biobuf, s *LSym, isPath bool) {
func (w *objWriter) writeRef(s *LSym, isPath bool) {
if s == nil || s.RefIdx != 0 {
return
}
var m map[string]int
switch s.Version {
case 0:
m = refIdx
m = w.refIdx
case 1:
m = vrefIdx
m = w.vrefIdx
default:
log.Fatalf("%s: invalid version number %d", s.Name, s.Version)
}
@ -441,111 +460,117 @@ func wrref(ctxt *Link, b *Biobuf, s *LSym, isPath bool) {
s.RefIdx = idx
return
}
Bputc(b, 0xfe)
w.wr.WriteByte(symPrefix)
if isPath {
wrstring(b, filepath.ToSlash(s.Name))
w.writeString(filepath.ToSlash(s.Name))
} else {
wrstring(b, s.Name)
w.writeString(s.Name)
}
wrint(b, int64(s.Version))
ctxt.RefsWritten++
s.RefIdx = ctxt.RefsWritten
m[s.Name] = ctxt.RefsWritten
w.writeInt(int64(s.Version))
w.nRefs++
s.RefIdx = w.nRefs
m[s.Name] = w.nRefs
}
func writerefs(ctxt *Link, b *Biobuf, s *LSym) {
wrref(ctxt, b, s, false)
wrref(ctxt, b, s.Gotype, false)
func (w *objWriter) writeRefs(s *LSym) {
w.writeRef(s, false)
w.writeRef(s.Gotype, false)
for i := range s.R {
wrref(ctxt, b, s.R[i].Sym, false)
w.writeRef(s.R[i].Sym, false)
}
if s.Type == STEXT {
for a := s.Autom; a != nil; a = a.Link {
wrref(ctxt, b, a.Asym, false)
wrref(ctxt, b, a.Gotype, false)
w.writeRef(a.Asym, false)
w.writeRef(a.Gotype, false)
}
pc := s.Pcln
for _, d := range pc.Funcdata {
wrref(ctxt, b, d, false)
w.writeRef(d, false)
}
for _, f := range pc.File {
wrref(ctxt, b, f, true)
w.writeRef(f, true)
}
}
}
func writesym(ctxt *Link, b *Biobuf, s *LSym) {
if ctxt.Debugasm != 0 {
fmt.Fprintf(ctxt.Bso, "%s ", s.Name)
if s.Version != 0 {
fmt.Fprintf(ctxt.Bso, "v=%d ", s.Version)
func (w *objWriter) writeSymDebug(s *LSym) {
ctxt := w.ctxt
fmt.Fprintf(ctxt.Bso, "%s ", s.Name)
if s.Version != 0 {
fmt.Fprintf(ctxt.Bso, "v=%d ", s.Version)
}
if s.Type != 0 {
fmt.Fprintf(ctxt.Bso, "t=%d ", s.Type)
}
if s.Dupok {
fmt.Fprintf(ctxt.Bso, "dupok ")
}
if s.Cfunc {
fmt.Fprintf(ctxt.Bso, "cfunc ")
}
if s.Nosplit {
fmt.Fprintf(ctxt.Bso, "nosplit ")
}
fmt.Fprintf(ctxt.Bso, "size=%d", s.Size)
if s.Type == STEXT {
fmt.Fprintf(ctxt.Bso, " args=%#x locals=%#x", uint64(s.Args), uint64(s.Locals))
if s.Leaf {
fmt.Fprintf(ctxt.Bso, " leaf")
}
if s.Type != 0 {
fmt.Fprintf(ctxt.Bso, "t=%d ", s.Type)
}
fmt.Fprintf(ctxt.Bso, "\n")
for p := s.Text; p != nil; p = p.Link {
fmt.Fprintf(ctxt.Bso, "\t%#04x %v\n", uint(int(p.Pc)), p)
}
var c int
var j int
for i := 0; i < len(s.P); {
fmt.Fprintf(ctxt.Bso, "\t%#04x", uint(i))
for j = i; j < i+16 && j < len(s.P); j++ {
fmt.Fprintf(ctxt.Bso, " %02x", s.P[j])
}
if s.Dupok {
fmt.Fprintf(ctxt.Bso, "dupok ")
for ; j < i+16; j++ {
fmt.Fprintf(ctxt.Bso, " ")
}
if s.Cfunc {
fmt.Fprintf(ctxt.Bso, "cfunc ")
}
if s.Nosplit {
fmt.Fprintf(ctxt.Bso, "nosplit ")
}
fmt.Fprintf(ctxt.Bso, "size=%d", s.Size)
if s.Type == STEXT {
fmt.Fprintf(ctxt.Bso, " args=%#x locals=%#x", uint64(s.Args), uint64(s.Locals))
if s.Leaf {
fmt.Fprintf(ctxt.Bso, " leaf")
fmt.Fprintf(ctxt.Bso, " ")
for j = i; j < i+16 && j < len(s.P); j++ {
c = int(s.P[j])
if ' ' <= c && c <= 0x7e {
fmt.Fprintf(ctxt.Bso, "%c", c)
} else {
fmt.Fprintf(ctxt.Bso, ".")
}
}
fmt.Fprintf(ctxt.Bso, "\n")
for p := s.Text; p != nil; p = p.Link {
fmt.Fprintf(ctxt.Bso, "\t%#04x %v\n", uint(int(p.Pc)), p)
}
var c int
var j int
for i := 0; i < len(s.P); {
fmt.Fprintf(ctxt.Bso, "\t%#04x", uint(i))
for j = i; j < i+16 && j < len(s.P); j++ {
fmt.Fprintf(ctxt.Bso, " %02x", s.P[j])
}
for ; j < i+16; j++ {
fmt.Fprintf(ctxt.Bso, " ")
}
fmt.Fprintf(ctxt.Bso, " ")
for j = i; j < i+16 && j < len(s.P); j++ {
c = int(s.P[j])
if ' ' <= c && c <= 0x7e {
fmt.Fprintf(ctxt.Bso, "%c", c)
} else {
fmt.Fprintf(ctxt.Bso, ".")
}
}
fmt.Fprintf(ctxt.Bso, "\n")
i += 16
}
sort.Sort(relocByOff(s.R)) // generate stable output
for _, r := range s.R {
name := ""
if r.Sym != nil {
name = r.Sym.Name
}
if ctxt.Arch.Thechar == '5' || ctxt.Arch.Thechar == '9' {
fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s+%x\n", int(r.Off), r.Siz, r.Type, name, uint64(int64(r.Add)))
} else {
fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s+%d\n", int(r.Off), r.Siz, r.Type, name, int64(r.Add))
}
}
i += 16
}
Bputc(b, 0xfe)
wrint(b, int64(s.Type))
wrsym(b, s)
sort.Sort(relocByOff(s.R)) // generate stable output
for _, r := range s.R {
name := ""
if r.Sym != nil {
name = r.Sym.Name
}
if ctxt.Arch.Thechar == '5' || ctxt.Arch.Thechar == '9' {
fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s+%x\n", int(r.Off), r.Siz, r.Type, name, uint64(int64(r.Add)))
} else {
fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s+%d\n", int(r.Off), r.Siz, r.Type, name, int64(r.Add))
}
}
}
func (w *objWriter) writeSym(s *LSym) {
ctxt := w.ctxt
if ctxt.Debugasm != 0 {
w.writeSymDebug(s)
}
w.wr.WriteByte(symPrefix)
w.writeInt(int64(s.Type))
w.writeRefIndex(s)
flags := int64(0)
if s.Dupok {
flags |= 1
@ -553,112 +578,110 @@ func writesym(ctxt *Link, b *Biobuf, s *LSym) {
if s.Local {
flags |= 1 << 1
}
wrint(b, flags)
wrint(b, s.Size)
wrsym(b, s.Gotype)
wrint(b, int64(len(s.P)))
w.writeInt(flags)
w.writeInt(s.Size)
w.writeRefIndex(s.Gotype)
w.writeInt(int64(len(s.P)))
wrint(b, int64(len(s.R)))
w.writeInt(int64(len(s.R)))
var r *Reloc
for i := 0; i < len(s.R); i++ {
r = &s.R[i]
wrint(b, int64(r.Off))
wrint(b, int64(r.Siz))
wrint(b, int64(r.Type))
wrint(b, r.Add)
wrsym(b, r.Sym)
w.writeInt(int64(r.Off))
w.writeInt(int64(r.Siz))
w.writeInt(int64(r.Type))
w.writeInt(r.Add)
w.writeRefIndex(r.Sym)
}
if s.Type == STEXT {
wrint(b, int64(s.Args))
wrint(b, int64(s.Locals))
if s.Nosplit {
wrint(b, 1)
} else {
wrint(b, 0)
}
flags := int64(0)
if s.Leaf {
flags |= 1
}
if s.Cfunc {
flags |= 1 << 1
}
if s.ReflectMethod {
flags |= 1 << 2
}
wrint(b, flags)
n := 0
for a := s.Autom; a != nil; a = a.Link {
n++
}
wrint(b, int64(n))
for a := s.Autom; a != nil; a = a.Link {
wrsym(b, a.Asym)
wrint(b, int64(a.Aoffset))
if a.Name == NAME_AUTO {
wrint(b, A_AUTO)
} else if a.Name == NAME_PARAM {
wrint(b, A_PARAM)
} else {
log.Fatalf("%s: invalid local variable type %d", s.Name, a.Name)
}
wrsym(b, a.Gotype)
}
if s.Type != STEXT {
return
}
pc := s.Pcln
wrint(b, int64(len(pc.Pcsp.P)))
wrint(b, int64(len(pc.Pcfile.P)))
wrint(b, int64(len(pc.Pcline.P)))
wrint(b, int64(len(pc.Pcdata)))
for i := 0; i < len(pc.Pcdata); i++ {
wrint(b, int64(len(pc.Pcdata[i].P)))
}
wrint(b, int64(len(pc.Funcdataoff)))
for i := 0; i < len(pc.Funcdataoff); i++ {
wrsym(b, pc.Funcdata[i])
}
for i := 0; i < len(pc.Funcdataoff); i++ {
wrint(b, pc.Funcdataoff[i])
}
wrint(b, int64(len(pc.File)))
for _, f := range pc.File {
wrsym(b, f)
w.writeInt(int64(s.Args))
w.writeInt(int64(s.Locals))
if s.Nosplit {
w.writeInt(1)
} else {
w.writeInt(0)
}
flags = int64(0)
if s.Leaf {
flags |= 1
}
if s.Cfunc {
flags |= 1 << 1
}
if s.ReflectMethod {
flags |= 1 << 2
}
w.writeInt(flags)
n := 0
for a := s.Autom; a != nil; a = a.Link {
n++
}
w.writeInt(int64(n))
for a := s.Autom; a != nil; a = a.Link {
w.writeRefIndex(a.Asym)
w.writeInt(int64(a.Aoffset))
if a.Name == NAME_AUTO {
w.writeInt(A_AUTO)
} else if a.Name == NAME_PARAM {
w.writeInt(A_PARAM)
} else {
log.Fatalf("%s: invalid local variable type %d", s.Name, a.Name)
}
w.writeRefIndex(a.Gotype)
}
pc := s.Pcln
w.writeInt(int64(len(pc.Pcsp.P)))
w.writeInt(int64(len(pc.Pcfile.P)))
w.writeInt(int64(len(pc.Pcline.P)))
w.writeInt(int64(len(pc.Pcdata)))
for i := 0; i < len(pc.Pcdata); i++ {
w.writeInt(int64(len(pc.Pcdata[i].P)))
}
w.writeInt(int64(len(pc.Funcdataoff)))
for i := 0; i < len(pc.Funcdataoff); i++ {
w.writeRefIndex(pc.Funcdata[i])
}
for i := 0; i < len(pc.Funcdataoff); i++ {
w.writeInt(pc.Funcdataoff[i])
}
w.writeInt(int64(len(pc.File)))
for _, f := range pc.File {
w.writeRefIndex(f)
}
}
// Reusable buffer to avoid allocations.
// This buffer was responsible for 15% of gc's allocations.
var varintbuf [10]uint8
func wrint(b *Biobuf, sval int64) {
func (w *objWriter) writeInt(sval int64) {
var v uint64
uv := (uint64(sval) << 1) ^ uint64(int64(sval>>63))
p := varintbuf[:]
p := w.varintbuf[:]
for v = uv; v >= 0x80; v >>= 7 {
p[0] = uint8(v | 0x80)
p = p[1:]
}
p[0] = uint8(v)
p = p[1:]
b.Write(varintbuf[:len(varintbuf)-len(p)])
w.wr.Write(w.varintbuf[:len(w.varintbuf)-len(p)])
}
func wrstring(b *Biobuf, s string) {
wrint(b, int64(len(s)))
b.w.WriteString(s)
func (w *objWriter) writeString(s string) {
w.writeInt(int64(len(s)))
w.wr.WriteString(s)
}
func wrsym(b *Biobuf, s *LSym) {
func (w *objWriter) writeRefIndex(s *LSym) {
if s == nil {
wrint(b, 0)
w.writeInt(0)
return
}
if s.RefIdx == 0 {
log.Fatalln("writing an unreferenced symbol", s.Name)
}
wrint(b, int64(s.RefIdx))
w.writeInt(int64(s.RefIdx))
}
// relocByOff sorts relocations by their offsets.