mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
This change splits the SDWARFINFO symbol type (a generic container of DWARF content) into separate sub-classes. The new symbol types are SDWARFCUINFO comp unit DIE, also CU info and CU packagename syms SDWARFCONST constant DIE SDWARFFCN subprogram DIE (default and concrete) SDWARFABSFCN abstract function DIE SDWARFTYPE type DIE SDWARFVAR global variable DIE Advantage of doing this: in the linker there are several places where we have to iterate over a symbol's relocations to pick out references to specific classes of DWARF sub-symbols (for example, looking for all abstract function DIEs referenced by a subprogram DIE, or looking at all the type DIEs used in a subprogram DIE). By splitting SDWARFINFO into parts clients can now look only at the relocation target's sym type as opposed to having to materialize the target sym name, or do a lookup. Change-Id: I4e0ee3216d3c8f1a78bec3d296c01e95b3d025b5 Reviewed-on: https://go-review.googlesource.com/c/go/+/234684 Reviewed-by: Cherry Zhang <cherryyz@google.com> Reviewed-by: Jeremy Faller <jeremy@golang.org>
450 lines
10 KiB
Go
450 lines
10 KiB
Go
// 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"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
// Entry point of writing new object file.
|
|
func WriteObjFile(ctxt *Link, b *bio.Writer, pkgpath string) {
|
|
|
|
debugAsmEmit(ctxt)
|
|
|
|
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.
|
|
flags := uint32(0)
|
|
if ctxt.Flag_shared {
|
|
flags |= goobj2.ObjFlagShared
|
|
}
|
|
if pkgpath == "" {
|
|
flags |= goobj2.ObjFlagNeedNameExpansion
|
|
}
|
|
h := goobj2.Header{
|
|
Magic: goobj2.Magic,
|
|
Fingerprint: ctxt.Fingerprint,
|
|
Flags: flags,
|
|
}
|
|
h.Write(w.Writer)
|
|
|
|
// String table
|
|
w.StringTable()
|
|
|
|
// Autolib
|
|
h.Offsets[goobj2.BlkAutolib] = w.Offset()
|
|
for i := range ctxt.Imports {
|
|
ctxt.Imports[i].Write(w.Writer)
|
|
}
|
|
|
|
// Package references
|
|
h.Offsets[goobj2.BlkPkgIdx] = w.Offset()
|
|
for _, pkg := range w.pkglist {
|
|
w.StringRef(pkg)
|
|
}
|
|
|
|
// DWARF file table
|
|
h.Offsets[goobj2.BlkDwarfFile] = w.Offset()
|
|
for _, f := range ctxt.PosTable.DebugLinesFileTable() {
|
|
w.StringRef(filepath.ToSlash(f))
|
|
}
|
|
|
|
// 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)
|
|
naux += uint32(nAuxSym(s))
|
|
}
|
|
}
|
|
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
|
|
}
|
|
}
|
|
|
|
func (w *writer) StringTable() {
|
|
w.AddString("")
|
|
for _, p := range w.ctxt.Imports {
|
|
w.AddString(p.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(filepath.ToSlash(f))
|
|
}
|
|
for _, call := range pc.InlTree.nodes {
|
|
f, _ := linkgetlineFromPos(w.ctxt, call.Pos)
|
|
w.AddString(filepath.ToSlash(f))
|
|
}
|
|
})
|
|
for _, f := range w.ctxt.PosTable.DebugLinesFileTable() {
|
|
w.AddString(filepath.ToSlash(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
|
|
}
|
|
if s.Leaf() {
|
|
flag |= goobj2.SymFlagLeaf
|
|
}
|
|
if s.NoSplit() {
|
|
flag |= goobj2.SymFlagNoSplit
|
|
}
|
|
if s.ReflectMethod() {
|
|
flag |= goobj2.SymFlagReflectMethod
|
|
}
|
|
if s.TopFrame() {
|
|
flag |= goobj2.SymFlagTopFrame
|
|
}
|
|
if strings.HasPrefix(s.Name, "type.") && s.Name[5] != '.' && s.Type == objabi.SRODATA {
|
|
flag |= goobj2.SymFlagGoType
|
|
}
|
|
name := s.Name
|
|
if strings.HasPrefix(name, "gofile..") {
|
|
name = filepath.ToSlash(name)
|
|
}
|
|
var align uint32
|
|
if s.Func != nil {
|
|
align = uint32(s.Func.Align)
|
|
}
|
|
var o goobj2.Sym
|
|
o.SetName(name, w.Writer)
|
|
o.SetABI(abi)
|
|
o.SetType(uint8(s.Type))
|
|
o.SetFlag(flag)
|
|
o.SetSiz(uint32(s.Size))
|
|
o.SetAlign(align)
|
|
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) {
|
|
var o goobj2.Reloc
|
|
o.SetOff(r.Off)
|
|
o.SetSiz(r.Siz)
|
|
o.SetType(uint8(r.Type))
|
|
o.SetAdd(r.Add)
|
|
o.SetSym(makeSymRef(r.Sym))
|
|
o.Write(w.Writer)
|
|
}
|
|
|
|
func (w *writer) aux1(typ uint8, rs *LSym) {
|
|
var o goobj2.Aux
|
|
o.SetType(typ)
|
|
o.SetSym(makeSymRef(rs))
|
|
o.Write(w.Writer)
|
|
}
|
|
|
|
func (w *writer) Aux(s *LSym) {
|
|
if s.Gotype != nil {
|
|
w.aux1(goobj2.AuxGotype, s.Gotype)
|
|
}
|
|
if s.Func != nil {
|
|
w.aux1(goobj2.AuxFuncInfo, s.Func.FuncInfoSym)
|
|
|
|
for _, d := range s.Func.Pcln.Funcdata {
|
|
w.aux1(goobj2.AuxFuncdata, d)
|
|
}
|
|
|
|
if s.Func.dwarfInfoSym != nil && s.Func.dwarfInfoSym.Size != 0 {
|
|
w.aux1(goobj2.AuxDwarfInfo, s.Func.dwarfInfoSym)
|
|
}
|
|
if s.Func.dwarfLocSym != nil && s.Func.dwarfLocSym.Size != 0 {
|
|
w.aux1(goobj2.AuxDwarfLoc, s.Func.dwarfLocSym)
|
|
}
|
|
if s.Func.dwarfRangesSym != nil && s.Func.dwarfRangesSym.Size != 0 {
|
|
w.aux1(goobj2.AuxDwarfRanges, s.Func.dwarfRangesSym)
|
|
}
|
|
if s.Func.dwarfDebugLinesSym != nil && s.Func.dwarfDebugLinesSym.Size != 0 {
|
|
w.aux1(goobj2.AuxDwarfLines, s.Func.dwarfDebugLinesSym)
|
|
}
|
|
}
|
|
}
|
|
|
|
// return the number of aux symbols s have.
|
|
func nAuxSym(s *LSym) int {
|
|
n := 0
|
|
if s.Gotype != nil {
|
|
n++
|
|
}
|
|
if s.Func != nil {
|
|
// FuncInfo is an aux symbol, each Funcdata is an aux symbol
|
|
n += 1 + len(s.Func.Pcln.Funcdata)
|
|
if s.Func.dwarfInfoSym != nil && s.Func.dwarfInfoSym.Size != 0 {
|
|
n++
|
|
}
|
|
if s.Func.dwarfLocSym != nil && s.Func.dwarfLocSym.Size != 0 {
|
|
n++
|
|
}
|
|
if s.Func.dwarfRangesSym != nil && s.Func.dwarfRangesSym.Size != 0 {
|
|
n++
|
|
}
|
|
if s.Func.dwarfDebugLinesSym != nil && s.Func.dwarfDebugLinesSym.Size != 0 {
|
|
n++
|
|
}
|
|
}
|
|
return n
|
|
}
|
|
|
|
// 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
|
|
}
|
|
o := goobj2.FuncInfo{
|
|
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.InlTree = make([]goobj2.InlTreeNode, len(pc.InlTree.nodes))
|
|
for i, inl := range pc.InlTree.nodes {
|
|
f, l := linkgetlineFromPos(ctxt, inl.Pos)
|
|
fsym := ctxt.Lookup(f)
|
|
o.InlTree[i] = goobj2.InlTreeNode{
|
|
Parent: int32(inl.Parent),
|
|
File: makeSymRef(fsym),
|
|
Line: l,
|
|
Func: makeSymRef(inl.Func),
|
|
ParentPC: inl.ParentPC,
|
|
}
|
|
}
|
|
|
|
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()
|
|
|
|
dwsyms := []*LSym{s.Func.dwarfRangesSym, s.Func.dwarfLocSym, s.Func.dwarfDebugLinesSym, s.Func.dwarfInfoSym}
|
|
for _, s := range dwsyms {
|
|
if s == nil || s.Size == 0 {
|
|
continue
|
|
}
|
|
s.PkgIdx = goobj2.PkgIdxSelf
|
|
s.SymIdx = symidx
|
|
s.Set(AttrIndexed, true)
|
|
symidx++
|
|
infosyms = append(infosyms, s)
|
|
}
|
|
}
|
|
ctxt.defs = append(ctxt.defs, infosyms...)
|
|
}
|
|
|
|
// debugDumpAux is a dumper for selected aux symbols.
|
|
func writeAuxSymDebug(ctxt *Link, par *LSym, aux *LSym) {
|
|
// Most aux symbols (ex: funcdata) are not interesting--
|
|
// pick out just the DWARF ones for now.
|
|
if aux.Type != objabi.SDWARFLOC &&
|
|
aux.Type != objabi.SDWARFFCN &&
|
|
aux.Type != objabi.SDWARFABSFCN &&
|
|
aux.Type != objabi.SDWARFLINES &&
|
|
aux.Type != objabi.SDWARFRANGE {
|
|
return
|
|
}
|
|
ctxt.writeSymDebugNamed(aux, "aux for "+par.Name)
|
|
}
|
|
|
|
func debugAsmEmit(ctxt *Link) {
|
|
if ctxt.Debugasm > 0 {
|
|
ctxt.traverseSyms(traverseDefs, ctxt.writeSymDebug)
|
|
if ctxt.Debugasm > 1 {
|
|
fn := func(par *LSym, aux *LSym) {
|
|
writeAuxSymDebug(ctxt, par, aux)
|
|
}
|
|
ctxt.traverseAuxSyms(traverseAux, fn)
|
|
}
|
|
}
|
|
}
|