import (
"cmd/internal/dwarf"
+ "cmd/internal/objabi"
"cmd/internal/src"
"fmt"
+ "sort"
+ "sync"
)
// Generate a sequence of opcodes that is as short as possible.
// Output the special opcode.
dctxt.AddUint8(s, uint8(opcode))
}
+
+// implement dwarf.Context
+type dwCtxt struct{ *Link }
+
+func (c dwCtxt) PtrSize() int {
+ return c.Arch.PtrSize
+}
+func (c dwCtxt) AddInt(s dwarf.Sym, size int, i int64) {
+ ls := s.(*LSym)
+ ls.WriteInt(c.Link, ls.Size, size, i)
+}
+func (c dwCtxt) AddUint16(s dwarf.Sym, i uint16) {
+ c.AddInt(s, 2, int64(i))
+}
+func (c dwCtxt) AddUint8(s dwarf.Sym, i uint8) {
+ b := []byte{byte(i)}
+ c.AddBytes(s, b)
+}
+func (c dwCtxt) AddBytes(s dwarf.Sym, b []byte) {
+ ls := s.(*LSym)
+ ls.WriteBytes(c.Link, ls.Size, b)
+}
+func (c dwCtxt) AddString(s dwarf.Sym, v string) {
+ ls := s.(*LSym)
+ ls.WriteString(c.Link, ls.Size, len(v), v)
+ ls.WriteInt(c.Link, ls.Size, 1, 0)
+}
+func (c dwCtxt) AddAddress(s dwarf.Sym, data interface{}, value int64) {
+ ls := s.(*LSym)
+ size := c.PtrSize()
+ if data != nil {
+ rsym := data.(*LSym)
+ ls.WriteAddr(c.Link, ls.Size, size, rsym, value)
+ } else {
+ ls.WriteInt(c.Link, ls.Size, size, value)
+ }
+}
+func (c dwCtxt) AddCURelativeAddress(s dwarf.Sym, data interface{}, value int64) {
+ ls := s.(*LSym)
+ rsym := data.(*LSym)
+ ls.WriteCURelativeAddr(c.Link, ls.Size, rsym, value)
+}
+func (c dwCtxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64) {
+ panic("should be used only in the linker")
+}
+func (c dwCtxt) AddDWARFAddrSectionOffset(s dwarf.Sym, t interface{}, ofs int64) {
+ size := 4
+ if isDwarf64(c.Link) {
+ size = 8
+ }
+
+ ls := s.(*LSym)
+ rsym := t.(*LSym)
+ ls.WriteAddr(c.Link, ls.Size, size, rsym, ofs)
+ r := &ls.R[len(ls.R)-1]
+ r.Type = objabi.R_DWARFSECREF
+}
+
+func (c dwCtxt) AddFileRef(s dwarf.Sym, f interface{}) {
+ ls := s.(*LSym)
+ rsym := f.(*LSym)
+ fidx := c.Link.PosTable.FileIndex(rsym.Name)
+ // Note the +1 here -- the value we're writing is going to be an
+ // index into the DWARF line table file section, whose entries
+ // are numbered starting at 1, not 0.
+ ls.WriteInt(c.Link, ls.Size, 4, int64(fidx+1))
+}
+
+func (c dwCtxt) CurrentOffset(s dwarf.Sym) int64 {
+ ls := s.(*LSym)
+ return ls.Size
+}
+
+// Here "from" is a symbol corresponding to an inlined or concrete
+// function, "to" is the symbol for the corresponding abstract
+// function, and "dclIdx" is the index of the symbol of interest with
+// respect to the Dcl slice of the original pre-optimization version
+// of the inlined function.
+func (c dwCtxt) RecordDclReference(from dwarf.Sym, to dwarf.Sym, dclIdx int, inlIndex int) {
+ ls := from.(*LSym)
+ tls := to.(*LSym)
+ ridx := len(ls.R) - 1
+ c.Link.DwFixups.ReferenceChildDIE(ls, ridx, tls, dclIdx, inlIndex)
+}
+
+func (c dwCtxt) RecordChildDieOffsets(s dwarf.Sym, vars []*dwarf.Var, offsets []int32) {
+ ls := s.(*LSym)
+ c.Link.DwFixups.RegisterChildDIEOffsets(ls, vars, offsets)
+}
+
+func (c dwCtxt) Logf(format string, args ...interface{}) {
+ c.Link.Logf(format, args...)
+}
+
+func isDwarf64(ctxt *Link) bool {
+ return ctxt.Headtype == objabi.Haix
+}
+
+func (ctxt *Link) dwarfSym(s *LSym) (dwarfInfoSym, dwarfLocSym, dwarfRangesSym, dwarfAbsFnSym, dwarfDebugLines *LSym) {
+ if s.Type != objabi.STEXT {
+ ctxt.Diag("dwarfSym of non-TEXT %v", s)
+ }
+ if s.Func.dwarfInfoSym == nil {
+ s.Func.dwarfInfoSym = &LSym{
+ Type: objabi.SDWARFFCN,
+ }
+ if ctxt.Flag_locationlists {
+ s.Func.dwarfLocSym = &LSym{
+ Type: objabi.SDWARFLOC,
+ }
+ }
+ s.Func.dwarfRangesSym = &LSym{
+ Type: objabi.SDWARFRANGE,
+ }
+ s.Func.dwarfDebugLinesSym = &LSym{
+ Type: objabi.SDWARFLINES,
+ }
+ if s.WasInlined() {
+ s.Func.dwarfAbsFnSym = ctxt.DwFixups.AbsFuncDwarfSym(s)
+ }
+ }
+ return s.Func.dwarfInfoSym, s.Func.dwarfLocSym, s.Func.dwarfRangesSym, s.Func.dwarfAbsFnSym, s.Func.dwarfDebugLinesSym
+}
+
+func (s *LSym) Length(dwarfContext interface{}) int64 {
+ return s.Size
+}
+
+// fileSymbol returns a symbol corresponding to the source file of the
+// first instruction (prog) of the specified function. This will
+// presumably be the file in which the function is defined.
+func (ctxt *Link) fileSymbol(fn *LSym) *LSym {
+ p := fn.Func.Text
+ if p != nil {
+ f, _ := linkgetlineFromPos(ctxt, p.Pos)
+ fsym := ctxt.Lookup(f)
+ return fsym
+ }
+ return nil
+}
+
+// populateDWARF fills in the DWARF Debugging Information Entries for
+// TEXT symbol 's'. The various DWARF symbols must already have been
+// initialized in InitTextSym.
+func (ctxt *Link) populateDWARF(curfn interface{}, s *LSym, myimportpath string) {
+ info, loc, ranges, absfunc, lines := ctxt.dwarfSym(s)
+ if info.Size != 0 {
+ ctxt.Diag("makeFuncDebugEntry double process %v", s)
+ }
+ var scopes []dwarf.Scope
+ var inlcalls dwarf.InlCalls
+ if ctxt.DebugInfo != nil {
+ scopes, inlcalls = ctxt.DebugInfo(s, info, curfn)
+ }
+ var err error
+ dwctxt := dwCtxt{ctxt}
+ filesym := ctxt.fileSymbol(s)
+ fnstate := &dwarf.FnState{
+ Name: s.Name,
+ Importpath: myimportpath,
+ Info: info,
+ Filesym: filesym,
+ Loc: loc,
+ Ranges: ranges,
+ Absfn: absfunc,
+ StartPC: s,
+ Size: s.Size,
+ External: !s.Static(),
+ Scopes: scopes,
+ InlCalls: inlcalls,
+ UseBASEntries: ctxt.UseBASEntries,
+ }
+ if absfunc != nil {
+ err = dwarf.PutAbstractFunc(dwctxt, fnstate)
+ if err != nil {
+ ctxt.Diag("emitting DWARF for %s failed: %v", s.Name, err)
+ }
+ err = dwarf.PutConcreteFunc(dwctxt, fnstate)
+ } else {
+ err = dwarf.PutDefaultFunc(dwctxt, fnstate)
+ }
+ if err != nil {
+ ctxt.Diag("emitting DWARF for %s failed: %v", s.Name, err)
+ }
+ // Fill in the debug lines symbol.
+ ctxt.generateDebugLinesSymbol(s, lines)
+}
+
+// DwarfIntConst creates a link symbol for an integer constant with the
+// given name, type and value.
+func (ctxt *Link) DwarfIntConst(myimportpath, name, typename string, val int64) {
+ if myimportpath == "" {
+ return
+ }
+ s := ctxt.LookupInit(dwarf.ConstInfoPrefix+myimportpath, func(s *LSym) {
+ s.Type = objabi.SDWARFCONST
+ ctxt.Data = append(ctxt.Data, s)
+ })
+ dwarf.PutIntConst(dwCtxt{ctxt}, s, ctxt.Lookup(dwarf.InfoPrefix+typename), myimportpath+"."+name, val)
+}
+
+func (ctxt *Link) DwarfAbstractFunc(curfn interface{}, s *LSym, myimportpath string) {
+ absfn := ctxt.DwFixups.AbsFuncDwarfSym(s)
+ if absfn.Size != 0 {
+ ctxt.Diag("internal error: DwarfAbstractFunc double process %v", s)
+ }
+ if s.Func == nil {
+ s.Func = new(FuncInfo)
+ }
+ scopes, _ := ctxt.DebugInfo(s, absfn, curfn)
+ dwctxt := dwCtxt{ctxt}
+ filesym := ctxt.fileSymbol(s)
+ fnstate := dwarf.FnState{
+ Name: s.Name,
+ Importpath: myimportpath,
+ Info: absfn,
+ Filesym: filesym,
+ Absfn: absfn,
+ External: !s.Static(),
+ Scopes: scopes,
+ UseBASEntries: ctxt.UseBASEntries,
+ }
+ if err := dwarf.PutAbstractFunc(dwctxt, &fnstate); err != nil {
+ ctxt.Diag("emitting DWARF for %s failed: %v", s.Name, err)
+ }
+}
+
+// This table is designed to aid in the creation of references between
+// DWARF subprogram DIEs.
+//
+// In most cases when one DWARF DIE has to refer to another DWARF DIE,
+// the target of the reference has an LSym, which makes it easy to use
+// the existing relocation mechanism. For DWARF inlined routine DIEs,
+// however, the subprogram DIE has to refer to a child
+// parameter/variable DIE of the abstract subprogram. This child DIE
+// doesn't have an LSym, and also of interest is the fact that when
+// DWARF generation is happening for inlined function F within caller
+// G, it's possible that DWARF generation hasn't happened yet for F,
+// so there is no way to know the offset of a child DIE within F's
+// abstract function. Making matters more complex, each inlined
+// instance of F may refer to a subset of the original F's variables
+// (depending on what happens with optimization, some vars may be
+// eliminated).
+//
+// The fixup table below helps overcome this hurdle. At the point
+// where a parameter/variable reference is made (via a call to
+// "ReferenceChildDIE"), a fixup record is generate that records
+// the relocation that is targeting that child variable. At a later
+// point when the abstract function DIE is emitted, there will be
+// a call to "RegisterChildDIEOffsets", at which point the offsets
+// needed to apply fixups are captured. Finally, once the parallel
+// portion of the compilation is done, fixups can actually be applied
+// during the "Finalize" method (this can't be done during the
+// parallel portion of the compile due to the possibility of data
+// races).
+//
+// This table is also used to record the "precursor" function node for
+// each function that is the target of an inline -- child DIE references
+// have to be made with respect to the original pre-optimization
+// version of the function (to allow for the fact that each inlined
+// body may be optimized differently).
+type DwarfFixupTable struct {
+ ctxt *Link
+ mu sync.Mutex
+ symtab map[*LSym]int // maps abstract fn LSYM to index in svec
+ svec []symFixups
+ precursor map[*LSym]fnState // maps fn Lsym to precursor Node, absfn sym
+}
+
+type symFixups struct {
+ fixups []relFixup
+ doffsets []declOffset
+ inlIndex int32
+ defseen bool
+}
+
+type declOffset struct {
+ // Index of variable within DCL list of pre-optimization function
+ dclIdx int32
+ // Offset of var's child DIE with respect to containing subprogram DIE
+ offset int32
+}
+
+type relFixup struct {
+ refsym *LSym
+ relidx int32
+ dclidx int32
+}
+
+type fnState struct {
+ // precursor function (really *gc.Node)
+ precursor interface{}
+ // abstract function symbol
+ absfn *LSym
+}
+
+func NewDwarfFixupTable(ctxt *Link) *DwarfFixupTable {
+ return &DwarfFixupTable{
+ ctxt: ctxt,
+ symtab: make(map[*LSym]int),
+ precursor: make(map[*LSym]fnState),
+ }
+}
+
+func (ft *DwarfFixupTable) GetPrecursorFunc(s *LSym) interface{} {
+ if fnstate, found := ft.precursor[s]; found {
+ return fnstate.precursor
+ }
+ return nil
+}
+
+func (ft *DwarfFixupTable) SetPrecursorFunc(s *LSym, fn interface{}) {
+ if _, found := ft.precursor[s]; found {
+ ft.ctxt.Diag("internal error: DwarfFixupTable.SetPrecursorFunc double call on %v", s)
+ }
+
+ // initialize abstract function symbol now. This is done here so
+ // as to avoid data races later on during the parallel portion of
+ // the back end.
+ absfn := ft.ctxt.LookupDerived(s, dwarf.InfoPrefix+s.Name+dwarf.AbstractFuncSuffix)
+ absfn.Set(AttrDuplicateOK, true)
+ absfn.Type = objabi.SDWARFABSFCN
+ ft.ctxt.Data = append(ft.ctxt.Data, absfn)
+
+ // In the case of "late" inlining (inlines that happen during
+ // wrapper generation as opposed to the main inlining phase) it's
+ // possible that we didn't cache the abstract function sym for the
+ // text symbol -- do so now if needed. See issue 38068.
+ if s.Func != nil && s.Func.dwarfAbsFnSym == nil {
+ s.Func.dwarfAbsFnSym = absfn
+ }
+
+ ft.precursor[s] = fnState{precursor: fn, absfn: absfn}
+}
+
+// Make a note of a child DIE reference: relocation 'ridx' within symbol 's'
+// is targeting child 'c' of DIE with symbol 'tgt'.
+func (ft *DwarfFixupTable) ReferenceChildDIE(s *LSym, ridx int, tgt *LSym, dclidx int, inlIndex int) {
+ // Protect against concurrent access if multiple backend workers
+ ft.mu.Lock()
+ defer ft.mu.Unlock()
+
+ // Create entry for symbol if not already present.
+ idx, found := ft.symtab[tgt]
+ if !found {
+ ft.svec = append(ft.svec, symFixups{inlIndex: int32(inlIndex)})
+ idx = len(ft.svec) - 1
+ ft.symtab[tgt] = idx
+ }
+
+ // Do we have child DIE offsets available? If so, then apply them,
+ // otherwise create a fixup record.
+ sf := &ft.svec[idx]
+ if len(sf.doffsets) > 0 {
+ found := false
+ for _, do := range sf.doffsets {
+ if do.dclIdx == int32(dclidx) {
+ off := do.offset
+ s.R[ridx].Add += int64(off)
+ found = true
+ break
+ }
+ }
+ if !found {
+ ft.ctxt.Diag("internal error: DwarfFixupTable.ReferenceChildDIE unable to locate child DIE offset for dclIdx=%d src=%v tgt=%v", dclidx, s, tgt)
+ }
+ } else {
+ sf.fixups = append(sf.fixups, relFixup{s, int32(ridx), int32(dclidx)})
+ }
+}
+
+// Called once DWARF generation is complete for a given abstract function,
+// whose children might have been referenced via a call above. Stores
+// the offsets for any child DIEs (vars, params) so that they can be
+// consumed later in on DwarfFixupTable.Finalize, which applies any
+// outstanding fixups.
+func (ft *DwarfFixupTable) RegisterChildDIEOffsets(s *LSym, vars []*dwarf.Var, coffsets []int32) {
+ // Length of these two slices should agree
+ if len(vars) != len(coffsets) {
+ ft.ctxt.Diag("internal error: RegisterChildDIEOffsets vars/offsets length mismatch")
+ return
+ }
+
+ // Generate the slice of declOffset's based in vars/coffsets
+ doffsets := make([]declOffset, len(coffsets))
+ for i := range coffsets {
+ doffsets[i].dclIdx = vars[i].ChildIndex
+ doffsets[i].offset = coffsets[i]
+ }
+
+ ft.mu.Lock()
+ defer ft.mu.Unlock()
+
+ // Store offsets for this symbol.
+ idx, found := ft.symtab[s]
+ if !found {
+ sf := symFixups{inlIndex: -1, defseen: true, doffsets: doffsets}
+ ft.svec = append(ft.svec, sf)
+ ft.symtab[s] = len(ft.svec) - 1
+ } else {
+ sf := &ft.svec[idx]
+ sf.doffsets = doffsets
+ sf.defseen = true
+ }
+}
+
+func (ft *DwarfFixupTable) processFixups(slot int, s *LSym) {
+ sf := &ft.svec[slot]
+ for _, f := range sf.fixups {
+ dfound := false
+ for _, doffset := range sf.doffsets {
+ if doffset.dclIdx == f.dclidx {
+ f.refsym.R[f.relidx].Add += int64(doffset.offset)
+ dfound = true
+ break
+ }
+ }
+ if !dfound {
+ ft.ctxt.Diag("internal error: DwarfFixupTable has orphaned fixup on %v targeting %v relidx=%d dclidx=%d", f.refsym, s, f.relidx, f.dclidx)
+ }
+ }
+}
+
+// return the LSym corresponding to the 'abstract subprogram' DWARF
+// info entry for a function.
+func (ft *DwarfFixupTable) AbsFuncDwarfSym(fnsym *LSym) *LSym {
+ // Protect against concurrent access if multiple backend workers
+ ft.mu.Lock()
+ defer ft.mu.Unlock()
+
+ if fnstate, found := ft.precursor[fnsym]; found {
+ return fnstate.absfn
+ }
+ ft.ctxt.Diag("internal error: AbsFuncDwarfSym requested for %v, not seen during inlining", fnsym)
+ return nil
+}
+
+// Called after all functions have been compiled; the main job of this
+// function is to identify cases where there are outstanding fixups.
+// This scenario crops up when we have references to variables of an
+// inlined routine, but that routine is defined in some other package.
+// This helper walks through and locate these fixups, then invokes a
+// helper to create an abstract subprogram DIE for each one.
+func (ft *DwarfFixupTable) Finalize(myimportpath string, trace bool) {
+ if trace {
+ ft.ctxt.Logf("DwarfFixupTable.Finalize invoked for %s\n", myimportpath)
+ }
+
+ // Collect up the keys from the precursor map, then sort the
+ // resulting list (don't want to rely on map ordering here).
+ fns := make([]*LSym, len(ft.precursor))
+ idx := 0
+ for fn := range ft.precursor {
+ fns[idx] = fn
+ idx++
+ }
+ sort.Sort(BySymName(fns))
+
+ // Should not be called during parallel portion of compilation.
+ if ft.ctxt.InParallel {
+ ft.ctxt.Diag("internal error: DwarfFixupTable.Finalize call during parallel backend")
+ }
+
+ // Generate any missing abstract functions.
+ for _, s := range fns {
+ absfn := ft.AbsFuncDwarfSym(s)
+ slot, found := ft.symtab[absfn]
+ if !found || !ft.svec[slot].defseen {
+ ft.ctxt.GenAbstractFunc(s)
+ }
+ }
+
+ // Apply fixups.
+ for _, s := range fns {
+ absfn := ft.AbsFuncDwarfSym(s)
+ slot, found := ft.symtab[absfn]
+ if !found {
+ ft.ctxt.Diag("internal error: DwarfFixupTable.Finalize orphan abstract function for %v", s)
+ } else {
+ ft.processFixups(slot, s)
+ }
+ }
+}
+
+type BySymName []*LSym
+
+func (s BySymName) Len() int { return len(s) }
+func (s BySymName) Less(i, j int) bool { return s[i].Name < s[j].Name }
+func (s BySymName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// 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 (
- "cmd/internal/dwarf"
+ "bytes"
+ "cmd/internal/bio"
+ "cmd/internal/goobj"
"cmd/internal/objabi"
"cmd/internal/sys"
+ "crypto/sha1"
+ "encoding/binary"
"fmt"
"io"
+ "path/filepath"
"sort"
- "sync"
+ "strings"
)
-func (ctxt *Link) writeSymDebug(s *LSym) {
- ctxt.writeSymDebugNamed(s, s.Name)
-}
+// Entry point of writing new object file.
+func WriteObjFile(ctxt *Link, b *bio.Writer) {
-func (ctxt *Link) writeSymDebugNamed(s *LSym, name string) {
- fmt.Fprintf(ctxt.Bso, "%s ", name)
- if s.Type != 0 {
- fmt.Fprintf(ctxt.Bso, "%v ", s.Type)
- }
- if s.Static() {
- fmt.Fprint(ctxt.Bso, "static ")
- }
- if s.DuplicateOK() {
- fmt.Fprintf(ctxt.Bso, "dupok ")
+ debugAsmEmit(ctxt)
+
+ genFuncInfoSyms(ctxt)
+
+ w := writer{
+ Writer: goobj.NewWriter(b),
+ ctxt: ctxt,
+ pkgpath: objabi.PathToPrefix(ctxt.Pkgpath),
}
- if s.CFunc() {
- fmt.Fprintf(ctxt.Bso, "cfunc ")
+
+ 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 |= goobj.ObjFlagShared
}
- if s.NoSplit() {
- fmt.Fprintf(ctxt.Bso, "nosplit ")
+ if w.pkgpath == "" {
+ flags |= goobj.ObjFlagNeedNameExpansion
}
- if s.TopFrame() {
- fmt.Fprintf(ctxt.Bso, "topframe ")
+ if ctxt.IsAsm {
+ flags |= goobj.ObjFlagFromAssembly
}
- fmt.Fprintf(ctxt.Bso, "size=%d", s.Size)
- if s.Type == objabi.STEXT {
- fmt.Fprintf(ctxt.Bso, " args=%#x locals=%#x funcid=%#x", uint64(s.Func.Args), uint64(s.Func.Locals), uint64(s.Func.FuncID))
- if s.Leaf() {
- fmt.Fprintf(ctxt.Bso, " leaf")
- }
+ h := goobj.Header{
+ Magic: goobj.Magic,
+ Fingerprint: ctxt.Fingerprint,
+ Flags: flags,
}
- fmt.Fprintf(ctxt.Bso, "\n")
- if s.Type == objabi.STEXT {
- for p := s.Func.Text; p != nil; p = p.Link {
- fmt.Fprintf(ctxt.Bso, "\t%#04x ", uint(int(p.Pc)))
- if ctxt.Debugasm > 1 {
- io.WriteString(ctxt.Bso, p.String())
- } else {
- p.InnermostString(ctxt.Bso)
- }
- fmt.Fprintln(ctxt.Bso)
- }
- }
- for i := 0; i < len(s.P); i += 16 {
- fmt.Fprintf(ctxt.Bso, "\t%#04x", uint(i))
- j := i
- for ; 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])
- b := byte('.')
- if ' ' <= c && c <= 0x7e {
- b = byte(c)
- }
- ctxt.Bso.WriteByte(b)
- }
+ h.Write(w.Writer)
- fmt.Fprintf(ctxt.Bso, "\n")
- }
+ // String table
+ w.StringTable()
- sort.Sort(relocByOff(s.R)) // generate stable output
- for _, r := range s.R {
- name := ""
- if r.Sym != nil {
- name = r.Sym.Name
- } else if r.Type == objabi.R_TLS_LE {
- name = "TLS"
- }
- if ctxt.Arch.InFamily(sys.ARM, sys.PPC64) {
- fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s+%x\n", int(r.Off), r.Siz, r.Type, name, uint64(r.Add))
- } else {
- fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s+%d\n", int(r.Off), r.Siz, r.Type, name, r.Add)
- }
+ // Autolib
+ h.Offsets[goobj.BlkAutolib] = w.Offset()
+ for i := range ctxt.Imports {
+ ctxt.Imports[i].Write(w.Writer)
}
-}
-// relocByOff sorts relocations by their offsets.
-type relocByOff []Reloc
-
-func (x relocByOff) Len() int { return len(x) }
-func (x relocByOff) Less(i, j int) bool { return x[i].Off < x[j].Off }
-func (x relocByOff) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
-
-// implement dwarf.Context
-type dwCtxt struct{ *Link }
+ // Package references
+ h.Offsets[goobj.BlkPkgIdx] = w.Offset()
+ for _, pkg := range w.pkglist {
+ w.StringRef(pkg)
+ }
-func (c dwCtxt) PtrSize() int {
- return c.Arch.PtrSize
-}
-func (c dwCtxt) AddInt(s dwarf.Sym, size int, i int64) {
- ls := s.(*LSym)
- ls.WriteInt(c.Link, ls.Size, size, i)
-}
-func (c dwCtxt) AddUint16(s dwarf.Sym, i uint16) {
- c.AddInt(s, 2, int64(i))
-}
-func (c dwCtxt) AddUint8(s dwarf.Sym, i uint8) {
- b := []byte{byte(i)}
- c.AddBytes(s, b)
-}
-func (c dwCtxt) AddBytes(s dwarf.Sym, b []byte) {
- ls := s.(*LSym)
- ls.WriteBytes(c.Link, ls.Size, b)
-}
-func (c dwCtxt) AddString(s dwarf.Sym, v string) {
- ls := s.(*LSym)
- ls.WriteString(c.Link, ls.Size, len(v), v)
- ls.WriteInt(c.Link, ls.Size, 1, 0)
-}
-func (c dwCtxt) AddAddress(s dwarf.Sym, data interface{}, value int64) {
- ls := s.(*LSym)
- size := c.PtrSize()
- if data != nil {
- rsym := data.(*LSym)
- ls.WriteAddr(c.Link, ls.Size, size, rsym, value)
- } else {
- ls.WriteInt(c.Link, ls.Size, size, value)
+ // File table (for DWARF and pcln generation).
+ h.Offsets[goobj.BlkFile] = w.Offset()
+ for _, f := range ctxt.PosTable.FileTable() {
+ w.StringRef(filepath.ToSlash(f))
}
-}
-func (c dwCtxt) AddCURelativeAddress(s dwarf.Sym, data interface{}, value int64) {
- ls := s.(*LSym)
- rsym := data.(*LSym)
- ls.WriteCURelativeAddr(c.Link, ls.Size, rsym, value)
-}
-func (c dwCtxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64) {
- panic("should be used only in the linker")
-}
-func (c dwCtxt) AddDWARFAddrSectionOffset(s dwarf.Sym, t interface{}, ofs int64) {
- size := 4
- if isDwarf64(c.Link) {
- size = 8
+
+ // Symbol definitions
+ h.Offsets[goobj.BlkSymdef] = w.Offset()
+ for _, s := range ctxt.defs {
+ w.Sym(s)
}
- ls := s.(*LSym)
- rsym := t.(*LSym)
- ls.WriteAddr(c.Link, ls.Size, size, rsym, ofs)
- r := &ls.R[len(ls.R)-1]
- r.Type = objabi.R_DWARFSECREF
-}
+ // Short hashed symbol definitions
+ h.Offsets[goobj.BlkHashed64def] = w.Offset()
+ for _, s := range ctxt.hashed64defs {
+ w.Sym(s)
+ }
-func (c dwCtxt) AddFileRef(s dwarf.Sym, f interface{}) {
- ls := s.(*LSym)
- rsym := f.(*LSym)
- fidx := c.Link.PosTable.FileIndex(rsym.Name)
- // Note the +1 here -- the value we're writing is going to be an
- // index into the DWARF line table file section, whose entries
- // are numbered starting at 1, not 0.
- ls.WriteInt(c.Link, ls.Size, 4, int64(fidx+1))
-}
+ // Hashed symbol definitions
+ h.Offsets[goobj.BlkHasheddef] = w.Offset()
+ for _, s := range ctxt.hasheddefs {
+ w.Sym(s)
+ }
-func (c dwCtxt) CurrentOffset(s dwarf.Sym) int64 {
- ls := s.(*LSym)
- return ls.Size
-}
+ // Non-pkg symbol definitions
+ h.Offsets[goobj.BlkNonpkgdef] = w.Offset()
+ for _, s := range ctxt.nonpkgdefs {
+ w.Sym(s)
+ }
-// Here "from" is a symbol corresponding to an inlined or concrete
-// function, "to" is the symbol for the corresponding abstract
-// function, and "dclIdx" is the index of the symbol of interest with
-// respect to the Dcl slice of the original pre-optimization version
-// of the inlined function.
-func (c dwCtxt) RecordDclReference(from dwarf.Sym, to dwarf.Sym, dclIdx int, inlIndex int) {
- ls := from.(*LSym)
- tls := to.(*LSym)
- ridx := len(ls.R) - 1
- c.Link.DwFixups.ReferenceChildDIE(ls, ridx, tls, dclIdx, inlIndex)
-}
+ // Non-pkg symbol references
+ h.Offsets[goobj.BlkNonpkgref] = w.Offset()
+ for _, s := range ctxt.nonpkgrefs {
+ w.Sym(s)
+ }
-func (c dwCtxt) RecordChildDieOffsets(s dwarf.Sym, vars []*dwarf.Var, offsets []int32) {
- ls := s.(*LSym)
- c.Link.DwFixups.RegisterChildDIEOffsets(ls, vars, offsets)
-}
+ // Referenced package symbol flags
+ h.Offsets[goobj.BlkRefFlags] = w.Offset()
+ w.refFlags()
-func (c dwCtxt) Logf(format string, args ...interface{}) {
- c.Link.Logf(format, args...)
-}
+ // Hashes
+ h.Offsets[goobj.BlkHash64] = w.Offset()
+ for _, s := range ctxt.hashed64defs {
+ w.Hash64(s)
+ }
+ h.Offsets[goobj.BlkHash] = w.Offset()
+ for _, s := range ctxt.hasheddefs {
+ w.Hash(s)
+ }
+ // TODO: hashedrefs unused/unsupported for now
-func isDwarf64(ctxt *Link) bool {
- return ctxt.Headtype == objabi.Haix
-}
+ // Reloc indexes
+ h.Offsets[goobj.BlkRelocIdx] = w.Offset()
+ nreloc := uint32(0)
+ lists := [][]*LSym{ctxt.defs, ctxt.hashed64defs, ctxt.hasheddefs, ctxt.nonpkgdefs}
+ for _, list := range lists {
+ for _, s := range list {
+ w.Uint32(nreloc)
+ nreloc += uint32(len(s.R))
+ }
+ }
+ w.Uint32(nreloc)
-func (ctxt *Link) dwarfSym(s *LSym) (dwarfInfoSym, dwarfLocSym, dwarfRangesSym, dwarfAbsFnSym, dwarfDebugLines *LSym) {
- if s.Type != objabi.STEXT {
- ctxt.Diag("dwarfSym of non-TEXT %v", s)
+ // Symbol Info indexes
+ h.Offsets[goobj.BlkAuxIdx] = w.Offset()
+ naux := uint32(0)
+ for _, list := range lists {
+ for _, s := range list {
+ w.Uint32(naux)
+ naux += uint32(nAuxSym(s))
+ }
}
- if s.Func.dwarfInfoSym == nil {
- s.Func.dwarfInfoSym = &LSym{
- Type: objabi.SDWARFFCN,
+ w.Uint32(naux)
+
+ // Data indexes
+ h.Offsets[goobj.BlkDataIdx] = w.Offset()
+ dataOff := uint32(0)
+ for _, list := range lists {
+ for _, s := range list {
+ w.Uint32(dataOff)
+ dataOff += uint32(len(s.P))
}
- if ctxt.Flag_locationlists {
- s.Func.dwarfLocSym = &LSym{
- Type: objabi.SDWARFLOC,
+ }
+ w.Uint32(dataOff)
+
+ // Relocs
+ h.Offsets[goobj.BlkReloc] = w.Offset()
+ for _, list := range lists {
+ for _, s := range list {
+ for i := range s.R {
+ w.Reloc(&s.R[i])
}
}
- s.Func.dwarfRangesSym = &LSym{
- Type: objabi.SDWARFRANGE,
+ }
+
+ // Aux symbol info
+ h.Offsets[goobj.BlkAux] = w.Offset()
+ for _, list := range lists {
+ for _, s := range list {
+ w.Aux(s)
}
- s.Func.dwarfDebugLinesSym = &LSym{
- Type: objabi.SDWARFLINES,
+ }
+
+ // Data
+ h.Offsets[goobj.BlkData] = w.Offset()
+ for _, list := range lists {
+ for _, s := range list {
+ w.Bytes(s.P)
}
- if s.WasInlined() {
- s.Func.dwarfAbsFnSym = ctxt.DwFixups.AbsFuncDwarfSym(s)
+ }
+
+ // Pcdata
+ h.Offsets[goobj.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)
+ }
}
}
- return s.Func.dwarfInfoSym, s.Func.dwarfLocSym, s.Func.dwarfRangesSym, s.Func.dwarfAbsFnSym, s.Func.dwarfDebugLinesSym
-}
-func (s *LSym) Length(dwarfContext interface{}) int64 {
- return s.Size
+ // Blocks used only by tools (objdump, nm).
+
+ // Referenced symbol names from other packages
+ h.Offsets[goobj.BlkRefName] = w.Offset()
+ w.refNames()
+
+ h.Offsets[goobj.BlkEnd] = w.Offset()
+
+ // Fix up block offsets in the header
+ end := start + int64(w.Offset())
+ b.MustSeek(start, 0)
+ h.Write(w.Writer)
+ b.MustSeek(end, 0)
}
-// fileSymbol returns a symbol corresponding to the source file of the
-// first instruction (prog) of the specified function. This will
-// presumably be the file in which the function is defined.
-func (ctxt *Link) fileSymbol(fn *LSym) *LSym {
- p := fn.Func.Text
- if p != nil {
- f, _ := linkgetlineFromPos(ctxt, p.Pos)
- fsym := ctxt.Lookup(f)
- return fsym
- }
- return nil
+type writer struct {
+ *goobj.Writer
+ ctxt *Link
+ pkgpath string // the package import path (escaped), "" if unknown
+ pkglist []string // list of packages referenced, indexed by ctxt.pkgIdx
}
-// populateDWARF fills in the DWARF Debugging Information Entries for
-// TEXT symbol 's'. The various DWARF symbols must already have been
-// initialized in InitTextSym.
-func (ctxt *Link) populateDWARF(curfn interface{}, s *LSym, myimportpath string) {
- info, loc, ranges, absfunc, lines := ctxt.dwarfSym(s)
- if info.Size != 0 {
- ctxt.Diag("makeFuncDebugEntry double process %v", s)
- }
- var scopes []dwarf.Scope
- var inlcalls dwarf.InlCalls
- if ctxt.DebugInfo != nil {
- scopes, inlcalls = ctxt.DebugInfo(s, info, curfn)
- }
- var err error
- dwctxt := dwCtxt{ctxt}
- filesym := ctxt.fileSymbol(s)
- fnstate := &dwarf.FnState{
- Name: s.Name,
- Importpath: myimportpath,
- Info: info,
- Filesym: filesym,
- Loc: loc,
- Ranges: ranges,
- Absfn: absfunc,
- StartPC: s,
- Size: s.Size,
- External: !s.Static(),
- Scopes: scopes,
- InlCalls: inlcalls,
- UseBASEntries: ctxt.UseBASEntries,
- }
- if absfunc != nil {
- err = dwarf.PutAbstractFunc(dwctxt, fnstate)
- if err != nil {
- ctxt.Diag("emitting DWARF for %s failed: %v", s.Name, err)
- }
- err = dwarf.PutConcreteFunc(dwctxt, fnstate)
- } else {
- err = dwarf.PutDefaultFunc(dwctxt, fnstate)
- }
- if err != nil {
- ctxt.Diag("emitting DWARF for %s failed: %v", s.Name, err)
- }
- // Fill in the debug lines symbol.
- ctxt.generateDebugLinesSymbol(s, lines)
+// 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
+ }
}
-// DwarfIntConst creates a link symbol for an integer constant with the
-// given name, type and value.
-func (ctxt *Link) DwarfIntConst(myimportpath, name, typename string, val int64) {
- if myimportpath == "" {
- return
+func (w *writer) StringTable() {
+ w.AddString("")
+ for _, p := range w.ctxt.Imports {
+ w.AddString(p.Pkg)
}
- s := ctxt.LookupInit(dwarf.ConstInfoPrefix+myimportpath, func(s *LSym) {
- s.Type = objabi.SDWARFCONST
- ctxt.Data = append(ctxt.Data, s)
+ for _, pkg := range w.pkglist {
+ w.AddString(pkg)
+ }
+ w.ctxt.traverseSyms(traverseAll, func(s *LSym) {
+ // TODO: this includes references of indexed symbols from other packages,
+ // for which the linker doesn't need the name. Consider moving them to
+ // a separate block (for tools only).
+ if w.pkgpath != "" {
+ s.Name = strings.Replace(s.Name, "\"\".", w.pkgpath+".", -1)
+ }
+ // Don't put names of builtins into the string table (to save
+ // space).
+ if s.PkgIdx == goobj.PkgIdxBuiltin {
+ return
+ }
+ w.AddString(s.Name)
})
- dwarf.PutIntConst(dwCtxt{ctxt}, s, ctxt.Lookup(dwarf.InfoPrefix+typename), myimportpath+"."+name, val)
-}
-func (ctxt *Link) DwarfAbstractFunc(curfn interface{}, s *LSym, myimportpath string) {
- absfn := ctxt.DwFixups.AbsFuncDwarfSym(s)
- if absfn.Size != 0 {
- ctxt.Diag("internal error: DwarfAbstractFunc double process %v", s)
- }
- if s.Func == nil {
- s.Func = new(FuncInfo)
- }
- scopes, _ := ctxt.DebugInfo(s, absfn, curfn)
- dwctxt := dwCtxt{ctxt}
- filesym := ctxt.fileSymbol(s)
- fnstate := dwarf.FnState{
- Name: s.Name,
- Importpath: myimportpath,
- Info: absfn,
- Filesym: filesym,
- Absfn: absfn,
- External: !s.Static(),
- Scopes: scopes,
- UseBASEntries: ctxt.UseBASEntries,
- }
- if err := dwarf.PutAbstractFunc(dwctxt, &fnstate); err != nil {
- ctxt.Diag("emitting DWARF for %s failed: %v", s.Name, err)
+ // All filenames are in the postable.
+ for _, f := range w.ctxt.PosTable.FileTable() {
+ w.AddString(filepath.ToSlash(f))
}
}
-// This table is designed to aid in the creation of references between
-// DWARF subprogram DIEs.
-//
-// In most cases when one DWARF DIE has to refer to another DWARF DIE,
-// the target of the reference has an LSym, which makes it easy to use
-// the existing relocation mechanism. For DWARF inlined routine DIEs,
-// however, the subprogram DIE has to refer to a child
-// parameter/variable DIE of the abstract subprogram. This child DIE
-// doesn't have an LSym, and also of interest is the fact that when
-// DWARF generation is happening for inlined function F within caller
-// G, it's possible that DWARF generation hasn't happened yet for F,
-// so there is no way to know the offset of a child DIE within F's
-// abstract function. Making matters more complex, each inlined
-// instance of F may refer to a subset of the original F's variables
-// (depending on what happens with optimization, some vars may be
-// eliminated).
-//
-// The fixup table below helps overcome this hurdle. At the point
-// where a parameter/variable reference is made (via a call to
-// "ReferenceChildDIE"), a fixup record is generate that records
-// the relocation that is targeting that child variable. At a later
-// point when the abstract function DIE is emitted, there will be
-// a call to "RegisterChildDIEOffsets", at which point the offsets
-// needed to apply fixups are captured. Finally, once the parallel
-// portion of the compilation is done, fixups can actually be applied
-// during the "Finalize" method (this can't be done during the
-// parallel portion of the compile due to the possibility of data
-// races).
-//
-// This table is also used to record the "precursor" function node for
-// each function that is the target of an inline -- child DIE references
-// have to be made with respect to the original pre-optimization
-// version of the function (to allow for the fact that each inlined
-// body may be optimized differently).
-type DwarfFixupTable struct {
- ctxt *Link
- mu sync.Mutex
- symtab map[*LSym]int // maps abstract fn LSYM to index in svec
- svec []symFixups
- precursor map[*LSym]fnState // maps fn Lsym to precursor Node, absfn sym
+func (w *writer) Sym(s *LSym) {
+ abi := uint16(s.ABI())
+ if s.Static() {
+ abi = goobj.SymABIstatic
+ }
+ flag := uint8(0)
+ if s.DuplicateOK() {
+ flag |= goobj.SymFlagDupok
+ }
+ if s.Local() {
+ flag |= goobj.SymFlagLocal
+ }
+ if s.MakeTypelink() {
+ flag |= goobj.SymFlagTypelink
+ }
+ if s.Leaf() {
+ flag |= goobj.SymFlagLeaf
+ }
+ if s.NoSplit() {
+ flag |= goobj.SymFlagNoSplit
+ }
+ if s.ReflectMethod() {
+ flag |= goobj.SymFlagReflectMethod
+ }
+ if s.TopFrame() {
+ flag |= goobj.SymFlagTopFrame
+ }
+ if strings.HasPrefix(s.Name, "type.") && s.Name[5] != '.' && s.Type == objabi.SRODATA {
+ flag |= goobj.SymFlagGoType
+ }
+ flag2 := uint8(0)
+ if s.UsedInIface() {
+ flag2 |= goobj.SymFlagUsedInIface
+ }
+ if strings.HasPrefix(s.Name, "go.itab.") && s.Type == objabi.SRODATA {
+ flag2 |= goobj.SymFlagItab
+ }
+ name := s.Name
+ if strings.HasPrefix(name, "gofile..") {
+ name = filepath.ToSlash(name)
+ }
+ var align uint32
+ if s.Func != nil {
+ align = uint32(s.Func.Align)
+ }
+ if s.ContentAddressable() {
+ // We generally assume data symbols are natually aligned,
+ // except for strings. If we dedup a string symbol and a
+ // non-string symbol with the same content, we should keep
+ // the largest alignment.
+ // TODO: maybe the compiler could set the alignment for all
+ // data symbols more carefully.
+ if s.Size != 0 && !strings.HasPrefix(s.Name, "go.string.") {
+ switch {
+ case w.ctxt.Arch.PtrSize == 8 && s.Size%8 == 0:
+ align = 8
+ case s.Size%4 == 0:
+ align = 4
+ case s.Size%2 == 0:
+ align = 2
+ }
+ // don't bother setting align to 1.
+ }
+ }
+ var o goobj.Sym
+ o.SetName(name, w.Writer)
+ o.SetABI(abi)
+ o.SetType(uint8(s.Type))
+ o.SetFlag(flag)
+ o.SetFlag2(flag2)
+ o.SetSiz(uint32(s.Size))
+ o.SetAlign(align)
+ o.Write(w.Writer)
}
-type symFixups struct {
- fixups []relFixup
- doffsets []declOffset
- inlIndex int32
- defseen bool
+func (w *writer) Hash64(s *LSym) {
+ if !s.ContentAddressable() || len(s.R) != 0 {
+ panic("Hash of non-content-addresable symbol")
+ }
+ b := contentHash64(s)
+ w.Bytes(b[:])
}
-type declOffset struct {
- // Index of variable within DCL list of pre-optimization function
- dclIdx int32
- // Offset of var's child DIE with respect to containing subprogram DIE
- offset int32
+func (w *writer) Hash(s *LSym) {
+ if !s.ContentAddressable() {
+ panic("Hash of non-content-addresable symbol")
+ }
+ b := w.contentHash(s)
+ w.Bytes(b[:])
}
-type relFixup struct {
- refsym *LSym
- relidx int32
- dclidx int32
+func contentHash64(s *LSym) goobj.Hash64Type {
+ var b goobj.Hash64Type
+ copy(b[:], s.P)
+ return b
}
-type fnState struct {
- // precursor function (really *gc.Node)
- precursor interface{}
- // abstract function symbol
- absfn *LSym
+// Compute the content hash for a content-addressable symbol.
+// We build a content hash based on its content and relocations.
+// Depending on the category of the referenced symbol, we choose
+// different hash algorithms such that the hash is globally
+// consistent.
+// - For referenced content-addressable symbol, its content hash
+// is globally consistent.
+// - For package symbol and builtin symbol, its local index is
+// globally consistent.
+// - For non-package symbol, its fully-expanded name is globally
+// consistent. For now, we require we know the current package
+// path so we can always expand symbol names. (Otherwise,
+// symbols with relocations are not considered hashable.)
+//
+// For now, we assume there is no circular dependencies among
+// hashed symbols.
+func (w *writer) contentHash(s *LSym) goobj.HashType {
+ h := sha1.New()
+ // The compiler trims trailing zeros _sometimes_. We just do
+ // it always.
+ h.Write(bytes.TrimRight(s.P, "\x00"))
+ var tmp [14]byte
+ for i := range s.R {
+ r := &s.R[i]
+ binary.LittleEndian.PutUint32(tmp[:4], uint32(r.Off))
+ tmp[4] = r.Siz
+ tmp[5] = uint8(r.Type)
+ binary.LittleEndian.PutUint64(tmp[6:14], uint64(r.Add))
+ h.Write(tmp[:])
+ rs := r.Sym
+ switch rs.PkgIdx {
+ case goobj.PkgIdxHashed64:
+ h.Write([]byte{0})
+ t := contentHash64(rs)
+ h.Write(t[:])
+ case goobj.PkgIdxHashed:
+ h.Write([]byte{1})
+ t := w.contentHash(rs)
+ h.Write(t[:])
+ case goobj.PkgIdxNone:
+ h.Write([]byte{2})
+ io.WriteString(h, rs.Name) // name is already expanded at this point
+ case goobj.PkgIdxBuiltin:
+ h.Write([]byte{3})
+ binary.LittleEndian.PutUint32(tmp[:4], uint32(rs.SymIdx))
+ h.Write(tmp[:4])
+ case goobj.PkgIdxSelf:
+ io.WriteString(h, w.pkgpath)
+ binary.LittleEndian.PutUint32(tmp[:4], uint32(rs.SymIdx))
+ h.Write(tmp[:4])
+ default:
+ io.WriteString(h, rs.Pkg)
+ binary.LittleEndian.PutUint32(tmp[:4], uint32(rs.SymIdx))
+ h.Write(tmp[:4])
+ }
+ }
+ var b goobj.HashType
+ copy(b[:], h.Sum(nil))
+ return b
}
-func NewDwarfFixupTable(ctxt *Link) *DwarfFixupTable {
- return &DwarfFixupTable{
- ctxt: ctxt,
- symtab: make(map[*LSym]int),
- precursor: make(map[*LSym]fnState),
+func makeSymRef(s *LSym) goobj.SymRef {
+ if s == nil {
+ return goobj.SymRef{}
+ }
+ if s.PkgIdx == 0 || !s.Indexed() {
+ fmt.Printf("unindexed symbol reference: %v\n", s)
+ panic("unindexed symbol reference")
}
+ return goobj.SymRef{PkgIdx: uint32(s.PkgIdx), SymIdx: uint32(s.SymIdx)}
}
-func (ft *DwarfFixupTable) GetPrecursorFunc(s *LSym) interface{} {
- if fnstate, found := ft.precursor[s]; found {
- return fnstate.precursor
- }
- return nil
+func (w *writer) Reloc(r *Reloc) {
+ var o goobj.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 goobj.Aux
+ o.SetType(typ)
+ o.SetSym(makeSymRef(rs))
+ o.Write(w.Writer)
}
-func (ft *DwarfFixupTable) SetPrecursorFunc(s *LSym, fn interface{}) {
- if _, found := ft.precursor[s]; found {
- ft.ctxt.Diag("internal error: DwarfFixupTable.SetPrecursorFunc double call on %v", s)
+func (w *writer) Aux(s *LSym) {
+ if s.Gotype != nil {
+ w.aux1(goobj.AuxGotype, s.Gotype)
}
+ if s.Func != nil {
+ w.aux1(goobj.AuxFuncInfo, s.Func.FuncInfoSym)
- // initialize abstract function symbol now. This is done here so
- // as to avoid data races later on during the parallel portion of
- // the back end.
- absfn := ft.ctxt.LookupDerived(s, dwarf.InfoPrefix+s.Name+dwarf.AbstractFuncSuffix)
- absfn.Set(AttrDuplicateOK, true)
- absfn.Type = objabi.SDWARFABSFCN
- ft.ctxt.Data = append(ft.ctxt.Data, absfn)
+ for _, d := range s.Func.Pcln.Funcdata {
+ w.aux1(goobj.AuxFuncdata, d)
+ }
- // In the case of "late" inlining (inlines that happen during
- // wrapper generation as opposed to the main inlining phase) it's
- // possible that we didn't cache the abstract function sym for the
- // text symbol -- do so now if needed. See issue 38068.
- if s.Func != nil && s.Func.dwarfAbsFnSym == nil {
- s.Func.dwarfAbsFnSym = absfn
+ if s.Func.dwarfInfoSym != nil && s.Func.dwarfInfoSym.Size != 0 {
+ w.aux1(goobj.AuxDwarfInfo, s.Func.dwarfInfoSym)
+ }
+ if s.Func.dwarfLocSym != nil && s.Func.dwarfLocSym.Size != 0 {
+ w.aux1(goobj.AuxDwarfLoc, s.Func.dwarfLocSym)
+ }
+ if s.Func.dwarfRangesSym != nil && s.Func.dwarfRangesSym.Size != 0 {
+ w.aux1(goobj.AuxDwarfRanges, s.Func.dwarfRangesSym)
+ }
+ if s.Func.dwarfDebugLinesSym != nil && s.Func.dwarfDebugLinesSym.Size != 0 {
+ w.aux1(goobj.AuxDwarfLines, s.Func.dwarfDebugLinesSym)
+ }
}
+}
- ft.precursor[s] = fnState{precursor: fn, absfn: absfn}
+// Emits flags of referenced indexed symbols.
+func (w *writer) refFlags() {
+ seen := make(map[*LSym]bool)
+ w.ctxt.traverseSyms(traverseRefs, func(rs *LSym) { // only traverse refs, not auxs, as tools don't need auxs
+ switch rs.PkgIdx {
+ case goobj.PkgIdxNone, goobj.PkgIdxHashed64, goobj.PkgIdxHashed, goobj.PkgIdxBuiltin, goobj.PkgIdxSelf: // not an external indexed reference
+ return
+ case goobj.PkgIdxInvalid:
+ panic("unindexed symbol reference")
+ }
+ if seen[rs] {
+ return
+ }
+ seen[rs] = true
+ symref := makeSymRef(rs)
+ flag2 := uint8(0)
+ if rs.UsedInIface() {
+ flag2 |= goobj.SymFlagUsedInIface
+ }
+ if flag2 == 0 {
+ return // no need to write zero flags
+ }
+ var o goobj.RefFlags
+ o.SetSym(symref)
+ o.SetFlag2(flag2)
+ o.Write(w.Writer)
+ })
}
-// Make a note of a child DIE reference: relocation 'ridx' within symbol 's'
-// is targeting child 'c' of DIE with symbol 'tgt'.
-func (ft *DwarfFixupTable) ReferenceChildDIE(s *LSym, ridx int, tgt *LSym, dclidx int, inlIndex int) {
- // Protect against concurrent access if multiple backend workers
- ft.mu.Lock()
- defer ft.mu.Unlock()
-
- // Create entry for symbol if not already present.
- idx, found := ft.symtab[tgt]
- if !found {
- ft.svec = append(ft.svec, symFixups{inlIndex: int32(inlIndex)})
- idx = len(ft.svec) - 1
- ft.symtab[tgt] = idx
- }
-
- // Do we have child DIE offsets available? If so, then apply them,
- // otherwise create a fixup record.
- sf := &ft.svec[idx]
- if len(sf.doffsets) > 0 {
- found := false
- for _, do := range sf.doffsets {
- if do.dclIdx == int32(dclidx) {
- off := do.offset
- s.R[ridx].Add += int64(off)
- found = true
- break
- }
+// Emits names of referenced indexed symbols, used by tools (objdump, nm)
+// only.
+func (w *writer) refNames() {
+ seen := make(map[*LSym]bool)
+ w.ctxt.traverseSyms(traverseRefs, func(rs *LSym) { // only traverse refs, not auxs, as tools don't need auxs
+ switch rs.PkgIdx {
+ case goobj.PkgIdxNone, goobj.PkgIdxHashed64, goobj.PkgIdxHashed, goobj.PkgIdxBuiltin, goobj.PkgIdxSelf: // not an external indexed reference
+ return
+ case goobj.PkgIdxInvalid:
+ panic("unindexed symbol reference")
}
- if !found {
- ft.ctxt.Diag("internal error: DwarfFixupTable.ReferenceChildDIE unable to locate child DIE offset for dclIdx=%d src=%v tgt=%v", dclidx, s, tgt)
+ if seen[rs] {
+ return
+ }
+ seen[rs] = true
+ symref := makeSymRef(rs)
+ var o goobj.RefName
+ o.SetSym(symref)
+ o.SetName(rs.Name, w.Writer)
+ o.Write(w.Writer)
+ })
+ // TODO: output in sorted order?
+ // Currently tools (cmd/internal/goobj package) doesn't use mmap,
+ // and it just read it into a map in memory upfront. If it uses
+ // mmap, if the output is sorted, it probably could avoid reading
+ // into memory and just do lookups in the mmap'd object file.
+}
+
+// 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++
}
- } else {
- sf.fixups = append(sf.fixups, relFixup{s, int32(ridx), int32(dclidx)})
}
+ return n
}
-// Called once DWARF generation is complete for a given abstract function,
-// whose children might have been referenced via a call above. Stores
-// the offsets for any child DIEs (vars, params) so that they can be
-// consumed later in on DwarfFixupTable.Finalize, which applies any
-// outstanding fixups.
-func (ft *DwarfFixupTable) RegisterChildDIEOffsets(s *LSym, vars []*dwarf.Var, coffsets []int32) {
- // Length of these two slices should agree
- if len(vars) != len(coffsets) {
- ft.ctxt.Diag("internal error: RegisterChildDIEOffsets vars/offsets length mismatch")
- return
- }
+// 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 := goobj.FuncInfo{
+ Args: uint32(s.Func.Args),
+ Locals: uint32(s.Func.Locals),
+ FuncID: objabi.FuncID(s.Func.FuncID),
+ }
+ 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)
+ }
+ i := 0
+ o.File = make([]goobj.CUFileIndex, len(pc.UsedFiles))
+ for f := range pc.UsedFiles {
+ o.File[i] = f
+ i++
+ }
+ sort.Slice(o.File, func(i, j int) bool { return o.File[i] < o.File[j] })
+ o.InlTree = make([]goobj.InlTreeNode, len(pc.InlTree.nodes))
+ for i, inl := range pc.InlTree.nodes {
+ f, l := getFileIndexAndLine(ctxt, inl.Pos)
+ o.InlTree[i] = goobj.InlTreeNode{
+ Parent: int32(inl.Parent),
+ File: goobj.CUFileIndex(f),
+ Line: l,
+ Func: makeSymRef(inl.Func),
+ ParentPC: inl.ParentPC,
+ }
+ }
- // Generate the slice of declOffset's based in vars/coffsets
- doffsets := make([]declOffset, len(coffsets))
- for i := range coffsets {
- doffsets[i].dclIdx = vars[i].ChildIndex
- doffsets[i].offset = coffsets[i]
+ o.Write(&b)
+ isym := &LSym{
+ Type: objabi.SDATA, // for now, I don't think it matters
+ PkgIdx: goobj.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 = goobj.PkgIdxSelf
+ s.SymIdx = symidx
+ s.Set(AttrIndexed, true)
+ symidx++
+ infosyms = append(infosyms, s)
+ }
}
+ ctxt.defs = append(ctxt.defs, infosyms...)
+}
- ft.mu.Lock()
- defer ft.mu.Unlock()
-
- // Store offsets for this symbol.
- idx, found := ft.symtab[s]
- if !found {
- sf := symFixups{inlIndex: -1, defseen: true, doffsets: doffsets}
- ft.svec = append(ft.svec, sf)
- ft.symtab[s] = len(ft.svec) - 1
- } else {
- sf := &ft.svec[idx]
- sf.doffsets = doffsets
- sf.defseen = true
+// 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 (ft *DwarfFixupTable) processFixups(slot int, s *LSym) {
- sf := &ft.svec[slot]
- for _, f := range sf.fixups {
- dfound := false
- for _, doffset := range sf.doffsets {
- if doffset.dclIdx == f.dclidx {
- f.refsym.R[f.relidx].Add += int64(doffset.offset)
- dfound = true
- break
+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)
}
- }
- if !dfound {
- ft.ctxt.Diag("internal error: DwarfFixupTable has orphaned fixup on %v targeting %v relidx=%d dclidx=%d", f.refsym, s, f.relidx, f.dclidx)
+ ctxt.traverseAuxSyms(traverseAux, fn)
}
}
}
-// return the LSym corresponding to the 'abstract subprogram' DWARF
-// info entry for a function.
-func (ft *DwarfFixupTable) AbsFuncDwarfSym(fnsym *LSym) *LSym {
- // Protect against concurrent access if multiple backend workers
- ft.mu.Lock()
- defer ft.mu.Unlock()
-
- if fnstate, found := ft.precursor[fnsym]; found {
- return fnstate.absfn
- }
- ft.ctxt.Diag("internal error: AbsFuncDwarfSym requested for %v, not seen during inlining", fnsym)
- return nil
+func (ctxt *Link) writeSymDebug(s *LSym) {
+ ctxt.writeSymDebugNamed(s, s.Name)
}
-// Called after all functions have been compiled; the main job of this
-// function is to identify cases where there are outstanding fixups.
-// This scenario crops up when we have references to variables of an
-// inlined routine, but that routine is defined in some other package.
-// This helper walks through and locate these fixups, then invokes a
-// helper to create an abstract subprogram DIE for each one.
-func (ft *DwarfFixupTable) Finalize(myimportpath string, trace bool) {
- if trace {
- ft.ctxt.Logf("DwarfFixupTable.Finalize invoked for %s\n", myimportpath)
+func (ctxt *Link) writeSymDebugNamed(s *LSym, name string) {
+ fmt.Fprintf(ctxt.Bso, "%s ", name)
+ if s.Type != 0 {
+ fmt.Fprintf(ctxt.Bso, "%v ", s.Type)
}
-
- // Collect up the keys from the precursor map, then sort the
- // resulting list (don't want to rely on map ordering here).
- fns := make([]*LSym, len(ft.precursor))
- idx := 0
- for fn := range ft.precursor {
- fns[idx] = fn
- idx++
+ if s.Static() {
+ fmt.Fprint(ctxt.Bso, "static ")
}
- sort.Sort(BySymName(fns))
-
- // Should not be called during parallel portion of compilation.
- if ft.ctxt.InParallel {
- ft.ctxt.Diag("internal error: DwarfFixupTable.Finalize call during parallel backend")
+ if s.DuplicateOK() {
+ fmt.Fprintf(ctxt.Bso, "dupok ")
}
-
- // Generate any missing abstract functions.
- for _, s := range fns {
- absfn := ft.AbsFuncDwarfSym(s)
- slot, found := ft.symtab[absfn]
- if !found || !ft.svec[slot].defseen {
- ft.ctxt.GenAbstractFunc(s)
+ if s.CFunc() {
+ fmt.Fprintf(ctxt.Bso, "cfunc ")
+ }
+ if s.NoSplit() {
+ fmt.Fprintf(ctxt.Bso, "nosplit ")
+ }
+ if s.TopFrame() {
+ fmt.Fprintf(ctxt.Bso, "topframe ")
+ }
+ fmt.Fprintf(ctxt.Bso, "size=%d", s.Size)
+ if s.Type == objabi.STEXT {
+ fmt.Fprintf(ctxt.Bso, " args=%#x locals=%#x funcid=%#x", uint64(s.Func.Args), uint64(s.Func.Locals), uint64(s.Func.FuncID))
+ if s.Leaf() {
+ fmt.Fprintf(ctxt.Bso, " leaf")
+ }
+ }
+ fmt.Fprintf(ctxt.Bso, "\n")
+ if s.Type == objabi.STEXT {
+ for p := s.Func.Text; p != nil; p = p.Link {
+ fmt.Fprintf(ctxt.Bso, "\t%#04x ", uint(int(p.Pc)))
+ if ctxt.Debugasm > 1 {
+ io.WriteString(ctxt.Bso, p.String())
+ } else {
+ p.InnermostString(ctxt.Bso)
+ }
+ fmt.Fprintln(ctxt.Bso)
+ }
+ }
+ for i := 0; i < len(s.P); i += 16 {
+ fmt.Fprintf(ctxt.Bso, "\t%#04x", uint(i))
+ j := i
+ for ; 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])
+ b := byte('.')
+ if ' ' <= c && c <= 0x7e {
+ b = byte(c)
+ }
+ ctxt.Bso.WriteByte(b)
+ }
+
+ fmt.Fprintf(ctxt.Bso, "\n")
}
- // Apply fixups.
- for _, s := range fns {
- absfn := ft.AbsFuncDwarfSym(s)
- slot, found := ft.symtab[absfn]
- if !found {
- ft.ctxt.Diag("internal error: DwarfFixupTable.Finalize orphan abstract function for %v", s)
+ sort.Sort(relocByOff(s.R)) // generate stable output
+ for _, r := range s.R {
+ name := ""
+ if r.Sym != nil {
+ name = r.Sym.Name
+ } else if r.Type == objabi.R_TLS_LE {
+ name = "TLS"
+ }
+ if ctxt.Arch.InFamily(sys.ARM, sys.PPC64) {
+ fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s+%x\n", int(r.Off), r.Siz, r.Type, name, uint64(r.Add))
} else {
- ft.processFixups(slot, s)
+ fmt.Fprintf(ctxt.Bso, "\trel %d+%d t=%d %s+%d\n", int(r.Off), r.Siz, r.Type, name, r.Add)
}
}
}
-type BySymName []*LSym
+// relocByOff sorts relocations by their offsets.
+type relocByOff []Reloc
-func (s BySymName) Len() int { return len(s) }
-func (s BySymName) Less(i, j int) bool { return s[i].Name < s[j].Name }
-func (s BySymName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+func (x relocByOff) Len() int { return len(x) }
+func (x relocByOff) Less(i, j int) bool { return x[i].Off < x[j].Off }
+func (x relocByOff) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
+++ /dev/null
-// 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/goobj"
- "cmd/internal/objabi"
- "crypto/sha1"
- "encoding/binary"
- "fmt"
- "io"
- "path/filepath"
- "sort"
- "strings"
-)
-
-// Entry point of writing new object file.
-func WriteObjFile(ctxt *Link, b *bio.Writer) {
-
- debugAsmEmit(ctxt)
-
- genFuncInfoSyms(ctxt)
-
- w := writer{
- Writer: goobj.NewWriter(b),
- ctxt: ctxt,
- pkgpath: objabi.PathToPrefix(ctxt.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 |= goobj.ObjFlagShared
- }
- if w.pkgpath == "" {
- flags |= goobj.ObjFlagNeedNameExpansion
- }
- if ctxt.IsAsm {
- flags |= goobj.ObjFlagFromAssembly
- }
- h := goobj.Header{
- Magic: goobj.Magic,
- Fingerprint: ctxt.Fingerprint,
- Flags: flags,
- }
- h.Write(w.Writer)
-
- // String table
- w.StringTable()
-
- // Autolib
- h.Offsets[goobj.BlkAutolib] = w.Offset()
- for i := range ctxt.Imports {
- ctxt.Imports[i].Write(w.Writer)
- }
-
- // Package references
- h.Offsets[goobj.BlkPkgIdx] = w.Offset()
- for _, pkg := range w.pkglist {
- w.StringRef(pkg)
- }
-
- // File table (for DWARF and pcln generation).
- h.Offsets[goobj.BlkFile] = w.Offset()
- for _, f := range ctxt.PosTable.FileTable() {
- w.StringRef(filepath.ToSlash(f))
- }
-
- // Symbol definitions
- h.Offsets[goobj.BlkSymdef] = w.Offset()
- for _, s := range ctxt.defs {
- w.Sym(s)
- }
-
- // Short hashed symbol definitions
- h.Offsets[goobj.BlkHashed64def] = w.Offset()
- for _, s := range ctxt.hashed64defs {
- w.Sym(s)
- }
-
- // Hashed symbol definitions
- h.Offsets[goobj.BlkHasheddef] = w.Offset()
- for _, s := range ctxt.hasheddefs {
- w.Sym(s)
- }
-
- // Non-pkg symbol definitions
- h.Offsets[goobj.BlkNonpkgdef] = w.Offset()
- for _, s := range ctxt.nonpkgdefs {
- w.Sym(s)
- }
-
- // Non-pkg symbol references
- h.Offsets[goobj.BlkNonpkgref] = w.Offset()
- for _, s := range ctxt.nonpkgrefs {
- w.Sym(s)
- }
-
- // Referenced package symbol flags
- h.Offsets[goobj.BlkRefFlags] = w.Offset()
- w.refFlags()
-
- // Hashes
- h.Offsets[goobj.BlkHash64] = w.Offset()
- for _, s := range ctxt.hashed64defs {
- w.Hash64(s)
- }
- h.Offsets[goobj.BlkHash] = w.Offset()
- for _, s := range ctxt.hasheddefs {
- w.Hash(s)
- }
- // TODO: hashedrefs unused/unsupported for now
-
- // Reloc indexes
- h.Offsets[goobj.BlkRelocIdx] = w.Offset()
- nreloc := uint32(0)
- lists := [][]*LSym{ctxt.defs, ctxt.hashed64defs, ctxt.hasheddefs, 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[goobj.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[goobj.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[goobj.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[goobj.BlkAux] = w.Offset()
- for _, list := range lists {
- for _, s := range list {
- w.Aux(s)
- }
- }
-
- // Data
- h.Offsets[goobj.BlkData] = w.Offset()
- for _, list := range lists {
- for _, s := range list {
- w.Bytes(s.P)
- }
- }
-
- // Pcdata
- h.Offsets[goobj.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)
- }
- }
- }
-
- // Blocks used only by tools (objdump, nm).
-
- // Referenced symbol names from other packages
- h.Offsets[goobj.BlkRefName] = w.Offset()
- w.refNames()
-
- h.Offsets[goobj.BlkEnd] = w.Offset()
-
- // 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 {
- *goobj.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) {
- // TODO: this includes references of indexed symbols from other packages,
- // for which the linker doesn't need the name. Consider moving them to
- // a separate block (for tools only).
- if w.pkgpath != "" {
- s.Name = strings.Replace(s.Name, "\"\".", w.pkgpath+".", -1)
- }
- // Don't put names of builtins into the string table (to save
- // space).
- if s.PkgIdx == goobj.PkgIdxBuiltin {
- return
- }
- w.AddString(s.Name)
- })
-
- // All filenames are in the postable.
- for _, f := range w.ctxt.PosTable.FileTable() {
- w.AddString(filepath.ToSlash(f))
- }
-}
-
-func (w *writer) Sym(s *LSym) {
- abi := uint16(s.ABI())
- if s.Static() {
- abi = goobj.SymABIstatic
- }
- flag := uint8(0)
- if s.DuplicateOK() {
- flag |= goobj.SymFlagDupok
- }
- if s.Local() {
- flag |= goobj.SymFlagLocal
- }
- if s.MakeTypelink() {
- flag |= goobj.SymFlagTypelink
- }
- if s.Leaf() {
- flag |= goobj.SymFlagLeaf
- }
- if s.NoSplit() {
- flag |= goobj.SymFlagNoSplit
- }
- if s.ReflectMethod() {
- flag |= goobj.SymFlagReflectMethod
- }
- if s.TopFrame() {
- flag |= goobj.SymFlagTopFrame
- }
- if strings.HasPrefix(s.Name, "type.") && s.Name[5] != '.' && s.Type == objabi.SRODATA {
- flag |= goobj.SymFlagGoType
- }
- flag2 := uint8(0)
- if s.UsedInIface() {
- flag2 |= goobj.SymFlagUsedInIface
- }
- if strings.HasPrefix(s.Name, "go.itab.") && s.Type == objabi.SRODATA {
- flag2 |= goobj.SymFlagItab
- }
- name := s.Name
- if strings.HasPrefix(name, "gofile..") {
- name = filepath.ToSlash(name)
- }
- var align uint32
- if s.Func != nil {
- align = uint32(s.Func.Align)
- }
- if s.ContentAddressable() {
- // We generally assume data symbols are natually aligned,
- // except for strings. If we dedup a string symbol and a
- // non-string symbol with the same content, we should keep
- // the largest alignment.
- // TODO: maybe the compiler could set the alignment for all
- // data symbols more carefully.
- if s.Size != 0 && !strings.HasPrefix(s.Name, "go.string.") {
- switch {
- case w.ctxt.Arch.PtrSize == 8 && s.Size%8 == 0:
- align = 8
- case s.Size%4 == 0:
- align = 4
- case s.Size%2 == 0:
- align = 2
- }
- // don't bother setting align to 1.
- }
- }
- var o goobj.Sym
- o.SetName(name, w.Writer)
- o.SetABI(abi)
- o.SetType(uint8(s.Type))
- o.SetFlag(flag)
- o.SetFlag2(flag2)
- o.SetSiz(uint32(s.Size))
- o.SetAlign(align)
- o.Write(w.Writer)
-}
-
-func (w *writer) Hash64(s *LSym) {
- if !s.ContentAddressable() || len(s.R) != 0 {
- panic("Hash of non-content-addresable symbol")
- }
- b := contentHash64(s)
- w.Bytes(b[:])
-}
-
-func (w *writer) Hash(s *LSym) {
- if !s.ContentAddressable() {
- panic("Hash of non-content-addresable symbol")
- }
- b := w.contentHash(s)
- w.Bytes(b[:])
-}
-
-func contentHash64(s *LSym) goobj.Hash64Type {
- var b goobj.Hash64Type
- copy(b[:], s.P)
- return b
-}
-
-// Compute the content hash for a content-addressable symbol.
-// We build a content hash based on its content and relocations.
-// Depending on the category of the referenced symbol, we choose
-// different hash algorithms such that the hash is globally
-// consistent.
-// - For referenced content-addressable symbol, its content hash
-// is globally consistent.
-// - For package symbol and builtin symbol, its local index is
-// globally consistent.
-// - For non-package symbol, its fully-expanded name is globally
-// consistent. For now, we require we know the current package
-// path so we can always expand symbol names. (Otherwise,
-// symbols with relocations are not considered hashable.)
-//
-// For now, we assume there is no circular dependencies among
-// hashed symbols.
-func (w *writer) contentHash(s *LSym) goobj.HashType {
- h := sha1.New()
- // The compiler trims trailing zeros _sometimes_. We just do
- // it always.
- h.Write(bytes.TrimRight(s.P, "\x00"))
- var tmp [14]byte
- for i := range s.R {
- r := &s.R[i]
- binary.LittleEndian.PutUint32(tmp[:4], uint32(r.Off))
- tmp[4] = r.Siz
- tmp[5] = uint8(r.Type)
- binary.LittleEndian.PutUint64(tmp[6:14], uint64(r.Add))
- h.Write(tmp[:])
- rs := r.Sym
- switch rs.PkgIdx {
- case goobj.PkgIdxHashed64:
- h.Write([]byte{0})
- t := contentHash64(rs)
- h.Write(t[:])
- case goobj.PkgIdxHashed:
- h.Write([]byte{1})
- t := w.contentHash(rs)
- h.Write(t[:])
- case goobj.PkgIdxNone:
- h.Write([]byte{2})
- io.WriteString(h, rs.Name) // name is already expanded at this point
- case goobj.PkgIdxBuiltin:
- h.Write([]byte{3})
- binary.LittleEndian.PutUint32(tmp[:4], uint32(rs.SymIdx))
- h.Write(tmp[:4])
- case goobj.PkgIdxSelf:
- io.WriteString(h, w.pkgpath)
- binary.LittleEndian.PutUint32(tmp[:4], uint32(rs.SymIdx))
- h.Write(tmp[:4])
- default:
- io.WriteString(h, rs.Pkg)
- binary.LittleEndian.PutUint32(tmp[:4], uint32(rs.SymIdx))
- h.Write(tmp[:4])
- }
- }
- var b goobj.HashType
- copy(b[:], h.Sum(nil))
- return b
-}
-
-func makeSymRef(s *LSym) goobj.SymRef {
- if s == nil {
- return goobj.SymRef{}
- }
- if s.PkgIdx == 0 || !s.Indexed() {
- fmt.Printf("unindexed symbol reference: %v\n", s)
- panic("unindexed symbol reference")
- }
- return goobj.SymRef{PkgIdx: uint32(s.PkgIdx), SymIdx: uint32(s.SymIdx)}
-}
-
-func (w *writer) Reloc(r *Reloc) {
- var o goobj.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 goobj.Aux
- o.SetType(typ)
- o.SetSym(makeSymRef(rs))
- o.Write(w.Writer)
-}
-
-func (w *writer) Aux(s *LSym) {
- if s.Gotype != nil {
- w.aux1(goobj.AuxGotype, s.Gotype)
- }
- if s.Func != nil {
- w.aux1(goobj.AuxFuncInfo, s.Func.FuncInfoSym)
-
- for _, d := range s.Func.Pcln.Funcdata {
- w.aux1(goobj.AuxFuncdata, d)
- }
-
- if s.Func.dwarfInfoSym != nil && s.Func.dwarfInfoSym.Size != 0 {
- w.aux1(goobj.AuxDwarfInfo, s.Func.dwarfInfoSym)
- }
- if s.Func.dwarfLocSym != nil && s.Func.dwarfLocSym.Size != 0 {
- w.aux1(goobj.AuxDwarfLoc, s.Func.dwarfLocSym)
- }
- if s.Func.dwarfRangesSym != nil && s.Func.dwarfRangesSym.Size != 0 {
- w.aux1(goobj.AuxDwarfRanges, s.Func.dwarfRangesSym)
- }
- if s.Func.dwarfDebugLinesSym != nil && s.Func.dwarfDebugLinesSym.Size != 0 {
- w.aux1(goobj.AuxDwarfLines, s.Func.dwarfDebugLinesSym)
- }
- }
-}
-
-// Emits flags of referenced indexed symbols.
-func (w *writer) refFlags() {
- seen := make(map[*LSym]bool)
- w.ctxt.traverseSyms(traverseRefs, func(rs *LSym) { // only traverse refs, not auxs, as tools don't need auxs
- switch rs.PkgIdx {
- case goobj.PkgIdxNone, goobj.PkgIdxHashed64, goobj.PkgIdxHashed, goobj.PkgIdxBuiltin, goobj.PkgIdxSelf: // not an external indexed reference
- return
- case goobj.PkgIdxInvalid:
- panic("unindexed symbol reference")
- }
- if seen[rs] {
- return
- }
- seen[rs] = true
- symref := makeSymRef(rs)
- flag2 := uint8(0)
- if rs.UsedInIface() {
- flag2 |= goobj.SymFlagUsedInIface
- }
- if flag2 == 0 {
- return // no need to write zero flags
- }
- var o goobj.RefFlags
- o.SetSym(symref)
- o.SetFlag2(flag2)
- o.Write(w.Writer)
- })
-}
-
-// Emits names of referenced indexed symbols, used by tools (objdump, nm)
-// only.
-func (w *writer) refNames() {
- seen := make(map[*LSym]bool)
- w.ctxt.traverseSyms(traverseRefs, func(rs *LSym) { // only traverse refs, not auxs, as tools don't need auxs
- switch rs.PkgIdx {
- case goobj.PkgIdxNone, goobj.PkgIdxHashed64, goobj.PkgIdxHashed, goobj.PkgIdxBuiltin, goobj.PkgIdxSelf: // not an external indexed reference
- return
- case goobj.PkgIdxInvalid:
- panic("unindexed symbol reference")
- }
- if seen[rs] {
- return
- }
- seen[rs] = true
- symref := makeSymRef(rs)
- var o goobj.RefName
- o.SetSym(symref)
- o.SetName(rs.Name, w.Writer)
- o.Write(w.Writer)
- })
- // TODO: output in sorted order?
- // Currently tools (cmd/internal/goobj package) doesn't use mmap,
- // and it just read it into a map in memory upfront. If it uses
- // mmap, if the output is sorted, it probably could avoid reading
- // into memory and just do lookups in the mmap'd object file.
-}
-
-// 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 := goobj.FuncInfo{
- Args: uint32(s.Func.Args),
- Locals: uint32(s.Func.Locals),
- FuncID: objabi.FuncID(s.Func.FuncID),
- }
- 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)
- }
- i := 0
- o.File = make([]goobj.CUFileIndex, len(pc.UsedFiles))
- for f := range pc.UsedFiles {
- o.File[i] = f
- i++
- }
- sort.Slice(o.File, func(i, j int) bool { return o.File[i] < o.File[j] })
- o.InlTree = make([]goobj.InlTreeNode, len(pc.InlTree.nodes))
- for i, inl := range pc.InlTree.nodes {
- f, l := getFileIndexAndLine(ctxt, inl.Pos)
- o.InlTree[i] = goobj.InlTreeNode{
- Parent: int32(inl.Parent),
- File: goobj.CUFileIndex(f),
- 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: goobj.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 = goobj.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)
- }
- }
-}