diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:23:18 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:23:18 +0000 |
commit | 43a123c1ae6613b3efeed291fa552ecd909d3acf (patch) | |
tree | fd92518b7024bc74031f78a1cf9e454b65e73665 /src/cmd/internal/obj/dwarf.go | |
parent | Initial commit. (diff) | |
download | golang-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.go | 709 |
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] } |