diff options
Diffstat (limited to 'src/cmd/link/internal/ld/pcln.go')
-rw-r--r-- | src/cmd/link/internal/ld/pcln.go | 935 |
1 files changed, 935 insertions, 0 deletions
diff --git a/src/cmd/link/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go new file mode 100644 index 0000000..34ab86c --- /dev/null +++ b/src/cmd/link/internal/ld/pcln.go @@ -0,0 +1,935 @@ +// Copyright 2013 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. + +package ld + +import ( + "cmd/internal/goobj" + "cmd/internal/objabi" + "cmd/internal/sys" + "cmd/link/internal/loader" + "cmd/link/internal/sym" + "fmt" + "internal/buildcfg" + "os" + "path/filepath" + "strings" +) + +const funcSize = 11 * 4 // funcSize is the size of the _func object in runtime/runtime2.go + +// pclntab holds the state needed for pclntab generation. +type pclntab struct { + // The first and last functions found. + firstFunc, lastFunc loader.Sym + + // Running total size of pclntab. + size int64 + + // runtime.pclntab's symbols + carrier loader.Sym + pclntab loader.Sym + 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 + // get a dummy function inserted between because the external linker can place + // functions in those areas. We mark those areas as not covered by the Go + // runtime. + // + // On most platforms this is the number of reachable functions. + nfunc int32 + + // The number of filenames in runtime.filetab. + nfiles uint32 +} + +// addGeneratedSym adds a generator symbol to pclntab, returning the new Sym. +// It is the caller's responsibility to save the symbol in state. +func (state *pclntab) addGeneratedSym(ctxt *Link, name string, size int64, f generatorFunc) loader.Sym { + size = Rnd(size, int64(ctxt.Arch.PtrSize)) + state.size += size + s := ctxt.createGeneratorSymbol(name, 0, sym.SPCLNTAB, size, f) + ctxt.loader.SetAttrReachable(s, true) + ctxt.loader.SetCarrierSym(s, state.carrier) + ctxt.loader.SetAttrNotInSymbolTable(s, true) + return s +} + +// makePclntab makes a pclntab object, and assembles all the compilation units +// 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 := new(pclntab) + + // Gather some basic stats and info. + seenCUs := make(map[*sym.CompilationUnit]struct{}) + 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 + } + state.lastFunc = s + + // We need to keep track of all compilation units we see. Some symbols + // (eg, go.buildid, _cgoexp_, etc) won't have a compilation unit. + cu := ldr.SymUnit(s) + if _, ok := seenCUs[cu]; cu != nil && !ok { + seenCUs[cu] = struct{}{} + cu.PclnIndex = len(compUnits) + compUnits = append(compUnits, cu) + } + } + return state, compUnits, funcs +} + +func emitPcln(ctxt *Link, s loader.Sym, container loader.Bitmap) bool { + // We want to generate func table entries only for the "lowest + // level" symbols, not containers of subsymbols. + return !container.Has(s) +} + +func computeDeferReturn(ctxt *Link, deferReturnSym, s loader.Sym) uint32 { + ldr := ctxt.loader + target := ctxt.Target + deferreturn := uint32(0) + lastWasmAddr := uint32(0) + + relocs := ldr.Relocs(s) + for ri := 0; ri < relocs.Count(); ri++ { + r := relocs.At(ri) + if target.IsWasm() && r.Type() == objabi.R_ADDR { + // wasm/ssa.go generates an ARESUMEPOINT just + // before the deferreturn call. The "PC" of + // the deferreturn call is stored in the + // R_ADDR relocation on the ARESUMEPOINT. + lastWasmAddr = uint32(r.Add()) + } + if r.Type().IsDirectCall() && (r.Sym() == deferReturnSym || ldr.IsDeferReturnTramp(r.Sym())) { + if target.IsWasm() { + deferreturn = lastWasmAddr - 1 + } else { + // Note: the relocation target is in the call instruction, but + // is not necessarily the whole instruction (for instance, on + // x86 the relocation applies to bytes [1:5] of the 5 byte call + // instruction). + deferreturn = uint32(r.Off()) + switch target.Arch.Family { + case sys.AMD64, sys.I386: + deferreturn-- + case sys.ARM, sys.ARM64, sys.Loong64, sys.MIPS, sys.MIPS64, sys.PPC64, sys.RISCV64: + // no change + case sys.S390X: + deferreturn -= 2 + default: + panic(fmt.Sprint("Unhandled architecture:", target.Arch.Family)) + } + } + break // only need one + } + } + return deferreturn +} + +// genInlTreeSym generates the InlTree sym for a function with the +// specified FuncInfo. +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 + // signal to the symtab() phase that it needs to be grouped in with + // other similar symbols (gcdata, etc); the dodata() phase will + // eventually switch the type back to SRODATA. + inlTreeSym.SetType(sym.SGOFUNC) + ldr.SetAttrReachable(its, true) + ldr.SetSymAlign(its, 4) // it has 32-bit fields + ninl := fi.NumInlTree() + for i := 0; i < int(ninl); i++ { + call := fi.InlTree(i) + nameOff, ok := nameOffsets[call.Func] + if !ok { + panic("couldn't find function name offset") + } + + inlFunc := ldr.FuncInfo(call.Func) + var funcID objabi.FuncID + startLine := int32(0) + if inlFunc.Valid() { + funcID = inlFunc.FuncID() + startLine = inlFunc.StartLine() + } else if !ctxt.linkShared { + // Inlined functions are always Go functions, and thus + // must have FuncInfo. + // + // Unfortunately, with -linkshared, the inlined + // function may be external symbols (from another + // shared library), and we don't load FuncInfo from the + // shared library. We will report potentially incorrect + // FuncID in this case. See https://go.dev/issue/55954. + panic(fmt.Sprintf("inlined function %s missing func info", ldr.SymName(call.Func))) + } + + // Construct runtime.inlinedCall value. + const size = 16 + inlTreeSym.SetUint8(arch, int64(i*size+0), uint8(funcID)) + // Bytes 1-3 are unused. + inlTreeSym.SetUint32(arch, int64(i*size+4), uint32(nameOff)) + inlTreeSym.SetUint32(arch, int64(i*size+8), uint32(call.ParentPC)) + inlTreeSym.SetUint32(arch, int64(i*size+12), uint32(startLine)) + } + 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) { + ldr := ctxt.loader + textStartOff := int64(8 + 2*ctxt.Arch.PtrSize) + size := int64(8 + 8*ctxt.Arch.PtrSize) + writeHeader := func(ctxt *Link, s loader.Sym) { + header := ctxt.loader.MakeSymbolUpdater(s) + + writeSymOffset := func(off int64, ws loader.Sym) int64 { + diff := ldr.SymValue(ws) - ldr.SymValue(s) + if diff <= 0 { + name := ldr.SymName(ws) + panic(fmt.Sprintf("expected runtime.pcheader(%x) to be placed before %s(%x)", ldr.SymValue(s), name, ldr.SymValue(ws))) + } + return header.SetUintptr(ctxt.Arch, off, uintptr(diff)) + } + + // Write header. + // Keep in sync with runtime/symtab.go:pcHeader and package debug/gosym. + header.SetUint32(ctxt.Arch, 0, 0xfffffff1) + 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)) + if off != textStartOff { + panic(fmt.Sprintf("pcHeader textStartOff: %d != %d", off, textStartOff)) + } + off += int64(ctxt.Arch.PtrSize) // skip runtimeText relocation + 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) + if off != size { + panic(fmt.Sprintf("pcHeader size: %d != %d", off, size)) + } + } + + state.pcheader = state.addGeneratedSym(ctxt, "runtime.pcheader", size, writeHeader) + // Create the runtimeText relocation. + sb := ldr.MakeSymbolUpdater(state.pcheader) + sb.SetAddr(ctxt.Arch, textStartOff, ldr.Lookup("runtime.text", 0)) +} + +// walkFuncs iterates over the funcs, calling a function for each unique +// function and inlined function. +func walkFuncs(ctxt *Link, funcs []loader.Sym, f func(loader.Sym)) { + ldr := ctxt.loader + seen := make(map[loader.Sym]struct{}) + for _, s := range funcs { + if _, ok := seen[s]; !ok { + f(s) + seen[s] = struct{}{} + } + + fi := ldr.FuncInfo(s) + if !fi.Valid() { + continue + } + fi.Preload() + for i, ni := 0, fi.NumInlTree(); i < int(ni); i++ { + call := fi.InlTree(i).Func + if _, ok := seen[call]; !ok { + f(call) + seen[call] = struct{}{} + } + } + } +} + +// 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) + + // The name used by the runtime is the concatenation of the 3 returned strings. + // For regular functions, only one returned string is nonempty. + // For generic functions, we use three parts so that we can print everything + // within the outermost "[]" as "...". + nameParts := func(name string) (string, string, string) { + i := strings.IndexByte(name, '[') + if i < 0 { + return name, "", "" + } + // TODO: use LastIndexByte once the bootstrap compiler is >= Go 1.5. + j := len(name) - 1 + for j > i && name[j] != ']' { + j-- + } + if j <= i { + return name, "", "" + } + return name[:i], "[...]", name[j+1:] + } + + // Write the null terminated strings. + writeFuncNameTab := func(ctxt *Link, s loader.Sym) { + symtab := ctxt.loader.MakeSymbolUpdater(s) + for s, off := range nameOffsets { + a, b, c := nameParts(ctxt.loader.SymName(s)) + o := int64(off) + o = symtab.AddStringAt(o, a) + o = symtab.AddStringAt(o, b) + _ = symtab.AddCStringAt(o, c) + } + } + + // Loop through the CUs, and calculate the size needed. + var size int64 + walkFuncs(ctxt, funcs, func(s loader.Sym) { + nameOffsets[s] = uint32(size) + a, b, c := nameParts(ctxt.loader.SymName(s)) + size += int64(len(a) + len(b) + len(c) + 1) // NULL terminate + }) + + state.funcnametab = state.addGeneratedSym(ctxt, "runtime.funcnametab", size, writeFuncNameTab) + return nameOffsets +} + +// 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 + + // 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. + // + // 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. + // + // TODO: 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) + + // 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 + } + + // Find the maximum file index we've seen. + if cuEntries[cu.PclnIndex] < i+1 { + cuEntries[cu.PclnIndex] = i + 1 // Store max + 1 + } + }) + + // 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) + + 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) + + // Write filetab. + writeFiletab := func(ctxt *Link, s loader.Sym) { + sb := ctxt.loader.MakeSymbolUpdater(s) + + // 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) + } + size += datSize + seen[pcSym] = struct{}{} + } + } + var pcsp, pcline, pcfile, pcinline loader.Sym + var pcdata []loader.Sym + for _, s := range funcs { + fi := ldr.FuncInfo(s) + if !fi.Valid() { + continue + } + fi.Preload() + pcsp, pcfile, pcline, pcinline, pcdata = ldr.PcdataAuxs(s, pcdata) + + pcSyms := []loader.Sym{pcsp, pcfile, pcline} + for _, pcSym := range pcSyms { + saveOffset(pcSym) + } + for _, pcSym := range pcdata { + saveOffset(pcSym) + } + if fi.NumInlTree() > 0 { + saveOffset(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(ldr *loader.Loader, s loader.Sym, fi loader.FuncInfo) uint32 { + if !fi.Valid() { + return 0 + } + numPCData := uint32(ldr.NumPcdata(s)) + if fi.NumInlTree() > 0 { + if numPCData < objabi.PCDATA_InlTreeIndex+1 { + numPCData = objabi.PCDATA_InlTreeIndex + 1 + } + } + return numPCData +} + +// generateFunctab creates the runtime.functab +// +// runtime.functab contains two things: +// +// - pc->func look up table. +// - array of func objects, interleaved with pcdata and funcdata +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) + writePcln := func(ctxt *Link, s loader.Sym) { + ldr := ctxt.loader + sb := ldr.MakeSymbolUpdater(s) + // Write the data. + writePCToFunc(ctxt, sb, funcs, startLocations) + writeFuncs(ctxt, sb, funcs, inlSyms, startLocations, cuOffsets, nameOffsets) + } + state.pclntab = state.addGeneratedSym(ctxt, "runtime.functab", size, writePcln) +} + +// funcData returns the funcdata and offsets for the FuncInfo. +// The funcdata are written into runtime.functab after each func +// object. This is a helper function to make querying the FuncInfo object +// cleaner. +// +// NB: Preload must be called on the FuncInfo before calling. +// NB: fdSyms is used as scratch space. +func funcData(ldr *loader.Loader, s loader.Sym, fi loader.FuncInfo, inlSym loader.Sym, fdSyms []loader.Sym) []loader.Sym { + fdSyms = fdSyms[:0] + if fi.Valid() { + fdSyms = ldr.Funcdata(s, fdSyms) + if fi.NumInlTree() > 0 { + if len(fdSyms) < objabi.FUNCDATA_InlTree+1 { + fdSyms = append(fdSyms, make([]loader.Sym, objabi.FUNCDATA_InlTree+1-len(fdSyms))...) + } + fdSyms[objabi.FUNCDATA_InlTree] = inlSym + } + } + return fdSyms +} + +// 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 offset + // 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*4 + 4) + + // Now find the space for the func objects. We do this in a running manner, + // so that we can find individual starting locations. + for i, s := range funcs { + size = Rnd(size, int64(ctxt.Arch.PtrSize)) + startLocations[i] = uint32(size) + fi := ldr.FuncInfo(s) + size += funcSize + if fi.Valid() { + fi.Preload() + numFuncData := ldr.NumFuncdata(s) + if fi.NumInlTree() > 0 { + if numFuncData < objabi.FUNCDATA_InlTree+1 { + numFuncData = objabi.FUNCDATA_InlTree + 1 + } + } + size += int64(numPCData(ldr, s, fi) * 4) + size += int64(numFuncData * 4) + } + } + + return size, startLocations +} + +// writePCToFunc writes the PC->func lookup table. +func writePCToFunc(ctxt *Link, sb *loader.SymbolBuilder, funcs []loader.Sym, startLocations []uint32) { + ldr := ctxt.loader + textStart := ldr.SymValue(ldr.Lookup("runtime.text", 0)) + pcOff := func(s loader.Sym) uint32 { + off := ldr.SymValue(s) - textStart + if off < 0 { + panic(fmt.Sprintf("expected func %s(%x) to be placed at or after textStart (%x)", ldr.SymName(s), ldr.SymValue(s), textStart)) + } + return uint32(off) + } + for i, s := range funcs { + sb.SetUint32(ctxt.Arch, int64(i*2*4), pcOff(s)) + sb.SetUint32(ctxt.Arch, int64((i*2+1)*4), startLocations[i]) + } + + // Final entry of table is just end pc offset. + lastFunc := funcs[len(funcs)-1] + sb.SetUint32(ctxt.Arch, int64(len(funcs))*2*4, pcOff(lastFunc)+uint32(ldr.SymSize(lastFunc))) +} + +// 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", abiInternalVer) + gofunc := ldr.Lookup("go:func.*", 0) + gofuncBase := ldr.SymValue(gofunc) + textStart := ldr.SymValue(ldr.Lookup("runtime.text", 0)) + funcdata := []loader.Sym{} + var pcsp, pcfile, pcline, pcinline loader.Sym + var pcdata []loader.Sym + + // Write the individual func objects. + for i, s := range funcs { + startLine := int32(0) + fi := ldr.FuncInfo(s) + if fi.Valid() { + fi.Preload() + pcsp, pcfile, pcline, pcinline, pcdata = ldr.PcdataAuxs(s, pcdata) + startLine = fi.StartLine() + } + + off := int64(startLocations[i]) + // entryOff uint32 (offset of func entry PC from textStart) + entryOff := ldr.SymValue(s) - textStart + if entryOff < 0 { + panic(fmt.Sprintf("expected func %s(%x) to be placed before or at textStart (%x)", ldr.SymName(s), ldr.SymValue(s), textStart)) + } + off = sb.SetUint32(ctxt.Arch, off, uint32(entryOff)) + + // nameOff int32 + nameOff, ok := nameOffsets[s] + if !ok { + panic("couldn't find function name offset") + } + off = sb.SetUint32(ctxt.Arch, off, uint32(nameOff)) + + // args int32 + // TODO: Move into funcinfo. + args := uint32(0) + if fi.Valid() { + args = uint32(fi.Args()) + } + off = sb.SetUint32(ctxt.Arch, off, args) + + // deferreturn + deferreturn := computeDeferReturn(ctxt, deferReturnSym, s) + off = sb.SetUint32(ctxt.Arch, off, deferreturn) + + // pcdata + if fi.Valid() { + off = sb.SetUint32(ctxt.Arch, off, uint32(ldr.SymValue(pcsp))) + off = sb.SetUint32(ctxt.Arch, off, uint32(ldr.SymValue(pcfile))) + off = sb.SetUint32(ctxt.Arch, off, uint32(ldr.SymValue(pcline))) + } else { + off += 12 + } + off = sb.SetUint32(ctxt.Arch, off, uint32(numPCData(ldr, s, fi))) + + // Store the offset to compilation unit's file table. + cuIdx := ^uint32(0) + if cu := ldr.SymUnit(s); cu != nil { + cuIdx = cuOffsets[cu.PclnIndex] + } + off = sb.SetUint32(ctxt.Arch, off, cuIdx) + + // startLine int32 + off = sb.SetUint32(ctxt.Arch, off, uint32(startLine)) + + // funcID uint8 + var funcID objabi.FuncID + if fi.Valid() { + funcID = fi.FuncID() + } + off = sb.SetUint8(ctxt.Arch, off, uint8(funcID)) + + // flag uint8 + var flag objabi.FuncFlag + if fi.Valid() { + flag = fi.FuncFlag() + } + off = sb.SetUint8(ctxt.Arch, off, uint8(flag)) + + off += 1 // pad + + // nfuncdata must be the final entry. + funcdata = funcData(ldr, s, fi, 0, funcdata) + off = sb.SetUint8(ctxt.Arch, off, uint8(len(funcdata))) + + // Output the pcdata. + if fi.Valid() { + for j, pcSym := range pcdata { + sb.SetUint32(ctxt.Arch, off+int64(j*4), uint32(ldr.SymValue(pcSym))) + } + if fi.NumInlTree() > 0 { + sb.SetUint32(ctxt.Arch, off+objabi.PCDATA_InlTreeIndex*4, uint32(ldr.SymValue(pcinline))) + } + } + + // Write funcdata refs as offsets from go:func.* and go:funcrel.*. + funcdata = funcData(ldr, s, fi, inlSyms[s], funcdata) + // Missing funcdata will be ^0. See runtime/symtab.go:funcdata. + off = int64(startLocations[i] + funcSize + numPCData(ldr, s, fi)*4) + for j := range funcdata { + dataoff := off + int64(4*j) + fdsym := funcdata[j] + if fdsym == 0 { + sb.SetUint32(ctxt.Arch, dataoff, ^uint32(0)) // ^0 is a sentinel for "no value" + continue + } + + if outer := ldr.OuterSym(fdsym); outer != gofunc { + panic(fmt.Sprintf("bad carrier sym for symbol %s (funcdata %s#%d), want go:func.* got %s", ldr.SymName(fdsym), ldr.SymName(s), j, ldr.SymName(outer))) + } + sb.SetUint32(ctxt.Arch, dataoff, uint32(ldr.SymValue(fdsym)-gofuncBase)) + } + } +} + +// pclntab initializes the pclntab symbol with +// runtime function and file name information. + +// 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. + + state, compUnits, funcs := makePclntab(ctxt, container) + + ldr := ctxt.loader + state.carrier = ldr.LookupOrCreateSym("runtime.pclntab", 0) + ldr.MakeSymbolUpdater(state.carrier).SetType(sym.SPCLNTAB) + ldr.SetAttrReachable(state.carrier, true) + setCarrierSym(sym.SPCLNTAB, state.carrier) + + 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 +} + +func gorootFinal() string { + root := buildcfg.GOROOT + if final := os.Getenv("GOROOT_FINAL"); final != "" { + root = final + } + return root +} + +func expandGoroot(s string) string { + const n = len("$GOROOT") + if len(s) >= n+1 && s[:n] == "$GOROOT" && (s[n] == '/' || s[n] == '\\') { + if final := gorootFinal(); final != "" { + return filepath.ToSlash(filepath.Join(final, s[n:])) + } + } + return s +} + +const ( + BUCKETSIZE = 256 * MINFUNC + SUBBUCKETS = 16 + SUBBUCKETSIZE = BUCKETSIZE / SUBBUCKETS + NOIDX = 0x7fffffff +) + +// findfunctab generates a lookup table to quickly find the containing +// function for a pc. See src/runtime/symtab.go:findfunc for details. +func (ctxt *Link) findfunctab(state *pclntab, container loader.Bitmap) { + ldr := ctxt.loader + + // find min and max address + min := ldr.SymValue(ctxt.Textp[0]) + lastp := ctxt.Textp[len(ctxt.Textp)-1] + max := ldr.SymValue(lastp) + ldr.SymSize(lastp) + + // for each subbucket, compute the minimum of all symbol indexes + // that map to that subbucket. + n := int32((max - min + SUBBUCKETSIZE - 1) / SUBBUCKETSIZE) + + nbuckets := int32((max - min + BUCKETSIZE - 1) / BUCKETSIZE) + + size := 4*int64(nbuckets) + int64(n) + + writeFindFuncTab := func(_ *Link, s loader.Sym) { + t := ldr.MakeSymbolUpdater(s) + + indexes := make([]int32, n) + for i := int32(0); i < n; i++ { + indexes[i] = NOIDX + } + idx := int32(0) + for i, s := range ctxt.Textp { + if !emitPcln(ctxt, s, container) { + continue + } + p := ldr.SymValue(s) + var e loader.Sym + i++ + if i < len(ctxt.Textp) { + e = ctxt.Textp[i] + } + for e != 0 && !emitPcln(ctxt, e, container) && i < len(ctxt.Textp) { + e = ctxt.Textp[i] + i++ + } + q := max + if e != 0 { + q = ldr.SymValue(e) + } + + //print("%d: [%lld %lld] %s\n", idx, p, q, s->name); + for ; p < q; p += SUBBUCKETSIZE { + i = int((p - min) / SUBBUCKETSIZE) + if indexes[i] > idx { + indexes[i] = idx + } + } + + i = int((q - 1 - min) / SUBBUCKETSIZE) + if indexes[i] > idx { + indexes[i] = idx + } + idx++ + } + + // fill in table + for i := int32(0); i < nbuckets; i++ { + base := indexes[i*SUBBUCKETS] + if base == NOIDX { + Errorf(nil, "hole in findfunctab") + } + t.SetUint32(ctxt.Arch, int64(i)*(4+SUBBUCKETS), uint32(base)) + for j := int32(0); j < SUBBUCKETS && i*SUBBUCKETS+j < n; j++ { + idx = indexes[i*SUBBUCKETS+j] + if idx == NOIDX { + Errorf(nil, "hole in findfunctab") + } + if idx-base >= 256 { + Errorf(nil, "too many functions in a findfunc bucket! %d/%d %d %d", i, nbuckets, j, idx-base) + } + + t.SetUint8(ctxt.Arch, int64(i)*(4+SUBBUCKETS)+4+int64(j), uint8(idx-base)) + } + } + } + + state.findfunctab = ctxt.createGeneratorSymbol("runtime.findfunctab", 0, sym.SRODATA, size, writeFindFuncTab) + ldr.SetAttrReachable(state.findfunctab, true) + ldr.SetAttrLocal(state.findfunctab, true) +} + +// findContainerSyms returns a bitmap, indexed by symbol number, where there's +// a 1 for every container symbol. +func (ctxt *Link) findContainerSyms() loader.Bitmap { + ldr := ctxt.loader + container := loader.MakeBitmap(ldr.NSym()) + // Find container symbols and mark them as such. + for _, s := range ctxt.Textp { + outer := ldr.OuterSym(s) + if outer != 0 { + container.Set(outer) + } + } + return container +} |