summaryrefslogtreecommitdiffstats
path: root/src/cmd/internal/obj/dwarf.go
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:23:18 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-16 19:23:18 +0000
commit43a123c1ae6613b3efeed291fa552ecd909d3acf (patch)
treefd92518b7024bc74031f78a1cf9e454b65e73665 /src/cmd/internal/obj/dwarf.go
parentInitial commit. (diff)
downloadgolang-1.20-upstream.tar.xz
golang-1.20-upstream.zip
Adding upstream version 1.20.14.upstream/1.20.14upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/cmd/internal/obj/dwarf.go')
-rw-r--r--src/cmd/internal/obj/dwarf.go709
1 files changed, 709 insertions, 0 deletions
diff --git a/src/cmd/internal/obj/dwarf.go b/src/cmd/internal/obj/dwarf.go
new file mode 100644
index 0000000..a9c13fd
--- /dev/null
+++ b/src/cmd/internal/obj/dwarf.go
@@ -0,0 +1,709 @@
+// 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.
+
+// Writes dwarf information to object files.
+
+package obj
+
+import (
+ "cmd/internal/dwarf"
+ "cmd/internal/objabi"
+ "cmd/internal/src"
+ "fmt"
+ "sort"
+ "sync"
+)
+
+// Generate a sequence of opcodes that is as short as possible.
+// See section 6.2.5
+const (
+ LINE_BASE = -4
+ LINE_RANGE = 10
+ PC_RANGE = (255 - OPCODE_BASE) / LINE_RANGE
+ OPCODE_BASE = 11
+)
+
+// generateDebugLinesSymbol fills the debug lines symbol of a given function.
+//
+// It's worth noting that this function doesn't generate the full debug_lines
+// DWARF section, saving that for the linker. This function just generates the
+// state machine part of debug_lines. The full table is generated by the
+// linker. Also, we use the file numbers from the full package (not just the
+// function in question) when generating the state machine. We do this so we
+// don't have to do a fixup on the indices when writing the full section.
+func (ctxt *Link) generateDebugLinesSymbol(s, lines *LSym) {
+ dctxt := dwCtxt{ctxt}
+
+ // Emit a LNE_set_address extended opcode, so as to establish the
+ // starting text address of this function.
+ dctxt.AddUint8(lines, 0)
+ dwarf.Uleb128put(dctxt, lines, 1+int64(ctxt.Arch.PtrSize))
+ dctxt.AddUint8(lines, dwarf.DW_LNE_set_address)
+ dctxt.AddAddress(lines, s, 0)
+
+ // Set up the debug_lines state machine to the default values
+ // we expect at the start of a new sequence.
+ stmt := true
+ line := int64(1)
+ pc := s.Func().Text.Pc
+ var lastpc int64 // last PC written to line table, not last PC in func
+ name := ""
+ prologue, wrotePrologue := false, false
+ // Walk the progs, generating the DWARF table.
+ for p := s.Func().Text; p != nil; p = p.Link {
+ prologue = prologue || (p.Pos.Xlogue() == src.PosPrologueEnd)
+ // If we're not at a real instruction, keep looping!
+ if p.Pos.Line() == 0 || (p.Link != nil && p.Link.Pc == p.Pc) {
+ continue
+ }
+ newStmt := p.Pos.IsStmt() != src.PosNotStmt
+ newName, newLine := ctxt.getFileSymbolAndLine(p.Pos)
+
+ // Output debug info.
+ wrote := false
+ if name != newName {
+ newFile := ctxt.PosTable.FileIndex(newName) + 1 // 1 indexing for the table.
+ dctxt.AddUint8(lines, dwarf.DW_LNS_set_file)
+ dwarf.Uleb128put(dctxt, lines, int64(newFile))
+ name = newName
+ wrote = true
+ }
+ if prologue && !wrotePrologue {
+ dctxt.AddUint8(lines, uint8(dwarf.DW_LNS_set_prologue_end))
+ wrotePrologue = true
+ wrote = true
+ }
+ if stmt != newStmt {
+ dctxt.AddUint8(lines, uint8(dwarf.DW_LNS_negate_stmt))
+ stmt = newStmt
+ wrote = true
+ }
+
+ if line != int64(newLine) || wrote {
+ pcdelta := p.Pc - pc
+ lastpc = p.Pc
+ putpclcdelta(ctxt, dctxt, lines, uint64(pcdelta), int64(newLine)-line)
+ line, pc = int64(newLine), p.Pc
+ }
+ }
+
+ // Because these symbols will be concatenated together by the
+ // linker, we need to reset the state machine that controls the
+ // debug symbols. Do this using an end-of-sequence operator.
+ //
+ // Note: at one point in time, Delve did not support multiple end
+ // sequence ops within a compilation unit (bug for this:
+ // https://github.com/go-delve/delve/issues/1694), however the bug
+ // has since been fixed (Oct 2019).
+ //
+ // Issue 38192: the DWARF standard specifies that when you issue
+ // an end-sequence op, the PC value should be one past the last
+ // text address in the translation unit, so apply a delta to the
+ // text address before the end sequence op. If this isn't done,
+ // GDB will assign a line number of zero the last row in the line
+ // table, which we don't want.
+ lastlen := uint64(s.Size - (lastpc - s.Func().Text.Pc))
+ dctxt.AddUint8(lines, dwarf.DW_LNS_advance_pc)
+ dwarf.Uleb128put(dctxt, lines, int64(lastlen))
+ dctxt.AddUint8(lines, 0) // start extended opcode
+ dwarf.Uleb128put(dctxt, lines, 1)
+ dctxt.AddUint8(lines, dwarf.DW_LNE_end_sequence)
+}
+
+func putpclcdelta(linkctxt *Link, dctxt dwCtxt, s *LSym, deltaPC uint64, deltaLC int64) {
+ // Choose a special opcode that minimizes the number of bytes needed to
+ // encode the remaining PC delta and LC delta.
+ var opcode int64
+ if deltaLC < LINE_BASE {
+ if deltaPC >= PC_RANGE {
+ opcode = OPCODE_BASE + (LINE_RANGE * PC_RANGE)
+ } else {
+ opcode = OPCODE_BASE + (LINE_RANGE * int64(deltaPC))
+ }
+ } else if deltaLC < LINE_BASE+LINE_RANGE {
+ if deltaPC >= PC_RANGE {
+ opcode = OPCODE_BASE + (deltaLC - LINE_BASE) + (LINE_RANGE * PC_RANGE)
+ if opcode > 255 {
+ opcode -= LINE_RANGE
+ }
+ } else {
+ opcode = OPCODE_BASE + (deltaLC - LINE_BASE) + (LINE_RANGE * int64(deltaPC))
+ }
+ } else {
+ if deltaPC <= PC_RANGE {
+ opcode = OPCODE_BASE + (LINE_RANGE - 1) + (LINE_RANGE * int64(deltaPC))
+ if opcode > 255 {
+ opcode = 255
+ }
+ } else {
+ // Use opcode 249 (pc+=23, lc+=5) or 255 (pc+=24, lc+=1).
+ //
+ // Let x=deltaPC-PC_RANGE. If we use opcode 255, x will be the remaining
+ // deltaPC that we need to encode separately before emitting 255. If we
+ // use opcode 249, we will need to encode x+1. If x+1 takes one more
+ // byte to encode than x, then we use opcode 255.
+ //
+ // In all other cases x and x+1 take the same number of bytes to encode,
+ // so we use opcode 249, which may save us a byte in encoding deltaLC,
+ // for similar reasons.
+ switch deltaPC - PC_RANGE {
+ // PC_RANGE is the largest deltaPC we can encode in one byte, using
+ // DW_LNS_const_add_pc.
+ //
+ // (1<<16)-1 is the largest deltaPC we can encode in three bytes, using
+ // DW_LNS_fixed_advance_pc.
+ //
+ // (1<<(7n))-1 is the largest deltaPC we can encode in n+1 bytes for
+ // n=1,3,4,5,..., using DW_LNS_advance_pc.
+ case PC_RANGE, (1 << 7) - 1, (1 << 16) - 1, (1 << 21) - 1, (1 << 28) - 1,
+ (1 << 35) - 1, (1 << 42) - 1, (1 << 49) - 1, (1 << 56) - 1, (1 << 63) - 1:
+ opcode = 255
+ default:
+ opcode = OPCODE_BASE + LINE_RANGE*PC_RANGE - 1 // 249
+ }
+ }
+ }
+ if opcode < OPCODE_BASE || opcode > 255 {
+ panic(fmt.Sprintf("produced invalid special opcode %d", opcode))
+ }
+
+ // Subtract from deltaPC and deltaLC the amounts that the opcode will add.
+ deltaPC -= uint64((opcode - OPCODE_BASE) / LINE_RANGE)
+ deltaLC -= (opcode-OPCODE_BASE)%LINE_RANGE + LINE_BASE
+
+ // Encode deltaPC.
+ if deltaPC != 0 {
+ if deltaPC <= PC_RANGE {
+ // Adjust the opcode so that we can use the 1-byte DW_LNS_const_add_pc
+ // instruction.
+ opcode -= LINE_RANGE * int64(PC_RANGE-deltaPC)
+ if opcode < OPCODE_BASE {
+ panic(fmt.Sprintf("produced invalid special opcode %d", opcode))
+ }
+ dctxt.AddUint8(s, dwarf.DW_LNS_const_add_pc)
+ } else if (1<<14) <= deltaPC && deltaPC < (1<<16) {
+ dctxt.AddUint8(s, dwarf.DW_LNS_fixed_advance_pc)
+ dctxt.AddUint16(s, uint16(deltaPC))
+ } else {
+ dctxt.AddUint8(s, dwarf.DW_LNS_advance_pc)
+ dwarf.Uleb128put(dctxt, s, int64(deltaPC))
+ }
+ }
+
+ // Encode deltaLC.
+ if deltaLC != 0 {
+ dctxt.AddUint8(s, dwarf.DW_LNS_advance_line)
+ dwarf.Sleb128put(dctxt, s, deltaLC)
+ }
+
+ // 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)
+ }
+ fn := s.Func()
+ if fn.dwarfInfoSym == nil {
+ fn.dwarfInfoSym = &LSym{
+ Type: objabi.SDWARFFCN,
+ }
+ if ctxt.Flag_locationlists {
+ fn.dwarfLocSym = &LSym{
+ Type: objabi.SDWARFLOC,
+ }
+ }
+ fn.dwarfRangesSym = &LSym{
+ Type: objabi.SDWARFRANGE,
+ }
+ fn.dwarfDebugLinesSym = &LSym{
+ Type: objabi.SDWARFLINES,
+ }
+ if s.WasInlined() {
+ fn.dwarfAbsFnSym = ctxt.DwFixups.AbsFuncDwarfSym(s)
+ }
+ }
+ return fn.dwarfInfoSym, fn.dwarfLocSym, fn.dwarfRangesSym, fn.dwarfAbsFnSym, fn.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, _ := ctxt.getFileSymbolAndLine(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, s.Wrapper())
+ } else {
+ err = dwarf.PutDefaultFunc(dwctxt, fnstate, s.Wrapper())
+ }
+ 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)
+}
+
+// DwarfGlobal creates a link symbol containing a DWARF entry for
+// a global variable.
+func (ctxt *Link) DwarfGlobal(myimportpath, typename string, varSym *LSym) {
+ if myimportpath == "" || varSym.Local() {
+ return
+ }
+ varname := varSym.Name
+ dieSymName := dwarf.InfoPrefix + varname
+ dieSym := ctxt.LookupInit(dieSymName, func(s *LSym) {
+ s.Type = objabi.SDWARFVAR
+ s.Set(AttrDuplicateOK, true) // needed for shared linkage
+ ctxt.Data = append(ctxt.Data, s)
+ })
+ typeSym := ctxt.Lookup(dwarf.InfoPrefix + typename)
+ dwarf.PutGlobal(dwCtxt{ctxt}, dieSym, typeSym, varSym, varname)
+}
+
+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.NewFuncInfo()
+ }
+ 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 fn := s.Func(); fn != nil && fn.dwarfAbsFnSym == nil {
+ fn.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] }