From 9ae8f71c9431d287893443fa2b7fbdb72a9b56a2 Mon Sep 17 00:00:00 2001 From: Jeremy Faller Date: Mon, 3 Aug 2020 13:19:46 -0400 Subject: [PATCH] [dev.link] cmd/link: stop renumbering files for pclntab generation MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Creates two new symbols: runtime.cutab, and runtime.filetab, and strips the filenames out of runtime.pclntab_old. All stats are for cmd/compile. Time: Pclntab_GC 48.2ms ± 3% 45.5ms ± 9% -5.47% (p=0.004 n=9+9) Alloc/op: Pclntab_GC 30.0MB ± 0% 29.5MB ± 0% -1.88% (p=0.000 n=10+10) Allocs/op: Pclntab_GC 90.4k ± 0% 73.1k ± 0% -19.11% (p=0.000 n=10+10) live-B: Pclntab_GC 29.1M ± 0% 29.2M ± 0% +0.10% (p=0.000 n=10+10) binary sizes: NEW: 18565600 OLD: 18532768 The size differences in the binary are caused by the increased size of the Func objects, and (less likely) some extra alignment padding needed as a result. This is probably the maximum increase in size we'll size from the pclntab reworking. Change-Id: Idd95a9b159fea46f7701cfe6506813b88257fbea Reviewed-on: https://go-review.googlesource.com/c/go/+/246497 Run-TryBot: Jeremy Faller TryBot-Result: Gobot Gobot Reviewed-by: Than McIntosh Reviewed-by: Austin Clements --- src/cmd/link/internal/ld/data.go | 4 + src/cmd/link/internal/ld/link.go | 1 - src/cmd/link/internal/ld/pcln.go | 303 ++++++++++++++++------------- src/cmd/link/internal/ld/symtab.go | 12 +- src/debug/gosym/pclntab.go | 52 +++-- src/runtime/runtime2.go | 7 +- src/runtime/symtab.go | 19 +- 7 files changed, 231 insertions(+), 167 deletions(-) diff --git a/src/cmd/link/internal/ld/data.go b/src/cmd/link/internal/ld/data.go index dc7096ea8c..a551d46403 100644 --- a/src/cmd/link/internal/ld/data.go +++ b/src/cmd/link/internal/ld/data.go @@ -1923,6 +1923,8 @@ func (state *dodataState) allocateDataSections(ctxt *Link) { ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.pclntab", 0), sect) ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.pcheader", 0), sect) ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.funcnametab", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.cutab", 0), sect) + ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.filetab", 0), sect) ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.pclntab_old", 0), sect) ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.epclntab", 0), sect) if ctxt.HeadType == objabi.Haix { @@ -2507,6 +2509,8 @@ func (ctxt *Link) address() []*sym.Segment { ctxt.xdefine("runtime.pclntab", sym.SRODATA, int64(pclntab.Vaddr)) ctxt.defineInternal("runtime.pcheader", sym.SRODATA) ctxt.defineInternal("runtime.funcnametab", sym.SRODATA) + ctxt.defineInternal("runtime.cutab", sym.SRODATA) + ctxt.defineInternal("runtime.filetab", sym.SRODATA) ctxt.defineInternal("runtime.pclntab_old", sym.SRODATA) ctxt.xdefine("runtime.epclntab", sym.SRODATA, int64(pclntab.Vaddr+pclntab.Length)) ctxt.xdefine("runtime.noptrdata", sym.SNOPTRDATA, int64(noptr.Vaddr)) diff --git a/src/cmd/link/internal/ld/link.go b/src/cmd/link/internal/ld/link.go index a2c8552e94..f26d051a49 100644 --- a/src/cmd/link/internal/ld/link.go +++ b/src/cmd/link/internal/ld/link.go @@ -71,7 +71,6 @@ type Link struct { LibraryByPkg map[string]*sym.Library Shlibs []Shlib Textp []loader.Sym - NumFilesyms int Moduledata loader.Sym PackageFile map[string]string diff --git a/src/cmd/link/internal/ld/pcln.go b/src/cmd/link/internal/ld/pcln.go index 30e0bdc839..c7535f6a61 100644 --- a/src/cmd/link/internal/ld/pcln.go +++ b/src/cmd/link/internal/ld/pcln.go @@ -6,16 +6,11 @@ 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" @@ -23,18 +18,13 @@ import ( // 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). +// index for the symbol "runtime.deferreturn", // // 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. @@ -42,9 +32,6 @@ type pclntab struct { // 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 +41,8 @@ type pclntab struct { pcheader loader.Sym funcnametab loader.Sym findfunctab loader.Sym + cutab loader.Sym + filetab 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,6 +53,9 @@ type pclntab struct { // On most platforms this is the number of reachable functions. nfunc int32 + // The number of filenames in runtime.filetab. + nfiles uint32 + // 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. @@ -89,11 +81,6 @@ func makeOldPclnState(ctxt *Link) *oldPclnState { 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 @@ -153,78 +140,6 @@ func ftabaddstring(ftab *loader.SymbolBuilder, s string) int32 { 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 { @@ -308,12 +223,7 @@ 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) + val := call.File nameoff, ok := newState.funcNameOffset[call.Func] if !ok { panic("couldn't find function name offset") @@ -359,11 +269,14 @@ 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.pclntab) } - size := int64(8 + 3*ctxt.Arch.PtrSize) + size := int64(8 + 6*ctxt.Arch.PtrSize) state.pcheader = state.addGeneratedSym(ctxt, "runtime.pcheader", size, writeHeader) } @@ -417,6 +330,139 @@ func (state *pclntab) generateFuncnametab(ctxt *Link, container loader.Bitmap) { state.funcnametab = state.addGeneratedSym(ctxt, "runtime.funcnametab", size, writeFuncNameTab) } +// walkFilenames walks the filenames in the all reachable functions. +func walkFilenames(ctxt *Link, container loader.Bitmap, f func(*sym.CompilationUnit, goobj.CUFileIndex)) { + ldr := ctxt.loader + + // Loop through all functions, finding the filenames we need. + for _, ls := range ctxt.Textp { + s := loader.Sym(ls) + if !emitPcln(ctxt, s, container) { + continue + } + + 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, container loader.Bitmap) []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. + // + // 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) + + // 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, container, 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 +} + // pclntab initializes the pclntab symbol with // runtime function and file name information. @@ -425,7 +471,7 @@ 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: + // As of August 2020, here's the layout of pclntab: // // .gopclntab/__gopclntab [elf/macho section] // runtime.pclntab @@ -438,17 +484,23 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab { // offset to runtime.pclntab_old from beginning of runtime.pcheader // // runtime.funcnametab - // []list of null terminated function names + // []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.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 oldState := makeOldPclnState(ctxt) - state, _ := makePclntab(ctxt, container) + state, compUnits := makePclntab(ctxt, container) ldr := ctxt.loader state.carrier = ldr.LookupOrCreateSym("runtime.pclntab", 0) @@ -461,6 +513,7 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab { state.pclntab = ldr.LookupOrCreateSym("runtime.pclntab_old", 0) state.generatePCHeader(ctxt) state.generateFuncnametab(ctxt, container) + cuOffsets := state.generateFilenameTabs(ctxt, compUnits, container) funcdataBytes := int64(0) ldr.SetCarrierSym(state.pclntab, state.carrier) @@ -583,7 +636,7 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab { // fixed size of struct, checked below off := funcstart - end := funcstart + int32(ctxt.Arch.PtrSize) + 3*4 + 5*4 + int32(len(pcdata))*4 + int32(len(funcdata))*int32(ctxt.Arch.PtrSize) + end := funcstart + int32(ctxt.Arch.PtrSize) + 3*4 + 6*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 } @@ -616,17 +669,6 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab { 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 { @@ -641,15 +683,12 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab { 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 = int32(ftab.SetUint32(ctxt.Arch, int64(off), cuIdx)) // funcID uint8 var funcID objabi.FuncID @@ -658,6 +697,8 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab { } off = int32(ftab.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 { @@ -694,26 +735,8 @@ func (ctxt *Link) pclntab(container loader.Bitmap) *pclntab { // 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)) - - 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))) - } - ftab.SetSize(int64(len(ftab.Data()))) - ctxt.NumFilesyms = len(oldState.numberedFiles) - if ctxt.Debugvlog != 0 { ctxt.Logf("pclntab=%d bytes, funcdata total %d bytes\n", ftab.Size(), funcdataBytes) } diff --git a/src/cmd/link/internal/ld/symtab.go b/src/cmd/link/internal/ld/symtab.go index bc880955b8..d05b98f04a 100644 --- a/src/cmd/link/internal/ld/symtab.go +++ b/src/cmd/link/internal/ld/symtab.go @@ -619,6 +619,14 @@ func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind { moduledata.AddAddr(ctxt.Arch, pcln.funcnametab) moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.funcnametab))) moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.funcnametab))) + // The cutab slice + moduledata.AddAddr(ctxt.Arch, pcln.cutab) + moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.cutab))) + moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.cutab))) + // The filetab slice + moduledata.AddAddr(ctxt.Arch, pcln.filetab) + moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.filetab))) + moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.filetab))) // The pclntab slice moduledata.AddAddr(ctxt.Arch, pcln.pclntab) moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pcln.pclntab))) @@ -627,10 +635,6 @@ func (ctxt *Link) symtab(pcln *pclntab) []sym.SymKind { moduledata.AddAddr(ctxt.Arch, pcln.pclntab) moduledata.AddUint(ctxt.Arch, uint64(pcln.nfunc+1)) moduledata.AddUint(ctxt.Arch, uint64(pcln.nfunc+1)) - // The filetab slice - moduledata.AddAddrPlus(ctxt.Arch, pcln.pclntab, int64(pcln.filetabOffset)) - moduledata.AddUint(ctxt.Arch, uint64(ctxt.NumFilesyms)+1) - moduledata.AddUint(ctxt.Arch, uint64(ctxt.NumFilesyms)+1) // findfunctab moduledata.AddAddr(ctxt.Arch, pcln.findfunctab) // minpc, maxpc diff --git a/src/debug/gosym/pclntab.go b/src/debug/gosym/pclntab.go index e5c50520fc..e383ea460a 100644 --- a/src/debug/gosym/pclntab.go +++ b/src/debug/gosym/pclntab.go @@ -53,6 +53,7 @@ type LineTable struct { quantum uint32 ptrsize uint32 funcnametab []byte + cutab []byte funcdata []byte functab []byte nfunctab uint32 @@ -223,17 +224,18 @@ func (t *LineTable) parsePclnTab() { switch possibleVersion { case ver116: t.nfunctab = uint32(t.uintptr(t.Data[8:])) - offset := t.uintptr(t.Data[8+t.ptrsize:]) + t.nfiletab = uint32(t.uintptr(t.Data[8+t.ptrsize:])) + offset := t.uintptr(t.Data[8+2*t.ptrsize:]) t.funcnametab = t.Data[offset:] - offset = t.uintptr(t.Data[8+2*t.ptrsize:]) + offset = t.uintptr(t.Data[8+3*t.ptrsize:]) + t.cutab = t.Data[offset:] + offset = t.uintptr(t.Data[8+4*t.ptrsize:]) + t.filetab = t.Data[offset:] + offset = t.uintptr(t.Data[8+5*t.ptrsize:]) t.funcdata = t.Data[offset:] t.functab = t.Data[offset:] functabsize := t.nfunctab*2*t.ptrsize + t.ptrsize - fileoff := t.binary.Uint32(t.functab[functabsize:]) - t.filetab = t.functab[fileoff:] t.functab = t.functab[:functabsize] - t.nfiletab = t.binary.Uint32(t.filetab) - t.filetab = t.filetab[:t.nfiletab*4] case ver12: t.nfunctab = uint32(t.uintptr(t.Data[8:])) t.funcdata = t.Data @@ -330,17 +332,22 @@ func (t *LineTable) funcName(off uint32) string { return s } -// string returns a Go string found at off. -func (t *LineTable) string(off uint32) string { +// stringFrom returns a Go string found at off from a position. +func (t *LineTable) stringFrom(arr []byte, off uint32) string { if s, ok := t.strings[off]; ok { return s } - i := bytes.IndexByte(t.funcdata[off:], 0) - s := string(t.funcdata[off : off+uint32(i)]) + i := bytes.IndexByte(arr[off:], 0) + s := string(arr[off : off+uint32(i)]) t.strings[off] = s return s } +// string returns a Go string found at off. +func (t *LineTable) string(off uint32) string { + return t.stringFrom(t.funcdata, off) +} + // step advances to the next pc, value pair in the encoded table. func (t *LineTable) step(p *[]byte, pc *uint64, val *int32, first bool) bool { uvdelta := t.readvarint(p) @@ -453,7 +460,15 @@ func (t *LineTable) go12PCToFile(pc uint64) (file string) { if fno <= 0 { return "" } - return t.string(t.binary.Uint32(t.filetab[4*fno:])) + if t.version == ver12 { + return t.string(t.binary.Uint32(t.filetab[4*fno:])) + } + // Go ≥ 1.16 + cuoff := t.binary.Uint32(f[t.ptrsize+7*4:]) + if fnoff := t.binary.Uint32(t.cutab[(cuoff+uint32(fno))*4:]); fnoff != ^uint32(0) { + return t.stringFrom(t.filetab, fnoff) + } + return "" } // go12LineToPC maps a (file, line) pair to a program counter for the Go 1.2 pcln table. @@ -496,9 +511,18 @@ func (t *LineTable) initFileMap() { } m := make(map[string]uint32) - for i := uint32(1); i < t.nfiletab; i++ { - s := t.string(t.binary.Uint32(t.filetab[4*i:])) - m[s] = i + if t.version == ver12 { + for i := uint32(1); i < t.nfiletab; i++ { + s := t.string(t.binary.Uint32(t.filetab[4*i:])) + m[s] = i + } + } else { + var pos uint32 + for i := uint32(1); i < t.nfiletab; i++ { + s := t.stringFrom(t.filetab, pos) + pos += uint32(len(s) + 1) + m[s] = i + } } t.fileMap = m } diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go index 0bddcaa789..5a79c7e6ec 100644 --- a/src/runtime/runtime2.go +++ b/src/runtime/runtime2.go @@ -804,9 +804,10 @@ type _func struct { pcfile int32 pcln int32 npcdata int32 - cuIndex uint16 // TODO(jfaller): 16 bits is never enough, make this larger. - funcID funcID // set for certain special runtime functions - nfuncdata uint8 // must be last + cuOffset uint32 // runtime.cutab offset of this function's CU + funcID funcID // set for certain special runtime functions + _ [2]byte // pad + nfuncdata uint8 // must be last } // Pseudo-Func that is returned for PCs that occur in inlined code. diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go index ddb5ea82b4..fbd9315522 100644 --- a/src/runtime/symtab.go +++ b/src/runtime/symtab.go @@ -334,14 +334,17 @@ const ( funcID_wrapper // any autogenerated code (hash/eq algorithms, method wrappers, etc.) ) -// PCHeader holds data used by the pclntab lookups. +// pcHeader holds data used by the pclntab lookups. type pcHeader struct { magic uint32 // 0xFFFFFFFA pad1, pad2 uint8 // 0,0 minLC uint8 // min instruction size ptrSize uint8 // size of a ptr in bytes nfunc int // number of functions in the module + nfiles uint // number of entries in the file tab. funcnameOffset uintptr // offset to the funcnametab variable from pcHeader + cuOffset uintptr // offset to the cutab variable from pcHeader + filetabOffset uintptr // offset to the filetab variable from pcHeader pclnOffset uintptr // offset to the pclntab variable from pcHeader } @@ -353,9 +356,10 @@ type pcHeader struct { type moduledata struct { pcHeader *pcHeader funcnametab []byte + cutab []uint32 + filetab []byte pclntable []byte ftab []functab - filetab []uint32 findfunctab uintptr minpc, maxpc uintptr @@ -851,7 +855,12 @@ func funcfile(f funcInfo, fileno int32) string { if !f.valid() { return "?" } - return gostringnocopy(&datap.pclntable[datap.filetab[fileno]]) + // Make sure the cu index and file offset are valid + if fileoff := datap.cutab[f.cuOffset+uint32(fileno)]; fileoff != ^uint32(0) { + return gostringnocopy(&datap.filetab[fileoff]) + } + // pcln section is corrupt. + return "?" } func funcline1(f funcInfo, targetpc uintptr, strict bool) (file string, line int32) { @@ -865,7 +874,7 @@ func funcline1(f funcInfo, targetpc uintptr, strict bool) (file string, line int // print("looking for ", hex(targetpc), " in ", funcname(f), " got file=", fileno, " line=", lineno, "\n") return "?", 0 } - file = gostringnocopy(&datap.pclntable[datap.filetab[fileno]]) + file = funcfile(f, fileno) return } @@ -1005,7 +1014,7 @@ type inlinedCall struct { parent int16 // index of parent in the inltree, or < 0 funcID funcID // type of the called function _ byte - file int32 // fileno index into filetab + file int32 // perCU file index for inlined call. See cmd/link:pcln.go line int32 // line number of the call site func_ int32 // offset into pclntab for name of called function parentPc int32 // position of an instruction whose source position is the call site (offset from entry) -- 2.48.1