Source file src/cmd/internal/obj/dwarf.go

     1  // Copyright 2019 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Writes dwarf information to object files.
     6  
     7  package obj
     8  
     9  import (
    10  	"cmd/internal/dwarf"
    11  	"cmd/internal/objabi"
    12  	"cmd/internal/src"
    13  	"fmt"
    14  	"slices"
    15  	"strings"
    16  	"sync"
    17  )
    18  
    19  // Generate a sequence of opcodes that is as short as possible.
    20  // See section 6.2.5
    21  const (
    22  	LINE_BASE   = -4
    23  	LINE_RANGE  = 10
    24  	PC_RANGE    = (255 - OPCODE_BASE) / LINE_RANGE
    25  	OPCODE_BASE = 11
    26  )
    27  
    28  // generateDebugLinesSymbol fills the debug lines symbol of a given function.
    29  //
    30  // It's worth noting that this function doesn't generate the full debug_lines
    31  // DWARF section, saving that for the linker. This function just generates the
    32  // state machine part of debug_lines. The full table is generated by the
    33  // linker.  Also, we use the file numbers from the full package (not just the
    34  // function in question) when generating the state machine. We do this so we
    35  // don't have to do a fixup on the indices when writing the full section.
    36  func (ctxt *Link) generateDebugLinesSymbol(s, lines *LSym) {
    37  	dctxt := dwCtxt{ctxt}
    38  
    39  	// Emit a LNE_set_address extended opcode, so as to establish the
    40  	// starting text address of this function.
    41  	dctxt.AddUint8(lines, 0)
    42  	dwarf.Uleb128put(dctxt, lines, 1+int64(ctxt.Arch.PtrSize))
    43  	dctxt.AddUint8(lines, dwarf.DW_LNE_set_address)
    44  	dctxt.AddAddress(lines, s, 0)
    45  
    46  	// Set up the debug_lines state machine to the default values
    47  	// we expect at the start of a new sequence.
    48  	stmt := true
    49  	line := int64(1)
    50  	pc := s.Func().Text.Pc
    51  	var lastpc int64 // last PC written to line table, not last PC in func
    52  	fileIndex := 1
    53  	prologue, wrotePrologue := false, false
    54  	// Walk the progs, generating the DWARF table.
    55  	for p := s.Func().Text; p != nil; p = p.Link {
    56  		prologue = prologue || (p.Pos.Xlogue() == src.PosPrologueEnd)
    57  		// If we're not at a real instruction, keep looping!
    58  		if p.Pos.Line() == 0 || (p.Link != nil && p.Link.Pc == p.Pc) {
    59  			continue
    60  		}
    61  		newStmt := p.Pos.IsStmt() != src.PosNotStmt
    62  		newFileIndex, newLine := ctxt.getFileIndexAndLine(p.Pos)
    63  		newFileIndex++ // 1 indexing for the table
    64  
    65  		// Output debug info.
    66  		wrote := false
    67  		if newFileIndex != fileIndex {
    68  			dctxt.AddUint8(lines, dwarf.DW_LNS_set_file)
    69  			dwarf.Uleb128put(dctxt, lines, int64(newFileIndex))
    70  			fileIndex = newFileIndex
    71  			wrote = true
    72  		}
    73  		if prologue && !wrotePrologue {
    74  			dctxt.AddUint8(lines, uint8(dwarf.DW_LNS_set_prologue_end))
    75  			wrotePrologue = true
    76  			wrote = true
    77  		}
    78  		if stmt != newStmt {
    79  			dctxt.AddUint8(lines, uint8(dwarf.DW_LNS_negate_stmt))
    80  			stmt = newStmt
    81  			wrote = true
    82  		}
    83  
    84  		if line != int64(newLine) || wrote {
    85  			pcdelta := p.Pc - pc
    86  			lastpc = p.Pc
    87  			putpclcdelta(ctxt, dctxt, lines, uint64(pcdelta), int64(newLine)-line)
    88  			line, pc = int64(newLine), p.Pc
    89  		}
    90  	}
    91  
    92  	// Because these symbols will be concatenated together by the
    93  	// linker, we need to reset the state machine that controls the
    94  	// debug symbols. Do this using an end-of-sequence operator.
    95  	//
    96  	// Note: at one point in time, Delve did not support multiple end
    97  	// sequence ops within a compilation unit (bug for this:
    98  	// https://github.com/go-delve/delve/issues/1694), however the bug
    99  	// has since been fixed (Oct 2019).
   100  	//
   101  	// Issue 38192: the DWARF standard specifies that when you issue
   102  	// an end-sequence op, the PC value should be one past the last
   103  	// text address in the translation unit, so apply a delta to the
   104  	// text address before the end sequence op. If this isn't done,
   105  	// GDB will assign a line number of zero the last row in the line
   106  	// table, which we don't want.
   107  	lastlen := uint64(s.Size - (lastpc - s.Func().Text.Pc))
   108  	dctxt.AddUint8(lines, dwarf.DW_LNS_advance_pc)
   109  	dwarf.Uleb128put(dctxt, lines, int64(lastlen))
   110  	dctxt.AddUint8(lines, 0) // start extended opcode
   111  	dwarf.Uleb128put(dctxt, lines, 1)
   112  	dctxt.AddUint8(lines, dwarf.DW_LNE_end_sequence)
   113  }
   114  
   115  func putpclcdelta(linkctxt *Link, dctxt dwCtxt, s *LSym, deltaPC uint64, deltaLC int64) {
   116  	// Choose a special opcode that minimizes the number of bytes needed to
   117  	// encode the remaining PC delta and LC delta.
   118  	var opcode int64
   119  	if deltaLC < LINE_BASE {
   120  		if deltaPC >= PC_RANGE {
   121  			opcode = OPCODE_BASE + (LINE_RANGE * PC_RANGE)
   122  		} else {
   123  			opcode = OPCODE_BASE + (LINE_RANGE * int64(deltaPC))
   124  		}
   125  	} else if deltaLC < LINE_BASE+LINE_RANGE {
   126  		if deltaPC >= PC_RANGE {
   127  			opcode = OPCODE_BASE + (deltaLC - LINE_BASE) + (LINE_RANGE * PC_RANGE)
   128  			if opcode > 255 {
   129  				opcode -= LINE_RANGE
   130  			}
   131  		} else {
   132  			opcode = OPCODE_BASE + (deltaLC - LINE_BASE) + (LINE_RANGE * int64(deltaPC))
   133  		}
   134  	} else {
   135  		if deltaPC <= PC_RANGE {
   136  			opcode = OPCODE_BASE + (LINE_RANGE - 1) + (LINE_RANGE * int64(deltaPC))
   137  			if opcode > 255 {
   138  				opcode = 255
   139  			}
   140  		} else {
   141  			// Use opcode 249 (pc+=23, lc+=5) or 255 (pc+=24, lc+=1).
   142  			//
   143  			// Let x=deltaPC-PC_RANGE.  If we use opcode 255, x will be the remaining
   144  			// deltaPC that we need to encode separately before emitting 255.  If we
   145  			// use opcode 249, we will need to encode x+1.  If x+1 takes one more
   146  			// byte to encode than x, then we use opcode 255.
   147  			//
   148  			// In all other cases x and x+1 take the same number of bytes to encode,
   149  			// so we use opcode 249, which may save us a byte in encoding deltaLC,
   150  			// for similar reasons.
   151  			switch deltaPC - PC_RANGE {
   152  			// PC_RANGE is the largest deltaPC we can encode in one byte, using
   153  			// DW_LNS_const_add_pc.
   154  			//
   155  			// (1<<16)-1 is the largest deltaPC we can encode in three bytes, using
   156  			// DW_LNS_fixed_advance_pc.
   157  			//
   158  			// (1<<(7n))-1 is the largest deltaPC we can encode in n+1 bytes for
   159  			// n=1,3,4,5,..., using DW_LNS_advance_pc.
   160  			case PC_RANGE, (1 << 7) - 1, (1 << 16) - 1, (1 << 21) - 1, (1 << 28) - 1,
   161  				(1 << 35) - 1, (1 << 42) - 1, (1 << 49) - 1, (1 << 56) - 1, (1 << 63) - 1:
   162  				opcode = 255
   163  			default:
   164  				opcode = OPCODE_BASE + LINE_RANGE*PC_RANGE - 1 // 249
   165  			}
   166  		}
   167  	}
   168  	if opcode < OPCODE_BASE || opcode > 255 {
   169  		panic(fmt.Sprintf("produced invalid special opcode %d", opcode))
   170  	}
   171  
   172  	// Subtract from deltaPC and deltaLC the amounts that the opcode will add.
   173  	deltaPC -= uint64((opcode - OPCODE_BASE) / LINE_RANGE)
   174  	deltaLC -= (opcode-OPCODE_BASE)%LINE_RANGE + LINE_BASE
   175  
   176  	// Encode deltaPC.
   177  	if deltaPC != 0 {
   178  		if deltaPC <= PC_RANGE {
   179  			// Adjust the opcode so that we can use the 1-byte DW_LNS_const_add_pc
   180  			// instruction.
   181  			opcode -= LINE_RANGE * int64(PC_RANGE-deltaPC)
   182  			if opcode < OPCODE_BASE {
   183  				panic(fmt.Sprintf("produced invalid special opcode %d", opcode))
   184  			}
   185  			dctxt.AddUint8(s, dwarf.DW_LNS_const_add_pc)
   186  		} else if (1<<14) <= deltaPC && deltaPC < (1<<16) {
   187  			dctxt.AddUint8(s, dwarf.DW_LNS_fixed_advance_pc)
   188  			dctxt.AddUint16(s, uint16(deltaPC))
   189  		} else {
   190  			dctxt.AddUint8(s, dwarf.DW_LNS_advance_pc)
   191  			dwarf.Uleb128put(dctxt, s, int64(deltaPC))
   192  		}
   193  	}
   194  
   195  	// Encode deltaLC.
   196  	if deltaLC != 0 {
   197  		dctxt.AddUint8(s, dwarf.DW_LNS_advance_line)
   198  		dwarf.Sleb128put(dctxt, s, deltaLC)
   199  	}
   200  
   201  	// Output the special opcode.
   202  	dctxt.AddUint8(s, uint8(opcode))
   203  }
   204  
   205  // implement dwarf.Context
   206  type dwCtxt struct{ *Link }
   207  
   208  func (c dwCtxt) PtrSize() int {
   209  	return c.Arch.PtrSize
   210  }
   211  func (c dwCtxt) Size(s dwarf.Sym) int64 {
   212  	return s.(*LSym).Size
   213  }
   214  func (c dwCtxt) AddInt(s dwarf.Sym, size int, i int64) {
   215  	ls := s.(*LSym)
   216  	ls.WriteInt(c.Link, ls.Size, size, i)
   217  }
   218  func (c dwCtxt) AddUint16(s dwarf.Sym, i uint16) {
   219  	c.AddInt(s, 2, int64(i))
   220  }
   221  func (c dwCtxt) AddUint8(s dwarf.Sym, i uint8) {
   222  	b := []byte{byte(i)}
   223  	c.AddBytes(s, b)
   224  }
   225  func (c dwCtxt) AddBytes(s dwarf.Sym, b []byte) {
   226  	ls := s.(*LSym)
   227  	ls.WriteBytes(c.Link, ls.Size, b)
   228  }
   229  func (c dwCtxt) AddString(s dwarf.Sym, v string) {
   230  	ls := s.(*LSym)
   231  	ls.WriteString(c.Link, ls.Size, len(v), v)
   232  	ls.WriteInt(c.Link, ls.Size, 1, 0)
   233  }
   234  func (c dwCtxt) AddAddress(s dwarf.Sym, data interface{}, value int64) {
   235  	ls := s.(*LSym)
   236  	size := c.PtrSize()
   237  	if data != nil {
   238  		rsym := data.(*LSym)
   239  		ls.WriteAddr(c.Link, ls.Size, size, rsym, value)
   240  	} else {
   241  		ls.WriteInt(c.Link, ls.Size, size, value)
   242  	}
   243  }
   244  func (c dwCtxt) AddCURelativeAddress(s dwarf.Sym, data interface{}, value int64) {
   245  	ls := s.(*LSym)
   246  	rsym := data.(*LSym)
   247  	ls.WriteCURelativeAddr(c.Link, ls.Size, rsym, value)
   248  }
   249  func (c dwCtxt) AddSectionOffset(s dwarf.Sym, size int, t interface{}, ofs int64) {
   250  	panic("should be used only in the linker")
   251  }
   252  func (c dwCtxt) AddDWARFAddrSectionOffset(s dwarf.Sym, t interface{}, ofs int64) {
   253  	size := 4
   254  	if isDwarf64(c.Link) {
   255  		size = 8
   256  	}
   257  
   258  	ls := s.(*LSym)
   259  	rsym := t.(*LSym)
   260  	ls.WriteAddr(c.Link, ls.Size, size, rsym, ofs)
   261  	r := &ls.R[len(ls.R)-1]
   262  	r.Type = objabi.R_DWARFSECREF
   263  }
   264  
   265  func (c dwCtxt) CurrentOffset(s dwarf.Sym) int64 {
   266  	ls := s.(*LSym)
   267  	return ls.Size
   268  }
   269  
   270  // Here "from" is a symbol corresponding to an inlined or concrete
   271  // function, "to" is the symbol for the corresponding abstract
   272  // function, and "dclIdx" is the index of the symbol of interest with
   273  // respect to the Dcl slice of the original pre-optimization version
   274  // of the inlined function.
   275  func (c dwCtxt) RecordDclReference(from dwarf.Sym, to dwarf.Sym, dclIdx int, inlIndex int) {
   276  	ls := from.(*LSym)
   277  	tls := to.(*LSym)
   278  	ridx := len(ls.R) - 1
   279  	c.Link.DwFixups.ReferenceChildDIE(ls, ridx, tls, dclIdx, inlIndex)
   280  }
   281  
   282  func (c dwCtxt) RecordChildDieOffsets(s dwarf.Sym, vars []*dwarf.Var, offsets []int32) {
   283  	ls := s.(*LSym)
   284  	c.Link.DwFixups.RegisterChildDIEOffsets(ls, vars, offsets)
   285  }
   286  
   287  func (c dwCtxt) Logf(format string, args ...interface{}) {
   288  	c.Link.Logf(format, args...)
   289  }
   290  
   291  func (c dwCtxt) AddIndirectTextRef(s dwarf.Sym, t interface{}) {
   292  	ls := s.(*LSym)
   293  	tsym := t.(*LSym)
   294  	// Note the doubling below -- DwTextCount is an estimate and
   295  	// usually a little short due to additional wrapper functions and
   296  	// such; by using c.DwTextCount*2 as the limit we'll ensure that
   297  	// we don't run out of space.
   298  	ls.WriteDwTxtAddrx(c.Link, ls.Size, tsym, c.DwTextCount*2)
   299  }
   300  
   301  func isDwarf64(ctxt *Link) bool {
   302  	return ctxt.Headtype == objabi.Haix
   303  }
   304  
   305  func (ctxt *Link) dwarfSym(s *LSym) (dwarfInfoSym, dwarfLocSym, dwarfRangesSym, dwarfAbsFnSym, dwarfDebugLines *LSym) {
   306  	if !s.Type.IsText() {
   307  		ctxt.Diag("dwarfSym of non-TEXT %v", s)
   308  	}
   309  	fn := s.Func()
   310  	if fn.dwarfInfoSym == nil {
   311  		fn.dwarfInfoSym = &LSym{
   312  			Type: objabi.SDWARFFCN,
   313  		}
   314  		if ctxt.Flag_locationlists {
   315  			fn.dwarfLocSym = &LSym{
   316  				Type: objabi.SDWARFLOC,
   317  			}
   318  		}
   319  		fn.dwarfRangesSym = &LSym{
   320  			Type: objabi.SDWARFRANGE,
   321  		}
   322  		fn.dwarfDebugLinesSym = &LSym{
   323  			Type: objabi.SDWARFLINES,
   324  		}
   325  		if s.WasInlined() {
   326  			fn.dwarfAbsFnSym = ctxt.DwFixups.AbsFuncDwarfSym(s)
   327  		}
   328  	}
   329  	return fn.dwarfInfoSym, fn.dwarfLocSym, fn.dwarfRangesSym, fn.dwarfAbsFnSym, fn.dwarfDebugLinesSym
   330  }
   331  
   332  // textPos returns the source position of the first instruction (prog)
   333  // of the specified function.
   334  func textPos(fn *LSym) src.XPos {
   335  	if p := fn.Func().Text; p != nil {
   336  		return p.Pos
   337  	}
   338  	return src.NoXPos
   339  }
   340  
   341  // populateDWARF fills in the DWARF Debugging Information Entries for
   342  // TEXT symbol 's'. The various DWARF symbols must already have been
   343  // initialized in InitTextSym.
   344  func (ctxt *Link) populateDWARF(curfn Func, s *LSym) {
   345  	myimportpath := ctxt.Pkgpath
   346  	if myimportpath == "" {
   347  		return
   348  	}
   349  
   350  	info, loc, ranges, absfunc, lines := ctxt.dwarfSym(s)
   351  	if info.Size != 0 {
   352  		ctxt.Diag("makeFuncDebugEntry double process %v", s)
   353  	}
   354  	var scopes []dwarf.Scope
   355  	var inlcalls dwarf.InlCalls
   356  	if ctxt.DebugInfo != nil {
   357  		scopes, inlcalls = ctxt.DebugInfo(ctxt, s, info, curfn)
   358  	}
   359  	var err error
   360  	dwctxt := dwCtxt{ctxt}
   361  	startPos := ctxt.InnermostPos(textPos(s))
   362  	if !startPos.IsKnown() || startPos.RelLine() != uint(s.Func().StartLine) {
   363  		panic("bad startPos")
   364  	}
   365  	fnstate := &dwarf.FnState{
   366  		Name:          s.Name,
   367  		Info:          info,
   368  		Loc:           loc,
   369  		Ranges:        ranges,
   370  		Absfn:         absfunc,
   371  		StartPC:       s,
   372  		Size:          s.Size,
   373  		StartPos:      startPos,
   374  		External:      !s.Static(),
   375  		Scopes:        scopes,
   376  		InlCalls:      inlcalls,
   377  		UseBASEntries: ctxt.UseBASEntries,
   378  	}
   379  	if absfunc != nil {
   380  		err = dwarf.PutAbstractFunc(dwctxt, fnstate)
   381  		if err != nil {
   382  			ctxt.Diag("emitting DWARF for %s failed: %v", s.Name, err)
   383  		}
   384  		err = dwarf.PutConcreteFunc(dwctxt, fnstate, s.Wrapper(),
   385  			ctxt.DwTextCount)
   386  	} else {
   387  		err = dwarf.PutDefaultFunc(dwctxt, fnstate, s.Wrapper())
   388  	}
   389  	if err != nil {
   390  		ctxt.Diag("emitting DWARF for %s failed: %v", s.Name, err)
   391  	}
   392  	// Fill in the debug lines symbol.
   393  	ctxt.generateDebugLinesSymbol(s, lines)
   394  }
   395  
   396  // DwarfIntConst creates a link symbol for an integer constant with the
   397  // given name, type and value.
   398  func (ctxt *Link) DwarfIntConst(name, typename string, val int64) {
   399  	myimportpath := ctxt.Pkgpath
   400  	if myimportpath == "" {
   401  		return
   402  	}
   403  	s := ctxt.LookupInit(dwarf.ConstInfoPrefix+myimportpath, func(s *LSym) {
   404  		s.Type = objabi.SDWARFCONST
   405  		ctxt.Data = append(ctxt.Data, s)
   406  	})
   407  	dwarf.PutIntConst(dwCtxt{ctxt}, s, ctxt.Lookup(dwarf.InfoPrefix+typename), myimportpath+"."+name, val)
   408  }
   409  
   410  // DwarfGlobal creates a link symbol containing a DWARF entry for
   411  // a global variable.
   412  func (ctxt *Link) DwarfGlobal(typename string, varSym *LSym) {
   413  	myimportpath := ctxt.Pkgpath
   414  	if myimportpath == "" || varSym.Local() {
   415  		return
   416  	}
   417  	varname := varSym.Name
   418  	dieSym := &LSym{
   419  		Type: objabi.SDWARFVAR,
   420  	}
   421  	varSym.NewVarInfo().dwarfInfoSym = dieSym
   422  	ctxt.Data = append(ctxt.Data, dieSym)
   423  	typeSym := ctxt.Lookup(dwarf.InfoPrefix + typename)
   424  	dwarf.PutGlobal(dwCtxt{ctxt}, dieSym, typeSym, varSym, varname)
   425  }
   426  
   427  func (ctxt *Link) DwarfAbstractFunc(curfn Func, s *LSym) {
   428  	absfn := ctxt.DwFixups.AbsFuncDwarfSym(s)
   429  	if absfn.Size != 0 {
   430  		ctxt.Diag("internal error: DwarfAbstractFunc double process %v", s)
   431  	}
   432  	if s.Func() == nil {
   433  		s.NewFuncInfo()
   434  	}
   435  	scopes, _ := ctxt.DebugInfo(ctxt, s, absfn, curfn)
   436  	dwctxt := dwCtxt{ctxt}
   437  	fnstate := dwarf.FnState{
   438  		Name:          s.Name,
   439  		Info:          absfn,
   440  		Absfn:         absfn,
   441  		StartPos:      ctxt.InnermostPos(curfn.Pos()),
   442  		External:      !s.Static(),
   443  		Scopes:        scopes,
   444  		UseBASEntries: ctxt.UseBASEntries,
   445  	}
   446  	if err := dwarf.PutAbstractFunc(dwctxt, &fnstate); err != nil {
   447  		ctxt.Diag("emitting DWARF for %s failed: %v", s.Name, err)
   448  	}
   449  }
   450  
   451  // This table is designed to aid in the creation of references between
   452  // DWARF subprogram DIEs.
   453  //
   454  // In most cases when one DWARF DIE has to refer to another DWARF DIE,
   455  // the target of the reference has an LSym, which makes it easy to use
   456  // the existing relocation mechanism. For DWARF inlined routine DIEs,
   457  // however, the subprogram DIE has to refer to a child
   458  // parameter/variable DIE of the abstract subprogram. This child DIE
   459  // doesn't have an LSym, and also of interest is the fact that when
   460  // DWARF generation is happening for inlined function F within caller
   461  // G, it's possible that DWARF generation hasn't happened yet for F,
   462  // so there is no way to know the offset of a child DIE within F's
   463  // abstract function. Making matters more complex, each inlined
   464  // instance of F may refer to a subset of the original F's variables
   465  // (depending on what happens with optimization, some vars may be
   466  // eliminated).
   467  //
   468  // The fixup table below helps overcome this hurdle. At the point
   469  // where a parameter/variable reference is made (via a call to
   470  // "ReferenceChildDIE"), a fixup record is generate that records
   471  // the relocation that is targeting that child variable. At a later
   472  // point when the abstract function DIE is emitted, there will be
   473  // a call to "RegisterChildDIEOffsets", at which point the offsets
   474  // needed to apply fixups are captured. Finally, once the parallel
   475  // portion of the compilation is done, fixups can actually be applied
   476  // during the "Finalize" method (this can't be done during the
   477  // parallel portion of the compile due to the possibility of data
   478  // races).
   479  //
   480  // This table is also used to record the "precursor" function node for
   481  // each function that is the target of an inline -- child DIE references
   482  // have to be made with respect to the original pre-optimization
   483  // version of the function (to allow for the fact that each inlined
   484  // body may be optimized differently).
   485  type DwarfFixupTable struct {
   486  	ctxt      *Link
   487  	mu        sync.Mutex
   488  	symtab    map[*LSym]int // maps abstract fn LSYM to index in svec
   489  	svec      []symFixups
   490  	precursor map[*LSym]fnState // maps fn Lsym to precursor Node, absfn sym
   491  }
   492  
   493  type symFixups struct {
   494  	fixups   []relFixup
   495  	doffsets []declOffset
   496  	inlIndex int32
   497  	defseen  bool
   498  }
   499  
   500  type declOffset struct {
   501  	// Index of variable within DCL list of pre-optimization function
   502  	dclIdx int32
   503  	// Offset of var's child DIE with respect to containing subprogram DIE
   504  	offset int32
   505  }
   506  
   507  type relFixup struct {
   508  	refsym *LSym
   509  	relidx int32
   510  	dclidx int32
   511  }
   512  
   513  type fnState struct {
   514  	// precursor function
   515  	precursor Func
   516  	// abstract function symbol
   517  	absfn *LSym
   518  }
   519  
   520  func NewDwarfFixupTable(ctxt *Link) *DwarfFixupTable {
   521  	return &DwarfFixupTable{
   522  		ctxt:      ctxt,
   523  		symtab:    make(map[*LSym]int),
   524  		precursor: make(map[*LSym]fnState),
   525  	}
   526  }
   527  
   528  func (ft *DwarfFixupTable) GetPrecursorFunc(s *LSym) Func {
   529  	if fnstate, found := ft.precursor[s]; found {
   530  		return fnstate.precursor
   531  	}
   532  	return nil
   533  }
   534  
   535  func (ft *DwarfFixupTable) SetPrecursorFunc(s *LSym, fn Func) {
   536  	if _, found := ft.precursor[s]; found {
   537  		ft.ctxt.Diag("internal error: DwarfFixupTable.SetPrecursorFunc double call on %v", s)
   538  	}
   539  
   540  	// initialize abstract function symbol now. This is done here so
   541  	// as to avoid data races later on during the parallel portion of
   542  	// the back end.
   543  	absfn := ft.ctxt.LookupDerived(s, dwarf.InfoPrefix+s.Name+dwarf.AbstractFuncSuffix)
   544  	absfn.Set(AttrDuplicateOK, true)
   545  	absfn.Type = objabi.SDWARFABSFCN
   546  	ft.ctxt.Data = append(ft.ctxt.Data, absfn)
   547  
   548  	// In the case of "late" inlining (inlines that happen during
   549  	// wrapper generation as opposed to the main inlining phase) it's
   550  	// possible that we didn't cache the abstract function sym for the
   551  	// text symbol -- do so now if needed. See issue 38068.
   552  	if fn := s.Func(); fn != nil && fn.dwarfAbsFnSym == nil {
   553  		fn.dwarfAbsFnSym = absfn
   554  	}
   555  
   556  	ft.precursor[s] = fnState{precursor: fn, absfn: absfn}
   557  }
   558  
   559  // Make a note of a child DIE reference: relocation 'ridx' within symbol 's'
   560  // is targeting child 'c' of DIE with symbol 'tgt'.
   561  func (ft *DwarfFixupTable) ReferenceChildDIE(s *LSym, ridx int, tgt *LSym, dclidx int, inlIndex int) {
   562  	// Protect against concurrent access if multiple backend workers
   563  	ft.mu.Lock()
   564  	defer ft.mu.Unlock()
   565  
   566  	// Create entry for symbol if not already present.
   567  	idx, found := ft.symtab[tgt]
   568  	if !found {
   569  		ft.svec = append(ft.svec, symFixups{inlIndex: int32(inlIndex)})
   570  		idx = len(ft.svec) - 1
   571  		ft.symtab[tgt] = idx
   572  	}
   573  
   574  	// Do we have child DIE offsets available? If so, then apply them,
   575  	// otherwise create a fixup record.
   576  	sf := &ft.svec[idx]
   577  	if len(sf.doffsets) > 0 {
   578  		found := false
   579  		for _, do := range sf.doffsets {
   580  			if do.dclIdx == int32(dclidx) {
   581  				off := do.offset
   582  				s.R[ridx].Add += int64(off)
   583  				found = true
   584  				break
   585  			}
   586  		}
   587  		if !found {
   588  			ft.ctxt.Diag("internal error: DwarfFixupTable.ReferenceChildDIE unable to locate child DIE offset for dclIdx=%d src=%v tgt=%v", dclidx, s, tgt)
   589  		}
   590  	} else {
   591  		sf.fixups = append(sf.fixups, relFixup{s, int32(ridx), int32(dclidx)})
   592  	}
   593  }
   594  
   595  // Called once DWARF generation is complete for a given abstract function,
   596  // whose children might have been referenced via a call above. Stores
   597  // the offsets for any child DIEs (vars, params) so that they can be
   598  // consumed later in on DwarfFixupTable.Finalize, which applies any
   599  // outstanding fixups.
   600  func (ft *DwarfFixupTable) RegisterChildDIEOffsets(s *LSym, vars []*dwarf.Var, coffsets []int32) {
   601  	// Length of these two slices should agree
   602  	if len(vars) != len(coffsets) {
   603  		ft.ctxt.Diag("internal error: RegisterChildDIEOffsets vars/offsets length mismatch")
   604  		return
   605  	}
   606  
   607  	// Generate the slice of declOffset's based in vars/coffsets
   608  	doffsets := make([]declOffset, len(coffsets))
   609  	for i := range coffsets {
   610  		doffsets[i].dclIdx = vars[i].ChildIndex
   611  		doffsets[i].offset = coffsets[i]
   612  	}
   613  
   614  	ft.mu.Lock()
   615  	defer ft.mu.Unlock()
   616  
   617  	// Store offsets for this symbol.
   618  	idx, found := ft.symtab[s]
   619  	if !found {
   620  		sf := symFixups{inlIndex: -1, defseen: true, doffsets: doffsets}
   621  		ft.svec = append(ft.svec, sf)
   622  		ft.symtab[s] = len(ft.svec) - 1
   623  	} else {
   624  		sf := &ft.svec[idx]
   625  		sf.doffsets = doffsets
   626  		sf.defseen = true
   627  	}
   628  }
   629  
   630  func (ft *DwarfFixupTable) processFixups(slot int, s *LSym) {
   631  	sf := &ft.svec[slot]
   632  	for _, f := range sf.fixups {
   633  		dfound := false
   634  		for _, doffset := range sf.doffsets {
   635  			if doffset.dclIdx == f.dclidx {
   636  				f.refsym.R[f.relidx].Add += int64(doffset.offset)
   637  				dfound = true
   638  				break
   639  			}
   640  		}
   641  		if !dfound {
   642  			ft.ctxt.Diag("internal error: DwarfFixupTable has orphaned fixup on %v targeting %v relidx=%d dclidx=%d", f.refsym, s, f.relidx, f.dclidx)
   643  		}
   644  	}
   645  }
   646  
   647  // return the LSym corresponding to the 'abstract subprogram' DWARF
   648  // info entry for a function.
   649  func (ft *DwarfFixupTable) AbsFuncDwarfSym(fnsym *LSym) *LSym {
   650  	// Protect against concurrent access if multiple backend workers
   651  	ft.mu.Lock()
   652  	defer ft.mu.Unlock()
   653  
   654  	if fnstate, found := ft.precursor[fnsym]; found {
   655  		return fnstate.absfn
   656  	}
   657  	ft.ctxt.Diag("internal error: AbsFuncDwarfSym requested for %v, not seen during inlining", fnsym)
   658  	return nil
   659  }
   660  
   661  // Called after all functions have been compiled; the main job of this
   662  // function is to identify cases where there are outstanding fixups.
   663  // This scenario crops up when we have references to variables of an
   664  // inlined routine, but that routine is defined in some other package.
   665  // This helper walks through and locate these fixups, then invokes a
   666  // helper to create an abstract subprogram DIE for each one.
   667  func (ft *DwarfFixupTable) Finalize(myimportpath string, trace bool) {
   668  	if trace {
   669  		ft.ctxt.Logf("DwarfFixupTable.Finalize invoked for %s\n", myimportpath)
   670  	}
   671  
   672  	// Collect up the keys from the precursor map, then sort the
   673  	// resulting list (don't want to rely on map ordering here).
   674  	fns := make([]*LSym, len(ft.precursor))
   675  	idx := 0
   676  	for fn := range ft.precursor {
   677  		fns[idx] = fn
   678  		idx++
   679  	}
   680  	slices.SortFunc(fns, func(a, b *LSym) int {
   681  		return strings.Compare(a.Name, b.Name)
   682  	})
   683  
   684  	// Should not be called during parallel portion of compilation.
   685  	if ft.ctxt.InParallel {
   686  		ft.ctxt.Diag("internal error: DwarfFixupTable.Finalize call during parallel backend")
   687  	}
   688  
   689  	// Generate any missing abstract functions.
   690  	for _, s := range fns {
   691  		absfn := ft.AbsFuncDwarfSym(s)
   692  		slot, found := ft.symtab[absfn]
   693  		if !found || !ft.svec[slot].defseen {
   694  			ft.ctxt.GenAbstractFunc(s)
   695  		}
   696  	}
   697  
   698  	// Apply fixups.
   699  	for _, s := range fns {
   700  		absfn := ft.AbsFuncDwarfSym(s)
   701  		slot, found := ft.symtab[absfn]
   702  		if !found {
   703  			ft.ctxt.Diag("internal error: DwarfFixupTable.Finalize orphan abstract function for %v", s)
   704  		} else {
   705  			ft.processFixups(slot, s)
   706  		}
   707  	}
   708  }
   709  

View as plain text