aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/link/internal/ld/pcln.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/link/internal/ld/pcln.go')
-rw-r--r--src/cmd/link/internal/ld/pcln.go939
1 files changed, 544 insertions, 395 deletions
diff --git a/src/cmd/link/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go
index 30e0bdc839..facb30fe15 100644
--- a/src/cmd/link/internal/ld/pcln.go
+++ b/src/cmd/link/internal/ld/pcln.go
@@ -6,45 +6,23 @@ package ld
import (
"cmd/internal/goobj"
- "cmd/internal/obj"
"cmd/internal/objabi"
- "cmd/internal/src"
"cmd/internal/sys"
"cmd/link/internal/loader"
"cmd/link/internal/sym"
- "encoding/binary"
"fmt"
- "log"
- "math"
"os"
"path/filepath"
- "strings"
)
-// oldPclnState holds state information used during pclntab generation. Here
-// 'ldr' is just a pointer to the context's loader, 'deferReturnSym' is the
-// index for the symbol "runtime.deferreturn", 'nameToOffset' is a helper
-// function for capturing function names, 'numberedFiles' records the file
-// number assigned to a given file symbol, 'filepaths' is a slice of expanded
-// paths (indexed by file number).
-//
-// NB: This is deprecated, and will be eliminated when pclntab_old is
-// eliminated.
-type oldPclnState struct {
- ldr *loader.Loader
- deferReturnSym loader.Sym
- numberedFiles map[string]int64
- filepaths []string
-}
-
// pclntab holds the state needed for pclntab generation.
type pclntab struct {
+ // The size of the func object in the runtime.
+ funcSize uint32
+
// The first and last functions found.
firstFunc, lastFunc loader.Sym
- // The offset to the filetab.
- filetabOffset int32
-
// Running total size of pclntab.
size int64
@@ -54,6 +32,9 @@ type pclntab struct {
pcheader loader.Sym
funcnametab loader.Sym
findfunctab loader.Sym
+ cutab loader.Sym
+ filetab loader.Sym
+ pctab loader.Sym
// The number of functions + number of TEXT sections - 1. This is such an
// unexpected value because platforms that have more than one TEXT section
@@ -64,11 +45,8 @@ type pclntab struct {
// On most platforms this is the number of reachable functions.
nfunc int32
- // maps the function symbol to offset in runtime.funcnametab
- // This doesn't need to reside in the state once pclntab_old's been
- // deleted -- it can live in generateFuncnametab.
- // TODO(jfaller): Delete me!
- funcNameOffset map[loader.Sym]int32
+ // The number of filenames in runtime.filetab.
+ nfiles uint32
}
// addGeneratedSym adds a generator symbol to pclntab, returning the new Sym.
@@ -83,40 +61,29 @@ func (state *pclntab) addGeneratedSym(ctxt *Link, name string, size int64, f gen
return s
}
-func makeOldPclnState(ctxt *Link) *oldPclnState {
- ldr := ctxt.loader
- drs := ldr.Lookup("runtime.deferreturn", sym.SymVerABIInternal)
- state := &oldPclnState{
- ldr: ldr,
- deferReturnSym: drs,
- numberedFiles: make(map[string]int64),
- // NB: initial entry in filepaths below is to reserve the zero value,
- // so that when we do a map lookup in numberedFiles fails, it will not
- // return a value slot in filepaths.
- filepaths: []string{""},
- }
-
- return state
-}
-
// makePclntab makes a pclntab object, and assembles all the compilation units
-// we'll need to write pclntab.
-func makePclntab(ctxt *Link, container loader.Bitmap) (*pclntab, []*sym.CompilationUnit) {
+// we'll need to write pclntab. Returns the pclntab structure, a slice of the
+// CompilationUnits we need, and a slice of the function symbols we need to
+// generate pclntab.
+func makePclntab(ctxt *Link, container loader.Bitmap) (*pclntab, []*sym.CompilationUnit, []loader.Sym) {
ldr := ctxt.loader
state := &pclntab{
- funcNameOffset: make(map[loader.Sym]int32),
+ // This is the size of the _func object in runtime/runtime2.go.
+ funcSize: uint32(ctxt.Arch.PtrSize + 9*4),
}
// Gather some basic stats and info.
seenCUs := make(map[*sym.CompilationUnit]struct{})
prevSect := ldr.SymSect(ctxt.Textp[0])
compUnits := []*sym.CompilationUnit{}
+ funcs := []loader.Sym{}
for _, s := range ctxt.Textp {
if !emitPcln(ctxt, s, container) {
continue
}
+ funcs = append(funcs, s)
state.nfunc++
if state.firstFunc == 0 {
state.firstFunc = s
@@ -142,116 +109,22 @@ func makePclntab(ctxt *Link, container loader.Bitmap) (*pclntab, []*sym.Compilat
compUnits = append(compUnits, cu)
}
}
- return state, compUnits
-}
-
-func ftabaddstring(ftab *loader.SymbolBuilder, s string) int32 {
- start := len(ftab.Data())
- ftab.Grow(int64(start + len(s) + 1)) // make room for s plus trailing NUL
- ftd := ftab.Data()
- copy(ftd[start:], s)
- return int32(start)
-}
-
-// numberfile assigns a file number to the file if it hasn't been assigned already.
-// This funciton looks at a CU's file at index [i], and if it's a new filename,
-// stores that filename in the global file table, and adds it to the map lookup
-// for renumbering pcfile.
-func (state *oldPclnState) numberfile(cu *sym.CompilationUnit, i goobj.CUFileIndex) int64 {
- file := cu.FileTable[i]
- if val, ok := state.numberedFiles[file]; ok {
- return val
- }
- path := file
- if strings.HasPrefix(path, src.FileSymPrefix) {
- path = file[len(src.FileSymPrefix):]
- }
- val := int64(len(state.filepaths))
- state.numberedFiles[file] = val
- state.filepaths = append(state.filepaths, expandGoroot(path))
- return val
-}
-
-func (state *oldPclnState) fileVal(cu *sym.CompilationUnit, i int32) int64 {
- file := cu.FileTable[i]
- if val, ok := state.numberedFiles[file]; ok {
- return val
- }
- panic("should have been numbered first")
-}
-
-func (state *oldPclnState) renumberfiles(ctxt *Link, cu *sym.CompilationUnit, fi loader.FuncInfo, d *sym.Pcdata) {
- // Give files numbers.
- nf := fi.NumFile()
- for i := uint32(0); i < nf; i++ {
- state.numberfile(cu, fi.File(int(i)))
- }
-
- buf := make([]byte, binary.MaxVarintLen32)
- newval := int32(-1)
- var out sym.Pcdata
- it := obj.NewPCIter(uint32(ctxt.Arch.MinLC))
- for it.Init(d.P); !it.Done; it.Next() {
- // value delta
- oldval := it.Value
-
- var val int32
- if oldval == -1 {
- val = -1
- } else {
- if oldval < 0 || oldval >= int32(len(cu.FileTable)) {
- log.Fatalf("bad pcdata %d", oldval)
- }
- val = int32(state.fileVal(cu, oldval))
- }
-
- dv := val - newval
- newval = val
-
- // value
- n := binary.PutVarint(buf, int64(dv))
- out.P = append(out.P, buf[:n]...)
-
- // pc delta
- pc := (it.NextPC - it.PC) / it.PCScale
- n = binary.PutUvarint(buf, uint64(pc))
- out.P = append(out.P, buf[:n]...)
- }
-
- // terminating value delta
- // we want to write varint-encoded 0, which is just 0
- out.P = append(out.P, 0)
-
- *d = out
-}
-
-// onlycsymbol looks at a symbol's name to report whether this is a
-// symbol that is referenced by C code
-func onlycsymbol(sname string) bool {
- switch sname {
- case "_cgo_topofstack", "__cgo_topofstack", "_cgo_panic", "crosscall2":
- return true
- }
- if strings.HasPrefix(sname, "_cgoexp_") {
- return true
- }
- return false
+ return state, compUnits, funcs
}
func emitPcln(ctxt *Link, s loader.Sym, container loader.Bitmap) bool {
- if ctxt.BuildMode == BuildModePlugin && ctxt.HeadType == objabi.Hdarwin && onlycsymbol(ctxt.loader.SymName(s)) {
- return false
- }
// We want to generate func table entries only for the "lowest
// level" symbols, not containers of subsymbols.
return !container.Has(s)
}
-func (state *oldPclnState) computeDeferReturn(target *Target, s loader.Sym) uint32 {
+func computeDeferReturn(ctxt *Link, deferReturnSym, s loader.Sym) uint32 {
+ ldr := ctxt.loader
+ target := ctxt.Target
deferreturn := uint32(0)
lastWasmAddr := uint32(0)
- relocs := state.ldr.Relocs(s)
+ relocs := ldr.Relocs(s)
for ri := 0; ri < relocs.Count(); ri++ {
r := relocs.At(ri)
if target.IsWasm() && r.Type() == objabi.R_ADDR {
@@ -262,7 +135,7 @@ func (state *oldPclnState) computeDeferReturn(target *Target, s loader.Sym) uint
// set the resumption point to PC_B.
lastWasmAddr = uint32(r.Add())
}
- if r.Type().IsDirectCall() && (r.Sym() == state.deferReturnSym || state.ldr.IsDeferReturnTramp(r.Sym())) {
+ if r.Type().IsDirectCall() && (r.Sym() == deferReturnSym || ldr.IsDeferReturnTramp(r.Sym())) {
if target.IsWasm() {
deferreturn = lastWasmAddr - 1
} else {
@@ -295,8 +168,8 @@ func (state *oldPclnState) computeDeferReturn(target *Target, s loader.Sym) uint
// genInlTreeSym generates the InlTree sym for a function with the
// specified FuncInfo.
-func (state *oldPclnState) genInlTreeSym(cu *sym.CompilationUnit, fi loader.FuncInfo, arch *sys.Arch, newState *pclntab) loader.Sym {
- ldr := state.ldr
+func genInlTreeSym(ctxt *Link, cu *sym.CompilationUnit, fi loader.FuncInfo, arch *sys.Arch, nameOffsets map[loader.Sym]uint32) loader.Sym {
+ ldr := ctxt.loader
its := ldr.CreateExtSym("", 0)
inlTreeSym := ldr.MakeSymbolUpdater(its)
// Note: the generated symbol is given a type of sym.SGOFUNC, as a
@@ -308,13 +181,8 @@ func (state *oldPclnState) genInlTreeSym(cu *sym.CompilationUnit, fi loader.Func
ninl := fi.NumInlTree()
for i := 0; i < int(ninl); i++ {
call := fi.InlTree(i)
- // Usually, call.File is already numbered since the file
- // shows up in the Pcfile table. However, two inlined calls
- // might overlap exactly so that only the innermost file
- // appears in the Pcfile table. In that case, this assigns
- // the outer file a number.
- val := state.numberfile(cu, call.File)
- nameoff, ok := newState.funcNameOffset[call.Func]
+ val := call.File
+ nameoff, ok := nameOffsets[call.Func]
if !ok {
panic("couldn't find function name offset")
}
@@ -337,6 +205,22 @@ func (state *oldPclnState) genInlTreeSym(cu *sym.CompilationUnit, fi loader.Func
return its
}
+// makeInlSyms returns a map of loader.Sym that are created inlSyms.
+func makeInlSyms(ctxt *Link, funcs []loader.Sym, nameOffsets map[loader.Sym]uint32) map[loader.Sym]loader.Sym {
+ ldr := ctxt.loader
+ // Create the inline symbols we need.
+ inlSyms := make(map[loader.Sym]loader.Sym)
+ for _, s := range funcs {
+ if fi := ldr.FuncInfo(s); fi.Valid() {
+ fi.Preload()
+ if fi.NumInlTree() > 0 {
+ inlSyms[s] = genInlTreeSym(ctxt, ldr.SymUnit(s), fi, ctxt.Arch, nameOffsets)
+ }
+ }
+ }
+ return inlSyms
+}
+
// generatePCHeader creates the runtime.pcheader symbol, setting it up as a
// generator to fill in its data later.
func (state *pclntab) generatePCHeader(ctxt *Link) {
@@ -359,24 +243,24 @@ func (state *pclntab) generatePCHeader(ctxt *Link) {
header.SetUint8(ctxt.Arch, 6, uint8(ctxt.Arch.MinLC))
header.SetUint8(ctxt.Arch, 7, uint8(ctxt.Arch.PtrSize))
off := header.SetUint(ctxt.Arch, 8, uint64(state.nfunc))
+ off = header.SetUint(ctxt.Arch, off, uint64(state.nfiles))
off = writeSymOffset(off, state.funcnametab)
+ off = writeSymOffset(off, state.cutab)
+ off = writeSymOffset(off, state.filetab)
+ off = writeSymOffset(off, state.pctab)
off = writeSymOffset(off, state.pclntab)
}
- size := int64(8 + 3*ctxt.Arch.PtrSize)
+ size := int64(8 + 7*ctxt.Arch.PtrSize)
state.pcheader = state.addGeneratedSym(ctxt, "runtime.pcheader", size, writeHeader)
}
-// walkFuncs iterates over the Textp, calling a function for each unique
+// walkFuncs iterates over the funcs, calling a function for each unique
// function and inlined function.
-func (state *pclntab) walkFuncs(ctxt *Link, container loader.Bitmap, f func(loader.Sym)) {
+func walkFuncs(ctxt *Link, funcs []loader.Sym, f func(loader.Sym)) {
ldr := ctxt.loader
seen := make(map[loader.Sym]struct{})
- for _, ls := range ctxt.Textp {
- s := loader.Sym(ls)
- if !emitPcln(ctxt, s, container) {
- continue
- }
+ for _, s := range funcs {
if _, ok := seen[s]; !ok {
f(s)
seen[s] = struct{}{}
@@ -397,207 +281,484 @@ func (state *pclntab) walkFuncs(ctxt *Link, container loader.Bitmap, f func(load
}
}
-// generateFuncnametab creates the function name table.
-func (state *pclntab) generateFuncnametab(ctxt *Link, container loader.Bitmap) {
+// generateFuncnametab creates the function name table. Returns a map of
+// func symbol to the name offset in runtime.funcnamtab.
+func (state *pclntab) generateFuncnametab(ctxt *Link, funcs []loader.Sym) map[loader.Sym]uint32 {
+ nameOffsets := make(map[loader.Sym]uint32, state.nfunc)
+
// Write the null terminated strings.
writeFuncNameTab := func(ctxt *Link, s loader.Sym) {
symtab := ctxt.loader.MakeSymbolUpdater(s)
- for s, off := range state.funcNameOffset {
+ for s, off := range nameOffsets {
symtab.AddStringAt(int64(off), ctxt.loader.SymName(s))
}
}
// Loop through the CUs, and calculate the size needed.
var size int64
- state.walkFuncs(ctxt, container, func(s loader.Sym) {
- state.funcNameOffset[s] = int32(size)
+ walkFuncs(ctxt, funcs, func(s loader.Sym) {
+ nameOffsets[s] = uint32(size)
size += int64(ctxt.loader.SymNameLen(s)) + 1 // NULL terminate
})
state.funcnametab = state.addGeneratedSym(ctxt, "runtime.funcnametab", size, writeFuncNameTab)
+ return nameOffsets
}
-// pclntab initializes the pclntab symbol with
-// runtime function and file name information.
+// walkFilenames walks funcs, calling a function for each filename used in each
+// function's line table.
+func walkFilenames(ctxt *Link, funcs []loader.Sym, f func(*sym.CompilationUnit, goobj.CUFileIndex)) {
+ ldr := ctxt.loader
-// pclntab generates the pcln table for the link output.
-func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
- // Go 1.2's symtab layout is documented in golang.org/s/go12symtab, but the
- // layout and data has changed since that time.
- //
- // As of July 2020, here's the layout of pclntab:
- //
- // .gopclntab/__gopclntab [elf/macho section]
- // runtime.pclntab
- // Carrier symbol for the entire pclntab section.
- //
- // runtime.pcheader (see: runtime/symtab.go:pcHeader)
- // 8-byte magic
- // nfunc [thearch.ptrsize bytes]
- // offset to runtime.funcnametab from the beginning of runtime.pcheader
- // offset to runtime.pclntab_old from beginning of runtime.pcheader
+ // Loop through all functions, finding the filenames we need.
+ for _, s := range funcs {
+ fi := ldr.FuncInfo(s)
+ if !fi.Valid() {
+ continue
+ }
+ fi.Preload()
+
+ cu := ldr.SymUnit(s)
+ for i, nf := 0, int(fi.NumFile()); i < nf; i++ {
+ f(cu, fi.File(i))
+ }
+ for i, ninl := 0, int(fi.NumInlTree()); i < ninl; i++ {
+ call := fi.InlTree(i)
+ f(cu, call.File)
+ }
+ }
+}
+
+// generateFilenameTabs creates LUTs needed for filename lookup. Returns a slice
+// of the index at which each CU begins in runtime.cutab.
+//
+// Function objects keep track of the files they reference to print the stack.
+// This function creates a per-CU list of filenames if CU[M] references
+// files[1-N], the following is generated:
+//
+// runtime.cutab:
+// CU[M]
+// offsetToFilename[0]
+// offsetToFilename[1]
+// ..
+//
+// runtime.filetab
+// filename[0]
+// filename[1]
+//
+// Looking up a filename then becomes:
+// 0) Given a func, and filename index [K]
+// 1) Get Func.CUIndex: M := func.cuOffset
+// 2) Find filename offset: fileOffset := runtime.cutab[M+K]
+// 3) Get the filename: getcstring(runtime.filetab[fileOffset])
+func (state *pclntab) generateFilenameTabs(ctxt *Link, compUnits []*sym.CompilationUnit, funcs []loader.Sym) []uint32 {
+ // On a per-CU basis, keep track of all the filenames we need.
//
- // runtime.funcnametab
- // []list of null terminated function names
+ // Note, that we store the filenames in a separate section in the object
+ // files, and deduplicate based on the actual value. It would be better to
+ // store the filenames as symbols, using content addressable symbols (and
+ // then not loading extra filenames), and just use the hash value of the
+ // symbol name to do this cataloging.
//
- // runtime.pclntab_old
- // function table, alternating PC and offset to func struct [each entry thearch.ptrsize bytes]
- // end PC [thearch.ptrsize bytes]
- // offset to file table [4 bytes]
- // func structures, pcdata tables.
- // filetable
+ // TOOD: Store filenames as symbols. (Note this would be easiest if you
+ // also move strings to ALWAYS using the larger content addressable hash
+ // function, and use that hash value for uniqueness testing.)
+ cuEntries := make([]goobj.CUFileIndex, len(compUnits))
+ fileOffsets := make(map[string]uint32)
- oldState := makeOldPclnState(ctxt)
- state, _ := makePclntab(ctxt, container)
+ // Walk the filenames.
+ // We store the total filename string length we need to load, and the max
+ // file index we've seen per CU so we can calculate how large the
+ // CU->global table needs to be.
+ var fileSize int64
+ walkFilenames(ctxt, funcs, func(cu *sym.CompilationUnit, i goobj.CUFileIndex) {
+ // Note we use the raw filename for lookup, but use the expanded filename
+ // when we save the size.
+ filename := cu.FileTable[i]
+ if _, ok := fileOffsets[filename]; !ok {
+ fileOffsets[filename] = uint32(fileSize)
+ fileSize += int64(len(expandFile(filename)) + 1) // NULL terminate
+ }
- ldr := ctxt.loader
- state.carrier = ldr.LookupOrCreateSym("runtime.pclntab", 0)
- ldr.MakeSymbolUpdater(state.carrier).SetType(sym.SPCLNTAB)
- ldr.SetAttrReachable(state.carrier, true)
+ // Find the maximum file index we've seen.
+ if cuEntries[cu.PclnIndex] < i+1 {
+ cuEntries[cu.PclnIndex] = i + 1 // Store max + 1
+ }
+ })
- // runtime.pclntab_old is just a placeholder,and will eventually be deleted.
- // It contains the pieces of runtime.pclntab that haven't moved to a more
- // rational form.
- state.pclntab = ldr.LookupOrCreateSym("runtime.pclntab_old", 0)
- state.generatePCHeader(ctxt)
- state.generateFuncnametab(ctxt, container)
+ // Calculate the size of the runtime.cutab variable.
+ var totalEntries uint32
+ cuOffsets := make([]uint32, len(cuEntries))
+ for i, entries := range cuEntries {
+ // Note, cutab is a slice of uint32, so an offset to a cu's entry is just the
+ // running total of all cu indices we've needed to store so far, not the
+ // number of bytes we've stored so far.
+ cuOffsets[i] = totalEntries
+ totalEntries += uint32(entries)
+ }
+
+ // Write cutab.
+ writeCutab := func(ctxt *Link, s loader.Sym) {
+ sb := ctxt.loader.MakeSymbolUpdater(s)
- funcdataBytes := int64(0)
- ldr.SetCarrierSym(state.pclntab, state.carrier)
- ldr.SetAttrNotInSymbolTable(state.pclntab, true)
- ftab := ldr.MakeSymbolUpdater(state.pclntab)
- ftab.SetValue(state.size)
- ftab.SetType(sym.SPCLNTAB)
- ftab.SetReachable(true)
+ var off int64
+ for i, max := range cuEntries {
+ // Write the per CU LUT.
+ cu := compUnits[i]
+ for j := goobj.CUFileIndex(0); j < max; j++ {
+ fileOffset, ok := fileOffsets[cu.FileTable[j]]
+ if !ok {
+ // We're looping through all possible file indices. It's possible a file's
+ // been deadcode eliminated, and although it's a valid file in the CU, it's
+ // not needed in this binary. When that happens, use an invalid offset.
+ fileOffset = ^uint32(0)
+ }
+ off = sb.SetUint32(ctxt.Arch, off, fileOffset)
+ }
+ }
+ }
+ state.cutab = state.addGeneratedSym(ctxt, "runtime.cutab", int64(totalEntries*4), writeCutab)
- ftab.Grow(int64(state.nfunc)*2*int64(ctxt.Arch.PtrSize) + int64(ctxt.Arch.PtrSize) + 4)
+ // Write filetab.
+ writeFiletab := func(ctxt *Link, s loader.Sym) {
+ sb := ctxt.loader.MakeSymbolUpdater(s)
- szHint := len(ctxt.Textp) * 2
- pctaboff := make(map[string]uint32, szHint)
- writepctab := func(off int32, p []byte) int32 {
- start, ok := pctaboff[string(p)]
- if !ok {
- if len(p) > 0 {
- start = uint32(len(ftab.Data()))
- ftab.AddBytes(p)
+ // Write the strings.
+ for filename, loc := range fileOffsets {
+ sb.AddStringAt(int64(loc), expandFile(filename))
+ }
+ }
+ state.nfiles = uint32(len(fileOffsets))
+ state.filetab = state.addGeneratedSym(ctxt, "runtime.filetab", fileSize, writeFiletab)
+
+ return cuOffsets
+}
+
+// generatePctab creates the runtime.pctab variable, holding all the
+// deduplicated pcdata.
+func (state *pclntab) generatePctab(ctxt *Link, funcs []loader.Sym) {
+ ldr := ctxt.loader
+
+ // Pctab offsets of 0 are considered invalid in the runtime. We respect
+ // that by just padding a single byte at the beginning of runtime.pctab,
+ // that way no real offsets can be zero.
+ size := int64(1)
+
+ // Walk the functions, finding offset to store each pcdata.
+ seen := make(map[loader.Sym]struct{})
+ saveOffset := func(pcSym loader.Sym) {
+ if _, ok := seen[pcSym]; !ok {
+ datSize := ldr.SymSize(pcSym)
+ if datSize != 0 {
+ ldr.SetSymValue(pcSym, size)
+ } else {
+ // Invalid PC data, record as zero.
+ ldr.SetSymValue(pcSym, 0)
}
- pctaboff[string(p)] = start
+ size += datSize
+ seen[pcSym] = struct{}{}
+ }
+ }
+ for _, s := range funcs {
+ fi := ldr.FuncInfo(s)
+ if !fi.Valid() {
+ continue
+ }
+ fi.Preload()
+
+ pcSyms := []loader.Sym{fi.Pcsp(), fi.Pcfile(), fi.Pcline()}
+ for _, pcSym := range pcSyms {
+ saveOffset(pcSym)
+ }
+ for _, pcSym := range fi.Pcdata() {
+ saveOffset(pcSym)
+ }
+ if fi.NumInlTree() > 0 {
+ saveOffset(fi.Pcinline())
+ }
+ }
+
+ // TODO: There is no reason we need a generator for this variable, and it
+ // could be moved to a carrier symbol. However, carrier symbols containing
+ // carrier symbols don't work yet (as of Aug 2020). Once this is fixed,
+ // runtime.pctab could just be a carrier sym.
+ writePctab := func(ctxt *Link, s loader.Sym) {
+ ldr := ctxt.loader
+ sb := ldr.MakeSymbolUpdater(s)
+ for sym := range seen {
+ sb.SetBytesAt(ldr.SymValue(sym), ldr.Data(sym))
+ }
+ }
+
+ state.pctab = state.addGeneratedSym(ctxt, "runtime.pctab", size, writePctab)
+}
+
+// numPCData returns the number of PCData syms for the FuncInfo.
+// NB: Preload must be called on valid FuncInfos before calling this function.
+func numPCData(fi loader.FuncInfo) uint32 {
+ if !fi.Valid() {
+ return 0
+ }
+ numPCData := uint32(len(fi.Pcdata()))
+ if fi.NumInlTree() > 0 {
+ if numPCData < objabi.PCDATA_InlTreeIndex+1 {
+ numPCData = objabi.PCDATA_InlTreeIndex + 1
}
- newoff := int32(ftab.SetUint32(ctxt.Arch, int64(off), start))
- return newoff
}
+ return numPCData
+}
+
+// Helper types for iterating pclntab.
+type pclnSetAddr func(*loader.SymbolBuilder, *sys.Arch, int64, loader.Sym, int64) int64
+type pclnSetUint func(*loader.SymbolBuilder, *sys.Arch, int64, uint64) int64
- setAddr := (*loader.SymbolBuilder).SetAddrPlus
- if ctxt.IsExe() && ctxt.IsInternal() {
- // Internal linking static executable. At this point the function
- // addresses are known, so we can just use them instead of emitting
- // relocations.
- // For other cases we are generating a relocatable binary so we
- // still need to emit relocations.
- setAddr = func(s *loader.SymbolBuilder, arch *sys.Arch, off int64, tgt loader.Sym, add int64) int64 {
- if v := ldr.SymValue(tgt); v != 0 {
- return s.SetUint(arch, off, uint64(v+add))
+// generateFunctab creates the runtime.functab
+//
+// runtime.functab contains two things:
+//
+// - pc->func look up table.
+// - array of func objects, interleaved with pcdata and funcdata
+//
+// Because of timing in the linker, generating this table takes two passes.
+// The first pass is executed early in the link, and it creates any needed
+// relocations to layout the data. The pieces that need relocations are:
+// 1) the PC->func table.
+// 2) The entry points in the func objects.
+// 3) The funcdata.
+// (1) and (2) are handled in walkPCToFunc. (3) is handled in walkFuncdata.
+//
+// After relocations, once we know where to write things in the output buffer,
+// we execute the second pass, which is actually writing the data.
+func (state *pclntab) generateFunctab(ctxt *Link, funcs []loader.Sym, inlSyms map[loader.Sym]loader.Sym, cuOffsets []uint32, nameOffsets map[loader.Sym]uint32) {
+ // Calculate the size of the table.
+ size, startLocations := state.calculateFunctabSize(ctxt, funcs)
+
+ // If we are internally linking a static executable, the function addresses
+ // are known, so we can just use them instead of emitting relocations. For
+ // other cases we still need to emit relocations.
+ //
+ // This boolean just helps us figure out which callback to use.
+ useSymValue := ctxt.IsExe() && ctxt.IsInternal()
+
+ writePcln := func(ctxt *Link, s loader.Sym) {
+ ldr := ctxt.loader
+ sb := ldr.MakeSymbolUpdater(s)
+
+ // Create our callbacks.
+ var setAddr pclnSetAddr
+ if useSymValue {
+ // We need to write the offset.
+ setAddr = func(s *loader.SymbolBuilder, arch *sys.Arch, off int64, tgt loader.Sym, add int64) int64 {
+ if v := ldr.SymValue(tgt); v != 0 {
+ s.SetUint(arch, off, uint64(v+add))
+ }
+ return 0
}
- return s.SetAddrPlus(arch, off, tgt, add)
+ } else {
+ // We already wrote relocations.
+ setAddr = func(s *loader.SymbolBuilder, arch *sys.Arch, off int64, tgt loader.Sym, add int64) int64 { return 0 }
}
+
+ // Write the data.
+ writePcToFunc(ctxt, sb, funcs, startLocations, setAddr, (*loader.SymbolBuilder).SetUint)
+ writeFuncs(ctxt, sb, funcs, inlSyms, startLocations, cuOffsets, nameOffsets)
+ state.writeFuncData(ctxt, sb, funcs, inlSyms, startLocations, setAddr, (*loader.SymbolBuilder).SetUint)
}
- pcsp := sym.Pcdata{}
- pcfile := sym.Pcdata{}
- pcline := sym.Pcdata{}
- pcdata := []sym.Pcdata{}
- funcdata := []loader.Sym{}
- funcdataoff := []int64{}
+ state.pclntab = state.addGeneratedSym(ctxt, "runtime.functab", size, writePcln)
- var nfunc int32
- prevFunc := ctxt.Textp[0]
- for _, s := range ctxt.Textp {
- if !emitPcln(ctxt, s, container) {
- continue
+ // Create the relocations we need.
+ ldr := ctxt.loader
+ sb := ldr.MakeSymbolUpdater(state.pclntab)
+
+ var setAddr pclnSetAddr
+ if useSymValue {
+ // If we should use the symbol value, and we don't have one, write a relocation.
+ setAddr = func(sb *loader.SymbolBuilder, arch *sys.Arch, off int64, tgt loader.Sym, add int64) int64 {
+ if v := ldr.SymValue(tgt); v == 0 {
+ sb.SetAddrPlus(arch, off, tgt, add)
+ }
+ return 0
}
+ } else {
+ // If we're externally linking, write a relocation.
+ setAddr = (*loader.SymbolBuilder).SetAddrPlus
+ }
+ setUintNOP := func(*loader.SymbolBuilder, *sys.Arch, int64, uint64) int64 { return 0 }
+ writePcToFunc(ctxt, sb, funcs, startLocations, setAddr, setUintNOP)
+ if !useSymValue {
+ // Generate relocations for funcdata when externally linking.
+ state.writeFuncData(ctxt, sb, funcs, inlSyms, startLocations, setAddr, setUintNOP)
+ }
+}
- thisSect := ldr.SymSect(s)
- prevSect := ldr.SymSect(prevFunc)
- if thisSect != prevSect {
- // With multiple text sections, there may be a hole here
- // in the address space (see the comment above). We use an
- // invalid funcoff value to mark the hole. See also
- // runtime/symtab.go:findfunc
- prevFuncSize := int64(ldr.SymSize(prevFunc))
- setAddr(ftab, ctxt.Arch, int64(nfunc)*2*int64(ctxt.Arch.PtrSize), prevFunc, prevFuncSize)
- ftab.SetUint(ctxt.Arch, int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), ^uint64(0))
- nfunc++
+// funcData returns the funcdata and offsets for the FuncInfo.
+// The funcdata and offsets are written into runtime.functab after each func
+// object. This is a helper function to make querying the FuncInfo object
+// cleaner.
+//
+// Note, the majority of fdOffsets are 0, meaning there is no offset between
+// the compiler's generated symbol, and what the runtime needs. They are
+// plumbed through for no loss of generality.
+//
+// NB: Preload must be called on the FuncInfo before calling.
+// NB: fdSyms and fdOffs are used as scratch space.
+func funcData(fi loader.FuncInfo, inlSym loader.Sym, fdSyms []loader.Sym, fdOffs []int64) ([]loader.Sym, []int64) {
+ fdSyms, fdOffs = fdSyms[:0], fdOffs[:0]
+ if fi.Valid() {
+ numOffsets := int(fi.NumFuncdataoff())
+ for i := 0; i < numOffsets; i++ {
+ fdOffs = append(fdOffs, fi.Funcdataoff(i))
}
- prevFunc = s
+ fdSyms = fi.Funcdata(fdSyms)
+ if fi.NumInlTree() > 0 {
+ if len(fdSyms) < objabi.FUNCDATA_InlTree+1 {
+ fdSyms = append(fdSyms, make([]loader.Sym, objabi.FUNCDATA_InlTree+1-len(fdSyms))...)
+ fdOffs = append(fdOffs, make([]int64, objabi.FUNCDATA_InlTree+1-len(fdOffs))...)
+ }
+ fdSyms[objabi.FUNCDATA_InlTree] = inlSym
+ }
+ }
+ return fdSyms, fdOffs
+}
+
+// calculateFunctabSize calculates the size of the pclntab, and the offsets in
+// the output buffer for individual func entries.
+func (state pclntab) calculateFunctabSize(ctxt *Link, funcs []loader.Sym) (int64, []uint32) {
+ ldr := ctxt.loader
+ startLocations := make([]uint32, len(funcs))
+
+ // Allocate space for the pc->func table. This structure consists of a pc
+ // and an offset to the func structure. After that, we have a single pc
+ // value that marks the end of the last function in the binary.
+ size := int64(int(state.nfunc)*2*ctxt.Arch.PtrSize + ctxt.Arch.PtrSize)
- pcsp.P = pcsp.P[:0]
- pcline.P = pcline.P[:0]
- pcfile.P = pcfile.P[:0]
- pcdata = pcdata[:0]
- funcdataoff = funcdataoff[:0]
- funcdata = funcdata[:0]
+ // Now find the space for the func objects. We do this in a running manner,
+ // so that we can find individual starting locations, and because funcdata
+ // requires alignment.
+ for i, s := range funcs {
+ size = Rnd(size, int64(ctxt.Arch.PtrSize))
+ startLocations[i] = uint32(size)
fi := ldr.FuncInfo(s)
+ size += int64(state.funcSize)
if fi.Valid() {
fi.Preload()
- npc := fi.NumPcdata()
- for i := uint32(0); i < npc; i++ {
- pcdata = append(pcdata, sym.Pcdata{P: fi.Pcdata(int(i))})
+ numFuncData := int(fi.NumFuncdataoff())
+ if fi.NumInlTree() > 0 {
+ if numFuncData < objabi.FUNCDATA_InlTree+1 {
+ numFuncData = objabi.FUNCDATA_InlTree + 1
+ }
}
- nfd := fi.NumFuncdataoff()
- for i := uint32(0); i < nfd; i++ {
- funcdataoff = append(funcdataoff, fi.Funcdataoff(int(i)))
+ size += int64(numPCData(fi) * 4)
+ if numFuncData > 0 { // Func data is aligned.
+ size = Rnd(size, int64(ctxt.Arch.PtrSize))
}
- funcdata = fi.Funcdata(funcdata)
+ size += int64(numFuncData * ctxt.Arch.PtrSize)
}
+ }
- if fi.Valid() && fi.NumInlTree() > 0 {
-
- if len(pcdata) <= objabi.PCDATA_InlTreeIndex {
- // Create inlining pcdata table.
- newpcdata := make([]sym.Pcdata, objabi.PCDATA_InlTreeIndex+1)
- copy(newpcdata, pcdata)
- pcdata = newpcdata
- }
+ return size, startLocations
+}
- if len(funcdataoff) <= objabi.FUNCDATA_InlTree {
- // Create inline tree funcdata.
- newfuncdata := make([]loader.Sym, objabi.FUNCDATA_InlTree+1)
- newfuncdataoff := make([]int64, objabi.FUNCDATA_InlTree+1)
- copy(newfuncdata, funcdata)
- copy(newfuncdataoff, funcdataoff)
- funcdata = newfuncdata
- funcdataoff = newfuncdataoff
- }
+// writePcToFunc writes the PC->func lookup table.
+// This function walks the pc->func lookup table, executing callbacks
+// to generate relocations and writing the values for the table.
+func writePcToFunc(ctxt *Link, sb *loader.SymbolBuilder, funcs []loader.Sym, startLocations []uint32, setAddr pclnSetAddr, setUint pclnSetUint) {
+ ldr := ctxt.loader
+ var prevFunc loader.Sym
+ prevSect := ldr.SymSect(funcs[0])
+ funcIndex := 0
+ for i, s := range funcs {
+ if thisSect := ldr.SymSect(s); thisSect != prevSect {
+ // With multiple text sections, there may be a hole here in the
+ // address space. We use an invalid funcoff value to mark the hole.
+ // See also runtime/symtab.go:findfunc
+ prevFuncSize := int64(ldr.SymSize(prevFunc))
+ setAddr(sb, ctxt.Arch, int64(funcIndex*2*ctxt.Arch.PtrSize), prevFunc, prevFuncSize)
+ setUint(sb, ctxt.Arch, int64((funcIndex*2+1)*ctxt.Arch.PtrSize), ^uint64(0))
+ funcIndex++
+ prevSect = thisSect
}
+ prevFunc = s
+ // TODO: We don't actually need these relocations, provided we go to a
+ // module->func look-up-table like we do for filenames. We could have a
+ // single relocation for the module, and have them all laid out as
+ // offsets from the beginning of that module.
+ setAddr(sb, ctxt.Arch, int64(funcIndex*2*ctxt.Arch.PtrSize), s, 0)
+ setUint(sb, ctxt.Arch, int64((funcIndex*2+1)*ctxt.Arch.PtrSize), uint64(startLocations[i]))
+ funcIndex++
- dSize := len(ftab.Data())
- funcstart := int32(dSize)
- funcstart += int32(-dSize) & (int32(ctxt.Arch.PtrSize) - 1) // align to ptrsize
+ // Write the entry location.
+ setAddr(sb, ctxt.Arch, int64(startLocations[i]), s, 0)
+ }
- setAddr(ftab, ctxt.Arch, int64(nfunc)*2*int64(ctxt.Arch.PtrSize), s, 0)
- ftab.SetUint(ctxt.Arch, int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), uint64(funcstart))
+ // Final entry of table is just end pc.
+ setAddr(sb, ctxt.Arch, int64(funcIndex)*2*int64(ctxt.Arch.PtrSize), prevFunc, ldr.SymSize(prevFunc))
+}
- // Write runtime._func. Keep in sync with ../../../../runtime/runtime2.go:/_func
- // and package debug/gosym.
+// writeFuncData writes the funcdata tables.
+//
+// This function executes a callback for each funcdata needed in
+// runtime.functab. It should be called once for internally linked static
+// binaries, or twice (once to generate the needed relocations) for other
+// build modes.
+//
+// Note the output of this function is interwoven with writeFuncs, but this is
+// a separate function, because it's needed in different passes in
+// generateFunctab.
+func (state *pclntab) writeFuncData(ctxt *Link, sb *loader.SymbolBuilder, funcs []loader.Sym, inlSyms map[loader.Sym]loader.Sym, startLocations []uint32, setAddr pclnSetAddr, setUint pclnSetUint) {
+ ldr := ctxt.loader
+ funcdata, funcdataoff := []loader.Sym{}, []int64{}
+ for i, s := range funcs {
+ fi := ldr.FuncInfo(s)
+ if !fi.Valid() {
+ continue
+ }
+ fi.Preload()
- // fixed size of struct, checked below
- off := funcstart
+ // funcdata, must be pointer-aligned and we're only int32-aligned.
+ // Missing funcdata will be 0 (nil pointer).
+ funcdata, funcdataoff := funcData(fi, inlSyms[s], funcdata, funcdataoff)
+ if len(funcdata) > 0 {
+ off := int64(startLocations[i] + state.funcSize + numPCData(fi)*4)
+ off = Rnd(off, int64(ctxt.Arch.PtrSize))
+ for j := range funcdata {
+ dataoff := off + int64(ctxt.Arch.PtrSize*j)
+ if funcdata[j] == 0 {
+ setUint(sb, ctxt.Arch, dataoff, uint64(funcdataoff[j]))
+ continue
+ }
+ // TODO: Does this need deduping?
+ setAddr(sb, ctxt.Arch, dataoff, funcdata[j], funcdataoff[j])
+ }
+ }
+ }
+}
+
+// writeFuncs writes the func structures and pcdata to runtime.functab.
+func writeFuncs(ctxt *Link, sb *loader.SymbolBuilder, funcs []loader.Sym, inlSyms map[loader.Sym]loader.Sym, startLocations, cuOffsets []uint32, nameOffsets map[loader.Sym]uint32) {
+ ldr := ctxt.loader
+ deferReturnSym := ldr.Lookup("runtime.deferreturn", sym.SymVerABIInternal)
+ funcdata, funcdataoff := []loader.Sym{}, []int64{}
- end := funcstart + int32(ctxt.Arch.PtrSize) + 3*4 + 5*4 + int32(len(pcdata))*4 + int32(len(funcdata))*int32(ctxt.Arch.PtrSize)
- if len(funcdata) > 0 && (end&int32(ctxt.Arch.PtrSize-1) != 0) {
- end += 4
+ // Write the individual func objects.
+ for i, s := range funcs {
+ fi := ldr.FuncInfo(s)
+ if fi.Valid() {
+ fi.Preload()
}
- ftab.Grow(int64(end))
- // entry uintptr
- off = int32(setAddr(ftab, ctxt.Arch, int64(off), s, 0))
+ // Note we skip the space for the entry value -- that's handled inn
+ // walkPCToFunc. We don't write it here, because it might require a
+ // relocation.
+ off := startLocations[i] + uint32(ctxt.Arch.PtrSize) // entry
// name int32
- nameoff, ok := state.funcNameOffset[s]
+ nameoff, ok := nameOffsets[s]
if !ok {
panic("couldn't find function name offset")
}
- off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(nameoff)))
+ off = uint32(sb.SetUint32(ctxt.Arch, int64(off), uint32(nameoff)))
// args int32
// TODO: Move into funcinfo.
@@ -605,118 +766,106 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
if fi.Valid() {
args = uint32(fi.Args())
}
- off = int32(ftab.SetUint32(ctxt.Arch, int64(off), args))
+ off = uint32(sb.SetUint32(ctxt.Arch, int64(off), args))
// deferreturn
- deferreturn := oldState.computeDeferReturn(&ctxt.Target, s)
- off = int32(ftab.SetUint32(ctxt.Arch, int64(off), deferreturn))
+ deferreturn := computeDeferReturn(ctxt, deferReturnSym, s)
+ off = uint32(sb.SetUint32(ctxt.Arch, int64(off), deferreturn))
- cu := ldr.SymUnit(s)
+ // pcdata
if fi.Valid() {
- pcsp = sym.Pcdata{P: fi.Pcsp()}
- pcfile = sym.Pcdata{P: fi.Pcfile()}
- pcline = sym.Pcdata{P: fi.Pcline()}
- oldState.renumberfiles(ctxt, cu, fi, &pcfile)
- if false {
- // Sanity check the new numbering
- it := obj.NewPCIter(uint32(ctxt.Arch.MinLC))
- for it.Init(pcfile.P); !it.Done; it.Next() {
- if it.Value < 1 || it.Value > int32(len(oldState.numberedFiles)) {
- ctxt.Errorf(s, "bad file number in pcfile: %d not in range [1, %d]\n", it.Value, len(oldState.numberedFiles))
- errorexit()
- }
- }
- }
- }
-
- if fi.Valid() && fi.NumInlTree() > 0 {
- its := oldState.genInlTreeSym(cu, fi, ctxt.Arch, state)
- funcdata[objabi.FUNCDATA_InlTree] = its
- pcdata[objabi.PCDATA_InlTreeIndex] = sym.Pcdata{P: fi.Pcinline()}
+ off = uint32(sb.SetUint32(ctxt.Arch, int64(off), uint32(ldr.SymValue(fi.Pcsp()))))
+ off = uint32(sb.SetUint32(ctxt.Arch, int64(off), uint32(ldr.SymValue(fi.Pcfile()))))
+ off = uint32(sb.SetUint32(ctxt.Arch, int64(off), uint32(ldr.SymValue(fi.Pcline()))))
+ } else {
+ off += 12
}
+ off = uint32(sb.SetUint32(ctxt.Arch, int64(off), uint32(numPCData(fi))))
- // pcdata
- off = writepctab(off, pcsp.P)
- off = writepctab(off, pcfile.P)
- off = writepctab(off, pcline.P)
- off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(len(pcdata))))
-
- // Store the compilation unit index.
- cuIdx := ^uint16(0)
+ // Store the offset to compilation unit's file table.
+ cuIdx := ^uint32(0)
if cu := ldr.SymUnit(s); cu != nil {
- if cu.PclnIndex > math.MaxUint16 {
- panic("cu limit reached.")
- }
- cuIdx = uint16(cu.PclnIndex)
+ cuIdx = cuOffsets[cu.PclnIndex]
}
- off = int32(ftab.SetUint16(ctxt.Arch, int64(off), cuIdx))
+ off = uint32(sb.SetUint32(ctxt.Arch, int64(off), cuIdx))
// funcID uint8
var funcID objabi.FuncID
if fi.Valid() {
funcID = fi.FuncID()
}
- off = int32(ftab.SetUint8(ctxt.Arch, int64(off), uint8(funcID)))
+ off = uint32(sb.SetUint8(ctxt.Arch, int64(off), uint8(funcID)))
+
+ off += 2 // pad
// nfuncdata must be the final entry.
- off = int32(ftab.SetUint8(ctxt.Arch, int64(off), uint8(len(funcdata))))
- for i := range pcdata {
- off = writepctab(off, pcdata[i].P)
- }
+ funcdata, funcdataoff = funcData(fi, 0, funcdata, funcdataoff)
+ off = uint32(sb.SetUint8(ctxt.Arch, int64(off), uint8(len(funcdata))))
- // funcdata, must be pointer-aligned and we're only int32-aligned.
- // Missing funcdata will be 0 (nil pointer).
- if len(funcdata) > 0 {
- if off&int32(ctxt.Arch.PtrSize-1) != 0 {
- off += 4
+ // Output the pcdata.
+ if fi.Valid() {
+ for j, pcSym := range fi.Pcdata() {
+ sb.SetUint32(ctxt.Arch, int64(off+uint32(j*4)), uint32(ldr.SymValue(pcSym)))
}
- for i := range funcdata {
- dataoff := int64(off) + int64(ctxt.Arch.PtrSize)*int64(i)
- if funcdata[i] == 0 {
- ftab.SetUint(ctxt.Arch, dataoff, uint64(funcdataoff[i]))
- continue
- }
- // TODO: Dedup.
- funcdataBytes += int64(len(ldr.Data(funcdata[i])))
- setAddr(ftab, ctxt.Arch, dataoff, funcdata[i], funcdataoff[i])
+ if fi.NumInlTree() > 0 {
+ sb.SetUint32(ctxt.Arch, int64(off+objabi.PCDATA_InlTreeIndex*4), uint32(ldr.SymValue(fi.Pcinline())))
}
- off += int32(len(funcdata)) * int32(ctxt.Arch.PtrSize)
- }
-
- if off != end {
- ctxt.Errorf(s, "bad math in functab: funcstart=%d off=%d but end=%d (npcdata=%d nfuncdata=%d ptrsize=%d)", funcstart, off, end, len(pcdata), len(funcdata), ctxt.Arch.PtrSize)
- errorexit()
}
-
- nfunc++
}
+}
- // Final entry of table is just end pc.
- setAddr(ftab, ctxt.Arch, int64(nfunc)*2*int64(ctxt.Arch.PtrSize), state.lastFunc, ldr.SymSize(state.lastFunc))
-
- // Start file table.
- dSize := len(ftab.Data())
- start := int32(dSize)
- start += int32(-dSize) & (int32(ctxt.Arch.PtrSize) - 1)
- state.filetabOffset = start
- ftab.SetUint32(ctxt.Arch, int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), uint32(start))
+// pclntab initializes the pclntab symbol with
+// runtime function and file name information.
- nf := len(oldState.numberedFiles)
- ftab.Grow(int64(start) + int64((nf+1)*4))
- ftab.SetUint32(ctxt.Arch, int64(start), uint32(nf+1))
- for i := nf; i > 0; i-- {
- path := oldState.filepaths[i]
- val := int64(i)
- ftab.SetUint32(ctxt.Arch, int64(start)+val*4, uint32(ftabaddstring(ftab, path)))
- }
+// pclntab generates the pcln table for the link output.
+func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab {
+ // Go 1.2's symtab layout is documented in golang.org/s/go12symtab, but the
+ // layout and data has changed since that time.
+ //
+ // As of August 2020, here's the layout of pclntab:
+ //
+ // .gopclntab/__gopclntab [elf/macho section]
+ // runtime.pclntab
+ // Carrier symbol for the entire pclntab section.
+ //
+ // runtime.pcheader (see: runtime/symtab.go:pcHeader)
+ // 8-byte magic
+ // nfunc [thearch.ptrsize bytes]
+ // offset to runtime.funcnametab from the beginning of runtime.pcheader
+ // offset to runtime.pclntab_old from beginning of runtime.pcheader
+ //
+ // runtime.funcnametab
+ // []list of null terminated function names
+ //
+ // runtime.cutab
+ // for i=0..#CUs
+ // for j=0..#max used file index in CU[i]
+ // uint32 offset into runtime.filetab for the filename[j]
+ //
+ // runtime.filetab
+ // []null terminated filename strings
+ //
+ // runtime.pctab
+ // []byte of deduplicated pc data.
+ //
+ // runtime.functab
+ // function table, alternating PC and offset to func struct [each entry thearch.ptrsize bytes]
+ // end PC [thearch.ptrsize bytes]
+ // func structures, pcdata offsets, func data.
- ftab.SetSize(int64(len(ftab.Data())))
+ state, compUnits, funcs := makePclntab(ctxt, container)
- ctxt.NumFilesyms = len(oldState.numberedFiles)
+ ldr := ctxt.loader
+ state.carrier = ldr.LookupOrCreateSym("runtime.pclntab", 0)
+ ldr.MakeSymbolUpdater(state.carrier).SetType(sym.SPCLNTAB)
+ ldr.SetAttrReachable(state.carrier, true)
- if ctxt.Debugvlog != 0 {
- ctxt.Logf("pclntab=%d bytes, funcdata total %d bytes\n", ftab.Size(), funcdataBytes)
- }
+ state.generatePCHeader(ctxt)
+ nameOffsets := state.generateFuncnametab(ctxt, funcs)
+ cuOffsets := state.generateFilenameTabs(ctxt, compUnits, funcs)
+ state.generatePctab(ctxt, funcs)
+ inlSyms := makeInlSyms(ctxt, funcs, nameOffsets)
+ state.generateFunctab(ctxt, funcs, inlSyms, cuOffsets, nameOffsets)
return state
}