From: Cherry Zhang Date: Thu, 22 Aug 2019 21:26:41 +0000 (-0400) Subject: [dev.link] cmd/internal/obj: write object file in new format X-Git-Tag: go1.14beta1~292^2^2~80 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=2c484c0356c5e13a480b3842de0d345224a7bbf8;p=gostls13.git [dev.link] cmd/internal/obj: write object file in new format If -newobj is set, write object file in new format, which uses indices for symbol references instead of symbol names. The file format is described at the beginning of cmd/internal/goobj2/objfile.go. A new package, cmd/internal/goobj2, is introduced for reading and writing new object files. (The package name is temporary.) It is written in a way that trys to make the encoding as regular as possible, and the reader and writer as symmetric as possible. This is incomplete, and currently nothing will consume the new object file. Change-Id: Ifefedbf6456d760d15a9f40a28af6486c93100fe Reviewed-on: https://go-review.googlesource.com/c/go/+/196030 Run-TryBot: Cherry Zhang TryBot-Result: Gobot Gobot Reviewed-by: Than McIntosh --- diff --git a/src/cmd/compile/internal/gc/iexport.go b/src/cmd/compile/internal/gc/iexport.go index da81331b82..39f7770136 100644 --- a/src/cmd/compile/internal/gc/iexport.go +++ b/src/cmd/compile/internal/gc/iexport.go @@ -202,7 +202,7 @@ import ( "bufio" "bytes" "cmd/compile/internal/types" - "cmd/internal/obj" + "cmd/internal/goobj2" "cmd/internal/src" "encoding/binary" "fmt" @@ -980,7 +980,7 @@ func (w *exportWriter) linkname(s *types.Sym) { func (w *exportWriter) symIdx(s *types.Sym) { if Ctxt.Flag_newobj { lsym := s.Linksym() - if lsym.PkgIdx > obj.PkgIdxSelf || lsym.PkgIdx == obj.PkgIdxInvalid || s.Linkname != "" { + if lsym.PkgIdx > goobj2.PkgIdxSelf || lsym.PkgIdx == goobj2.PkgIdxInvalid || s.Linkname != "" { w.int64(-1) } else { w.int64(int64(lsym.SymIdx)) diff --git a/src/cmd/dist/buildtool.go b/src/cmd/dist/buildtool.go index f27ea17230..e85dd9a660 100644 --- a/src/cmd/dist/buildtool.go +++ b/src/cmd/dist/buildtool.go @@ -54,6 +54,7 @@ var bootstrapDirs = []string{ "cmd/internal/gcprog", "cmd/internal/dwarf", "cmd/internal/edit", + "cmd/internal/goobj2", "cmd/internal/objabi", "cmd/internal/obj", "cmd/internal/obj/arm", diff --git a/src/cmd/internal/goobj2/funcinfo.go b/src/cmd/internal/goobj2/funcinfo.go new file mode 100644 index 0000000000..5938b5f920 --- /dev/null +++ b/src/cmd/internal/goobj2/funcinfo.go @@ -0,0 +1,114 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package goobj2 + +import ( + "bytes" + "encoding/binary" +) + +// FuncInfo is serialized as a symbol (aux symbol). The symbol data is +// the binary encoding of the struct below. +// +// TODO: make each pcdata a separate symbol? +type FuncInfo struct { + NoSplit uint8 + Flags uint8 + + Args uint32 + Locals uint32 + + Pcsp uint32 + Pcfile uint32 + Pcline uint32 + Pcinline uint32 + Pcdata []uint32 + PcdataEnd uint32 + Funcdataoff []uint32 + File []SymRef // TODO: just use string? + + // TODO: InlTree +} + +const ( + FuncFlagLeaf = 1 << iota + FuncFlagCFunc + FuncFlagReflectMethod + FuncFlagShared // This is really silly + FuncFlagTopFrame +) + +func (a *FuncInfo) Write(w *bytes.Buffer) { + w.WriteByte(a.NoSplit) + w.WriteByte(a.Flags) + + var b [4]byte + writeUint32 := func(x uint32) { + binary.LittleEndian.PutUint32(b[:], x) + w.Write(b[:]) + } + + writeUint32(a.Args) + writeUint32(a.Locals) + + writeUint32(a.Pcsp) + writeUint32(a.Pcfile) + writeUint32(a.Pcline) + writeUint32(a.Pcinline) + writeUint32(uint32(len(a.Pcdata))) + for _, x := range a.Pcdata { + writeUint32(x) + } + writeUint32(a.PcdataEnd) + writeUint32(uint32(len(a.Funcdataoff))) + for _, x := range a.Funcdataoff { + writeUint32(x) + } + writeUint32(uint32(len(a.File))) + for _, f := range a.File { + writeUint32(f.PkgIdx) + writeUint32(f.SymIdx) + } + + // TODO: InlTree +} + +func (a *FuncInfo) Read(b []byte) { + a.NoSplit = b[0] + a.Flags = b[1] + b = b[2:] + + readUint32 := func() uint32 { + x := binary.LittleEndian.Uint32(b) + b = b[4:] + return x + } + + a.Args = readUint32() + a.Locals = readUint32() + + a.Pcsp = readUint32() + a.Pcfile = readUint32() + a.Pcline = readUint32() + a.Pcinline = readUint32() + pcdatalen := readUint32() + a.Pcdata = make([]uint32, pcdatalen) + for i := range a.Pcdata { + a.Pcdata[i] = readUint32() + } + a.PcdataEnd = readUint32() + funcdataofflen := readUint32() + a.Funcdataoff = make([]uint32, funcdataofflen) + for i := range a.Funcdataoff { + a.Funcdataoff[i] = readUint32() + } + filelen := readUint32() + a.File = make([]SymRef, filelen) + for i := range a.File { + a.File[i] = SymRef{readUint32(), readUint32()} + } + + // TODO: InlTree +} diff --git a/src/cmd/internal/goobj2/objfile.go b/src/cmd/internal/goobj2/objfile.go new file mode 100644 index 0000000000..eb9290a699 --- /dev/null +++ b/src/cmd/internal/goobj2/objfile.go @@ -0,0 +1,513 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Go new object file format, reading and writing. + +package goobj2 // TODO: replace the goobj package? + +import ( + "cmd/internal/bio" + "encoding/binary" + "errors" + "fmt" + "io" +) + +// New object file format. +// +// Header struct { +// Magic [...]byte // "\x00go114LD" +// // TODO: Fingerprint +// Offsets [...]uint32 // byte offset of each block below +// } +// +// Strings [...]struct { +// Len uint32 +// Data [...]byte +// } +// +// PkgIndex [...]stringOff // TODO: add fingerprints +// +// SymbolDefs [...]struct { +// Name stringOff +// ABI uint16 +// Type uint8 +// Flag uint8 +// Size uint32 +// } +// NonPkgDefs [...]struct { // non-pkg symbol definitions +// ... // same as SymbolDefs +// } +// NonPkgRefs [...]struct { // non-pkg symbol references +// ... // same as SymbolDefs +// } +// +// RelocIndex [...]uint32 // index to Relocs +// AuxIndex [...]uint32 // index to Aux +// DataIndex [...]uint32 // offset to Data +// +// Relocs [...]struct { +// Off int32 +// Size uint8 +// Type uint8 +// Add int64 +// Sym symRef +// } +// +// Aux [...]struct { +// Type uint8 +// Sym symRef +// } +// +// Data [...]byte +// Pcdata [...]byte +// +// stringOff is a uint32 (?) offset that points to the corresponding +// string, which is a uint32 length followed by that number of bytes. +// +// symRef is struct { PkgIdx, SymIdx uint32 }. +// +// Slice type (e.g. []symRef) is encoded as a length prefix (uint32) +// followed by that number of elements. +// +// The types below correspond to the encoded data structure in the +// object file. + +// Symbol indexing. +// +// Each symbol is referenced with a pair of indices, { PkgIdx, SymIdx }, +// as the symRef struct above. +// +// PkgIdx is either a predeclared index (see PkgIdxNone below) or +// an index of an imported package. For the latter case, PkgIdx is the +// index of the package in the PkgIndex array. 0 is an invalid index. +// +// SymIdx is the index of the symbol in the given package. +// - If PkgIdx is PkgIdxSelf, SymIdx is the index of the symbol in the +// SymbolDefs array. +// - If PkgIdx is PkgIdxNone, SymIdx is the index of the symbol in the +// NonPkgDefs array (could natually overflow to NonPkgRefs array). +// - Otherwise, SymIdx is the index of the symbol in some other package's +// SymbolDefs array. +// +// {0, 0} represents a nil symbol. Otherwise PkgIdx should not be 0. +// +// RelocIndex, AuxIndex, and DataIndex contains indices/offsets to +// Relocs/Aux/Data blocks, one element per symbol, first for all the +// defined symbols, then all the defined non-package symbols, in the +// same order of SymbolDefs/NonPkgDefs arrays. For N total defined +// symbols, the array is of length N+1. The last element is the total +// number of relocations (aux symbols, data blocks, etc.). +// +// They can be accessed by index. For the i-th symbol, its relocations +// are the RelocIndex[i]-th (inclusive) to RelocIndex[i+1]-th (exclusive) +// elements in the Relocs array. Aux/Data are likewise. (The index is +// 0-based.) + +// Auxiliary symbols. +// +// Each symbol may (or may not) be associated with a number of auxiliary +// symbols. They are described in the Aux block. See Aux struct below. +// Currently a symbol's Gotype and FuncInfo are auxiliary symbols. We +// may make use of aux symbols in more cases, e.g. DWARF symbols. + +// Package Index. +const ( + PkgIdxNone = (1<<31 - 1) - iota // Non-package symbols + PkgIdxBuiltin // Predefined symbols // TODO: not used for now, we could use it for compiler-generated symbols like runtime.newobject + PkgIdxSelf // Symbols defined in the current package + PkgIdxInvalid = 0 + // The index of other referenced packages starts from 1. +) + +// Blocks +const ( + BlkPkgIdx = iota + BlkSymdef + BlkNonpkgdef + BlkNonpkgref + BlkRelocIdx + BlkAuxIdx + BlkDataIdx + BlkReloc + BlkAux + BlkData + BlkPcdata + NBlk +) + +// File header. +// TODO: probably no need to export this. +type Header struct { + Magic string + Offsets [NBlk]uint32 +} + +const Magic = "\x00go114LD" + +func (h *Header) Write(w *Writer) { + w.RawString(h.Magic) + for _, x := range h.Offsets { + w.Uint32(x) + } +} + +func (h *Header) Read(r *Reader) error { + b := r.BytesAt(0, len(Magic)) + h.Magic = string(b) + if h.Magic != Magic { + return errors.New("wrong magic, not a Go object file") + } + off := uint32(len(h.Magic)) + for i := range h.Offsets { + h.Offsets[i] = r.uint32At(off) + off += 4 + } + return nil +} + +func (h *Header) Size() int { + return len(h.Magic) + 4*len(h.Offsets) +} + +// Symbol definition. +type Sym struct { + Name string + ABI uint16 + Type uint8 + Flag uint8 + Siz uint32 +} + +const SymABIstatic = ^uint16(0) + +const ( + SymFlagDupok = 1 << iota + SymFlagLocal + SymFlagTypelink +) + +func (s *Sym) Write(w *Writer) { + w.StringRef(s.Name) + w.Uint16(s.ABI) + w.Uint8(s.Type) + w.Uint8(s.Flag) + w.Uint32(s.Siz) +} + +func (s *Sym) Read(r *Reader, off uint32) { + s.Name = r.StringRef(off) + s.ABI = r.uint16At(off + 4) + s.Type = r.uint8At(off + 6) + s.Flag = r.uint8At(off + 7) + s.Siz = r.uint32At(off + 8) +} + +func (s *Sym) Size() int { + return 4 + 2 + 1 + 1 + 4 +} + +// Symbol reference. +type SymRef struct { + PkgIdx uint32 + SymIdx uint32 +} + +func (s *SymRef) Write(w *Writer) { + w.Uint32(s.PkgIdx) + w.Uint32(s.SymIdx) +} + +func (s *SymRef) Read(r *Reader, off uint32) { + s.PkgIdx = r.uint32At(off) + s.SymIdx = r.uint32At(off + 4) +} + +func (s *SymRef) Size() int { + return 4 + 4 +} + +// Relocation. +type Reloc struct { + Off int32 + Siz uint8 + Type uint8 + Add int64 + Sym SymRef +} + +func (r *Reloc) Write(w *Writer) { + w.Uint32(uint32(r.Off)) + w.Uint8(r.Siz) + w.Uint8(r.Type) + w.Uint64(uint64(r.Add)) + r.Sym.Write(w) +} + +func (o *Reloc) Read(r *Reader, off uint32) { + o.Off = r.int32At(off) + o.Siz = r.uint8At(off + 4) + o.Type = r.uint8At(off + 5) + o.Add = r.int64At(off + 6) + o.Sym.Read(r, off+14) +} + +func (r *Reloc) Size() int { + return 4 + 1 + 1 + 8 + r.Sym.Size() +} + +// Aux symbol info. +type Aux struct { + Type uint8 + Sym SymRef +} + +// Aux Type +const ( + AuxGotype = iota + AuxFuncInfo + AuxFuncdata + + // TODO: more. DWARF? Pcdata? +) + +func (a *Aux) Write(w *Writer) { + w.Uint8(a.Type) + a.Sym.Write(w) +} + +func (a *Aux) Read(r *Reader, off uint32) { + a.Type = r.uint8At(off) + a.Sym.Read(r, off+1) +} + +func (a *Aux) Size() int { + return 1 + a.Sym.Size() +} + +type Writer struct { + wr *bio.Writer + stringMap map[string]uint32 + off uint32 // running offset +} + +func NewWriter(wr *bio.Writer) *Writer { + return &Writer{wr: wr, stringMap: make(map[string]uint32)} +} + +func (w *Writer) AddString(s string) { + if _, ok := w.stringMap[s]; ok { + return + } + w.stringMap[s] = w.off + w.Uint32(uint32(len(s))) + w.RawString(s) +} + +func (w *Writer) StringRef(s string) { + off, ok := w.stringMap[s] + if !ok { + panic(fmt.Sprintf("writeStringRef: string not added: %q", s)) + } + w.Uint32(off) +} + +func (w *Writer) RawString(s string) { + w.wr.WriteString(s) + w.off += uint32(len(s)) +} + +func (w *Writer) Bytes(s []byte) { + w.wr.Write(s) + w.off += uint32(len(s)) +} + +func (w *Writer) Uint64(x uint64) { + var b [8]byte + binary.LittleEndian.PutUint64(b[:], x) + w.wr.Write(b[:]) + w.off += 8 +} + +func (w *Writer) Uint32(x uint32) { + var b [4]byte + binary.LittleEndian.PutUint32(b[:], x) + w.wr.Write(b[:]) + w.off += 4 +} + +func (w *Writer) Uint16(x uint16) { + var b [2]byte + binary.LittleEndian.PutUint16(b[:], x) + w.wr.Write(b[:]) + w.off += 2 +} + +func (w *Writer) Uint8(x uint8) { + w.wr.WriteByte(x) + w.off++ +} + +func (w *Writer) Offset() uint32 { + return w.off +} + +type Reader struct { + rd io.ReaderAt + start uint32 + h Header // keep block offsets +} + +func NewReader(rd io.ReaderAt, off uint32) *Reader { + r := &Reader{rd: rd, start: off} + 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 + b := make([]byte, len) + _, err := r.rd.ReadAt(b[:], int64(r.start+off)) + if err != nil { + panic("corrupted input") + } + return b +} + +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[:]) +} + +func (r *Reader) int64At(off uint32) int64 { + return int64(r.uint64At(off)) +} + +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[:]) +} + +func (r *Reader) int32At(off uint32) int32 { + return int32(r.uint32At(off)) +} + +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[:]) +} + +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") + } + return b[0] +} + +func (r *Reader) StringAt(off uint32) string { + // TODO: have some way to construct a string without copy + l := r.uint32At(off) + b := make([]byte, l) + n, err := r.rd.ReadAt(b, int64(r.start+off+4)) + if n != int(l) || err != nil { + panic("corrupted input") + } + return string(b) +} + +func (r *Reader) StringRef(off uint32) string { + return r.StringAt(r.uint32At(off)) +} + +func (r *Reader) Pkglist() []string { + n := (r.h.Offsets[BlkPkgIdx+1] - r.h.Offsets[BlkPkgIdx]) / 4 + s := make([]string, n) + for i := range s { + off := r.h.Offsets[BlkPkgIdx] + uint32(i)*4 + s[i] = r.StringRef(off) + } + return s +} + +func (r *Reader) NSym() int { + symsiz := (&Sym{}).Size() + return int(r.h.Offsets[BlkSymdef+1]-r.h.Offsets[BlkSymdef]) / symsiz +} + +func (r *Reader) NNonpkgdef() int { + symsiz := (&Sym{}).Size() + return int(r.h.Offsets[BlkNonpkgdef+1]-r.h.Offsets[BlkNonpkgdef]) / symsiz +} + +func (r *Reader) NNonpkgref() int { + symsiz := (&Sym{}).Size() + return int(r.h.Offsets[BlkNonpkgref+1]-r.h.Offsets[BlkNonpkgref]) / symsiz +} + +// SymOff returns the offset of the i-th symbol. +func (r *Reader) SymOff(i int) uint32 { + symsiz := (&Sym{}).Size() + return r.h.Offsets[BlkSymdef] + uint32(i*symsiz) +} + +// NReloc returns the number of relocations of the i-th symbol. +func (r *Reader) NReloc(i int) int { + relocIdxOff := r.h.Offsets[BlkRelocIdx] + uint32(i*4) + return int(r.uint32At(relocIdxOff+4) - r.uint32At(relocIdxOff)) +} + +// RelocOff returns the offset of the j-th relocation of the i-th symbol. +func (r *Reader) RelocOff(i int, j int) uint32 { + relocIdxOff := r.h.Offsets[BlkRelocIdx] + uint32(i*4) + relocIdx := r.uint32At(relocIdxOff) + relocsiz := (&Reloc{}).Size() + return r.h.Offsets[BlkReloc] + (relocIdx+uint32(j))*uint32(relocsiz) +} + +// NAux returns the number of aux symbols of the i-th symbol. +func (r *Reader) NAux(i int) int { + auxIdxOff := r.h.Offsets[BlkAuxIdx] + uint32(i*4) + return int(r.uint32At(auxIdxOff+4) - r.uint32At(auxIdxOff)) +} + +// AuxOff returns the offset of the j-th aux symbol of the i-th symbol. +func (r *Reader) AuxOff(i int, j int) uint32 { + auxIdxOff := r.h.Offsets[BlkAuxIdx] + uint32(i*4) + auxIdx := r.uint32At(auxIdxOff) + auxsiz := (&Aux{}).Size() + return r.h.Offsets[BlkAux] + (auxIdx+uint32(j))*uint32(auxsiz) +} + +// DataOff returns the offset of the i-th symbol's data. +func (r *Reader) DataOff(i int) uint32 { + dataIdxOff := r.h.Offsets[BlkDataIdx] + uint32(i*4) + return r.h.Offsets[BlkData] + r.uint32At(dataIdxOff) +} + +// DataSize returns the size of the i-th symbol's data. +func (r *Reader) DataSize(i int) int { + return int(r.DataOff(i+1) - r.DataOff(i)) +} + +// AuxDataBase returns the base offset of the aux data block. +func (r *Reader) PcdataBase() uint32 { + return r.h.Offsets[BlkPcdata] +} diff --git a/src/cmd/internal/obj/link.go b/src/cmd/internal/obj/link.go index f1cf342d3d..2c106bab30 100644 --- a/src/cmd/internal/obj/link.go +++ b/src/cmd/internal/obj/link.go @@ -413,6 +413,8 @@ type FuncInfo struct { GCLocals *LSym GCRegs *LSym StackObjects *LSym + + FuncInfoSym *LSym } type InlMark struct { @@ -636,15 +638,6 @@ type Pcdata struct { P []byte } -// Package Index. -const ( - PkgIdxNone = (1<<31 - 1) - iota // Non-package symbols - PkgIdxBuiltin // Predefined symbols // TODO: not used for now, we could use it for compiler-generated symbols like runtime.newobject - PkgIdxSelf // Symbols defined in the current package - PkgIdxInvalid = 0 - // The index of other referenced packages starts from 1. -) - // Link holds the context for writing object code from a compiler // to be linker input or for reading that input into the linker. type Link struct { diff --git a/src/cmd/internal/obj/objfile.go b/src/cmd/internal/obj/objfile.go index c51a11c51f..a27004a389 100644 --- a/src/cmd/internal/obj/objfile.go +++ b/src/cmd/internal/obj/objfile.go @@ -82,6 +82,11 @@ func newObjWriter(ctxt *Link, b *bufio.Writer, pkgpath string) *objWriter { } func WriteObjFile(ctxt *Link, bout *bio.Writer, pkgpath string) { + if ctxt.Flag_newobj { + WriteObjFile2(ctxt, bout, pkgpath) + return + } + b := bout.Writer w := newObjWriter(ctxt, b, pkgpath) diff --git a/src/cmd/internal/obj/objfile2.go b/src/cmd/internal/obj/objfile2.go new file mode 100644 index 0000000000..42f050a940 --- /dev/null +++ b/src/cmd/internal/obj/objfile2.go @@ -0,0 +1,350 @@ +// Copyright 2019 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Writing Go object files. + +package obj + +import ( + "bytes" + "cmd/internal/bio" + "cmd/internal/goobj2" + "cmd/internal/objabi" + "fmt" + "strings" +) + +// Entry point of writing new object file. +func WriteObjFile2(ctxt *Link, b *bio.Writer, pkgpath string) { + genFuncInfoSyms(ctxt) + + w := writer{ + Writer: goobj2.NewWriter(b), + ctxt: ctxt, + pkgpath: objabi.PathToPrefix(pkgpath), + } + + start := b.Offset() + w.init() + + // Header + // We just reserve the space. We'll fill in the offsets later. + h := goobj2.Header{Magic: goobj2.Magic} + h.Write(w.Writer) + + // String table + w.StringTable() + + // Package references + h.Offsets[goobj2.BlkPkgIdx] = w.Offset() + for _, pkg := range w.pkglist { + w.StringRef(pkg) + } + + // Symbol definitions + h.Offsets[goobj2.BlkSymdef] = w.Offset() + for _, s := range ctxt.defs { + w.Sym(s) + } + + // Non-pkg symbol definitions + h.Offsets[goobj2.BlkNonpkgdef] = w.Offset() + for _, s := range ctxt.nonpkgdefs { + w.Sym(s) + } + + // Non-pkg symbol references + h.Offsets[goobj2.BlkNonpkgref] = w.Offset() + for _, s := range ctxt.nonpkgrefs { + w.Sym(s) + } + + // Reloc indexes + h.Offsets[goobj2.BlkRelocIdx] = w.Offset() + nreloc := uint32(0) + lists := [][]*LSym{ctxt.defs, ctxt.nonpkgdefs} + for _, list := range lists { + for _, s := range list { + w.Uint32(nreloc) + nreloc += uint32(len(s.R)) + } + } + w.Uint32(nreloc) + + // Symbol Info indexes + h.Offsets[goobj2.BlkAuxIdx] = w.Offset() + naux := uint32(0) + for _, list := range lists { + for _, s := range list { + w.Uint32(naux) + if s.Gotype != nil { + naux++ + } + if s.Func != nil { + // FuncInfo is an aux symbol, each Funcdata is an aux symbol + naux += 1 + uint32(len(s.Func.Pcln.Funcdata)) + } + } + } + w.Uint32(naux) + + // Data indexes + h.Offsets[goobj2.BlkDataIdx] = w.Offset() + dataOff := uint32(0) + for _, list := range lists { + for _, s := range list { + w.Uint32(dataOff) + dataOff += uint32(len(s.P)) + } + } + w.Uint32(dataOff) + + // Relocs + h.Offsets[goobj2.BlkReloc] = w.Offset() + for _, list := range lists { + for _, s := range list { + for i := range s.R { + w.Reloc(&s.R[i]) + } + } + } + + // Aux symbol info + h.Offsets[goobj2.BlkAux] = w.Offset() + for _, list := range lists { + for _, s := range list { + w.Aux(s) + } + } + + // Data + h.Offsets[goobj2.BlkData] = w.Offset() + for _, list := range lists { + for _, s := range list { + w.Bytes(s.P) + } + } + + // Pcdata + h.Offsets[goobj2.BlkPcdata] = w.Offset() + for _, s := range ctxt.Text { // iteration order must match genFuncInfoSyms + if s.Func != nil { + pc := &s.Func.Pcln + w.Bytes(pc.Pcsp.P) + w.Bytes(pc.Pcfile.P) + w.Bytes(pc.Pcline.P) + w.Bytes(pc.Pcinline.P) + for i := range pc.Pcdata { + w.Bytes(pc.Pcdata[i].P) + } + } + } + + // Fix up block offsets in the header + end := start + int64(w.Offset()) + b.MustSeek(start, 0) + h.Write(w.Writer) + b.MustSeek(end, 0) +} + +type writer struct { + *goobj2.Writer + ctxt *Link + pkgpath string // the package import path (escaped), "" if unknown + pkglist []string // list of packages referenced, indexed by ctxt.pkgIdx +} + +// prepare package index list +func (w *writer) init() { + w.pkglist = make([]string, len(w.ctxt.pkgIdx)+1) + w.pkglist[0] = "" // dummy invalid package for index 0 + for pkg, i := range w.ctxt.pkgIdx { + w.pkglist[i] = pkg + } + + // Also make sure imported packages appear in the list (even if no symbol is referenced). + for _, pkg := range w.ctxt.Imports { + if _, ok := w.ctxt.pkgIdx[pkg]; !ok { + w.pkglist = append(w.pkglist, pkg) + } + } +} + +func (w *writer) StringTable() { + w.AddString("") + for _, pkg := range w.ctxt.Imports { + w.AddString(pkg) + } + for _, pkg := range w.pkglist { + w.AddString(pkg) + } + w.ctxt.traverseSyms(traverseAll, func(s *LSym) { + if w.pkgpath != "" { + s.Name = strings.Replace(s.Name, "\"\".", w.pkgpath+".", -1) + } + w.AddString(s.Name) + }) + w.ctxt.traverseSyms(traverseDefs, func(s *LSym) { + if s.Type != objabi.STEXT { + return + } + pc := &s.Func.Pcln + for _, f := range pc.File { + w.AddString(f) + } + for _, call := range pc.InlTree.nodes { + f, _ := linkgetlineFromPos(w.ctxt, call.Pos) + w.AddString(f) + } + }) +} + +func (w *writer) Sym(s *LSym) { + abi := uint16(s.ABI()) + if s.Static() { + abi = goobj2.SymABIstatic + } + flag := uint8(0) + if s.DuplicateOK() { + flag |= goobj2.SymFlagDupok + } + if s.Local() { + flag |= goobj2.SymFlagLocal + } + if s.MakeTypelink() { + flag |= goobj2.SymFlagTypelink + } + o := goobj2.Sym{ + Name: s.Name, + ABI: abi, + Type: uint8(s.Type), + Flag: flag, + Siz: uint32(s.Size), + } + o.Write(w.Writer) +} + +func makeSymRef(s *LSym) goobj2.SymRef { + if s == nil { + return goobj2.SymRef{} + } + if s.PkgIdx == 0 || !s.Indexed() { + fmt.Printf("unindexed symbol reference: %v\n", s) + panic("unindexed symbol reference") + } + return goobj2.SymRef{PkgIdx: uint32(s.PkgIdx), SymIdx: uint32(s.SymIdx)} +} + +func (w *writer) Reloc(r *Reloc) { + o := goobj2.Reloc{ + Off: r.Off, + Siz: r.Siz, + Type: uint8(r.Type), + Add: r.Add, + Sym: makeSymRef(r.Sym), + } + o.Write(w.Writer) +} + +func (w *writer) Aux(s *LSym) { + if s.Gotype != nil { + o := goobj2.Aux{ + Type: goobj2.AuxGotype, + Sym: makeSymRef(s.Gotype), + } + o.Write(w.Writer) + } + if s.Func != nil { + o := goobj2.Aux{ + Type: goobj2.AuxFuncInfo, + Sym: makeSymRef(s.Func.FuncInfoSym), + } + o.Write(w.Writer) + + for _, d := range s.Func.Pcln.Funcdata { + o := goobj2.Aux{ + Type: goobj2.AuxFuncdata, + Sym: makeSymRef(d), + } + o.Write(w.Writer) + } + } +} + +// generate symbols for FuncInfo. +func genFuncInfoSyms(ctxt *Link) { + infosyms := make([]*LSym, 0, len(ctxt.Text)) + var pcdataoff uint32 + var b bytes.Buffer + symidx := int32(len(ctxt.defs)) + for _, s := range ctxt.Text { + if s.Func == nil { + continue + } + nosplit := uint8(0) + if s.NoSplit() { + nosplit = 1 + } + flags := uint8(0) + if s.Leaf() { + flags |= goobj2.FuncFlagLeaf + } + if s.CFunc() { + flags |= goobj2.FuncFlagCFunc + } + if s.ReflectMethod() { + flags |= goobj2.FuncFlagReflectMethod + } + if ctxt.Flag_shared { // This is really silly + flags |= goobj2.FuncFlagShared + } + if s.TopFrame() { + flags |= goobj2.FuncFlagTopFrame + } + o := goobj2.FuncInfo{ + NoSplit: nosplit, + Flags: flags, + Args: uint32(s.Func.Args), + Locals: uint32(s.Func.Locals), + } + pc := &s.Func.Pcln + o.Pcsp = pcdataoff + pcdataoff += uint32(len(pc.Pcsp.P)) + o.Pcfile = pcdataoff + pcdataoff += uint32(len(pc.Pcfile.P)) + o.Pcline = pcdataoff + pcdataoff += uint32(len(pc.Pcline.P)) + o.Pcinline = pcdataoff + pcdataoff += uint32(len(pc.Pcinline.P)) + o.Pcdata = make([]uint32, len(pc.Pcdata)) + for i, pcd := range pc.Pcdata { + o.Pcdata[i] = pcdataoff + pcdataoff += uint32(len(pcd.P)) + } + o.PcdataEnd = pcdataoff + o.Funcdataoff = make([]uint32, len(pc.Funcdataoff)) + for i, x := range pc.Funcdataoff { + o.Funcdataoff[i] = uint32(x) + } + o.File = make([]goobj2.SymRef, len(pc.File)) + for i, f := range pc.File { + fsym := ctxt.Lookup(f) + o.File[i] = makeSymRef(fsym) + } + + o.Write(&b) + isym := &LSym{ + Type: objabi.SDATA, // for now, I don't think it matters + PkgIdx: goobj2.PkgIdxSelf, + SymIdx: symidx, + P: append([]byte(nil), b.Bytes()...), + } + isym.Set(AttrIndexed, true) + symidx++ + infosyms = append(infosyms, isym) + s.Func.FuncInfoSym = isym + b.Reset() + } + ctxt.defs = append(ctxt.defs, infosyms...) +} diff --git a/src/cmd/internal/obj/sym.go b/src/cmd/internal/obj/sym.go index e47c511ddc..e72ec3e701 100644 --- a/src/cmd/internal/obj/sym.go +++ b/src/cmd/internal/obj/sym.go @@ -32,6 +32,7 @@ package obj import ( + "cmd/internal/goobj2" "cmd/internal/objabi" "fmt" "log" @@ -173,7 +174,7 @@ func (ctxt *Link) NumberSyms(asm bool) { var idx, nonpkgidx int32 = 0, 0 ctxt.traverseSyms(traverseDefs, func(s *LSym) { if asm || s.Pkg == "_" || s.DuplicateOK() { - s.PkgIdx = PkgIdxNone + s.PkgIdx = goobj2.PkgIdxNone s.SymIdx = nonpkgidx if nonpkgidx != int32(len(ctxt.nonpkgdefs)) { panic("bad index") @@ -181,7 +182,7 @@ func (ctxt *Link) NumberSyms(asm bool) { ctxt.nonpkgdefs = append(ctxt.nonpkgdefs, s) nonpkgidx++ } else { - s.PkgIdx = PkgIdxSelf + s.PkgIdx = goobj2.PkgIdxSelf s.SymIdx = idx if idx != int32(len(ctxt.defs)) { panic("bad index") @@ -195,12 +196,12 @@ func (ctxt *Link) NumberSyms(asm bool) { ipkg := int32(1) // 0 is invalid index nonpkgdef := nonpkgidx ctxt.traverseSyms(traverseRefs|traverseAux, func(rs *LSym) { - if rs.PkgIdx != PkgIdxInvalid { + if rs.PkgIdx != goobj2.PkgIdxInvalid { return } pkg := rs.Pkg if pkg == "" || pkg == "\"\"" || pkg == "_" || !rs.Indexed() { - rs.PkgIdx = PkgIdxNone + rs.PkgIdx = goobj2.PkgIdxNone rs.SymIdx = nonpkgidx rs.Set(AttrIndexed, true) if nonpkgidx != nonpkgdef+int32(len(ctxt.nonpkgrefs)) {