1
0
mirror of https://github.com/golang/go synced 2024-11-19 07:14:45 -07:00

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 Textp *LSym
Etextp *LSym Etextp *LSym
Errors int Errors int
RefsWritten int // Number of symbol references already written to object file.
// state for writing objects // state for writing objects
Text []*LSym Text []*LSym

View File

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