| // 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, "", "" |
| } |
| j := strings.LastIndexByte(name, ']') |
| 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 |
| } |