]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.link] cmd/link: stop renumbering files for pclntab generation
authorJeremy Faller <jeremy@golang.org>
Mon, 3 Aug 2020 17:19:46 +0000 (13:19 -0400)
committerJeremy Faller <jeremy@golang.org>
Wed, 12 Aug 2020 17:14:51 +0000 (17:14 +0000)
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 <jeremy@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Than McIntosh <thanm@google.com>
Reviewed-by: Austin Clements <austin@google.com>
src/cmd/link/internal/ld/data.go
src/cmd/link/internal/ld/link.go
src/cmd/link/internal/ld/pcln.go
src/cmd/link/internal/ld/symtab.go
src/debug/gosym/pclntab.go
src/runtime/runtime2.go
src/runtime/symtab.go

index dc7096ea8cc80940d08a930f87160d8efda3f84f..a551d464038ec6a6293eeadc1de5894ed3191aa7 100644 (file)
@@ -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))
index a2c8552e940be21868bcaa8110aa6c0e93d4b951..f26d051a491bbc3671cdb3c81a2a94f6000e6966 100644 (file)
@@ -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
index 30e0bdc839c9b0a5ff4d540876b39642906024d0..c7535f6a61bec899a0a7f173c4108e39abc7563f 100644 (file)
@@ -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)
        }
index bc880955b8ca965a6b4b09af8a83cc1bbc77e204..d05b98f04a66ad453c16c1aecb1ab9c1326f9255 100644 (file)
@@ -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
index e5c50520fca2db654b5d186379ddb1e73708f2bf..e383ea460a49b08dbc74ed54f5dfb0dbd7931b02 100644 (file)
@@ -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
 }
index 0bddcaa789b168d5ebb60f53f0325d73dc2a1bc3..5a79c7e6ec725822881536831a82054a38b29fa8 100644 (file)
@@ -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.
index ddb5ea82b4640389c3acca913f90846ccfef8fe5..fbd931552235ee7ebd451c4b45e3eea8854fa064 100644 (file)
@@ -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)