]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.link] cmd/link: add runtime.pcheader
authorJeremy Faller <jeremy@golang.org>
Wed, 24 Jun 2020 18:30:16 +0000 (14:30 -0400)
committerJeremy Faller <jeremy@golang.org>
Thu, 30 Jul 2020 19:36:06 +0000 (19:36 +0000)
As of July 2020, a fair amount of the new linker's live memory, and
runtime is spent generating pclntab. In an effort to streamline that
code, this change starts breaking up the generation of runtime.pclntab
into smaller chunks that can run later in a link. These changes are
described in an (as yet not widely distributed) document that lays out
an improved format. Largely the work consists of breaking up
runtime.pclntab into smaller pieces, stopping much of the data
rewriting, and getting runtime.pclntab into a form where we can reason
about its size and look to shrink it. This change is the first part of
that work -- just pulling out the header, and demonstrating where a
majority of that work will be.

Change-Id: I65618d0d0c780f7e5977c9df4abdbd1696fedfcb
Reviewed-on: https://go-review.googlesource.com/c/go/+/241598
Run-TryBot: Jeremy Faller <jeremy@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Reviewed-by: Austin Clements <austin@google.com>
src/cmd/link/internal/ld/data.go
src/cmd/link/internal/ld/pcln.go
src/cmd/link/internal/ld/symtab.go
src/cmd/link/internal/loader/symbolbuilder.go
src/debug/gosym/pclntab.go
src/debug/gosym/pclntab_test.go
src/debug/gosym/testdata/pcln115.gz [new file with mode: 0644]
src/runtime/symtab.go

index 9329e32e8c144b4b7a08e0851e66df857d4cbc4a..05ed022a74e8fac36ce0773e9b4c3fb824a581b9 100644 (file)
@@ -1921,6 +1921,8 @@ func (state *dodataState) allocateDataSections(ctxt *Link) {
        /* gopclntab */
        sect = state.allocateNamedSectionAndAssignSyms(seg, genrelrosecname(".gopclntab"), sym.SPCLNTAB, sym.SRODATA, relroSecPerm)
        ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.pclntab", 0), sect)
+       ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.pcheader", 0), sect)
+       ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.pclntab_old", 0), sect)
        ldr.SetSymSect(ldr.LookupOrCreateSym("runtime.epclntab", 0), sect)
 
        // 6g uses 4-byte relocation offsets, so the entire segment must fit in 32 bits.
@@ -2477,6 +2479,8 @@ func (ctxt *Link) address() []*sym.Segment {
        ctxt.xdefine("runtime.symtab", sym.SRODATA, int64(symtab.Vaddr))
        ctxt.xdefine("runtime.esymtab", sym.SRODATA, int64(symtab.Vaddr+symtab.Length))
        ctxt.xdefine("runtime.pclntab", sym.SRODATA, int64(pclntab.Vaddr))
+       pcvar := ctxt.xdefine("runtime.pcheader", sym.SRODATA, int64(pclntab.Vaddr))
+       ctxt.xdefine("runtime.pclntab_old", sym.SRODATA, int64(pclntab.Vaddr)+ldr.SymSize(pcvar))
        ctxt.xdefine("runtime.epclntab", sym.SRODATA, int64(pclntab.Vaddr+pclntab.Length))
        ctxt.xdefine("runtime.noptrdata", sym.SNOPTRDATA, int64(noptr.Vaddr))
        ctxt.xdefine("runtime.enoptrdata", sym.SNOPTRDATA, int64(noptr.Vaddr+noptr.Length))
index 71eaed1a54aadfeea2e246bf16c147c82890537a..5ea210c139fc1d66c18b1931672d4ef98de6f011 100644 (file)
@@ -227,13 +227,41 @@ func (state *pclnState) genInlTreeSym(fi loader.FuncInfo, arch *sys.Arch) loader
        return its
 }
 
+// generatePCHeader creates the runtime.pcheader symbol, setting it up as a
+// generator to fill in its data later.
+func generatePCHeader(ctxt *Link, carrier *loader.SymbolBuilder, pclntabSym loader.Sym) {
+       ldr := ctxt.loader
+       writeHeader := func(ctxt *Link, s loader.Sym) {
+               ldr := ctxt.loader
+               header := ctxt.loader.MakeSymbolUpdater(s)
+
+               // Check symbol order.
+               diff := ldr.SymValue(pclntabSym) - ldr.SymValue(s)
+               if diff <= 0 {
+                       panic(fmt.Sprintf("expected runtime.pcheader(%x) to be placed before runtime.pclntab(%x)", ldr.SymValue(s), ldr.SymValue(pclntabSym)))
+               }
+
+               // Write header.
+               // Keep in sync with runtime/symtab.go:pcHeader.
+               header.SetUint32(ctxt.Arch, 0, 0xfffffffa)
+               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(pclntabNfunc))
+               header.SetUintptr(ctxt.Arch, off, uintptr(diff))
+       }
+
+       size := int64(8 + 2*ctxt.Arch.PtrSize)
+       s := ctxt.createGeneratorSymbol("runtime.pcheader", 0, sym.SPCLNTAB, size, writeHeader)
+       ldr.SetAttrReachable(s, true)
+       ldr.SetCarrierSym(s, carrier.Sym())
+}
+
 // pclntab initializes the pclntab symbol with
 // runtime function and file name information.
 
 // These variables are used to initialize runtime.firstmoduledata, see symtab.go:symtab.
 var pclntabNfunc int32
 var pclntabFiletabOffset int32
-var pclntabPclntabOffset int32
 var pclntabFirstFunc loader.Sym
 var pclntabLastFunc loader.Sym
 
@@ -242,22 +270,46 @@ var pclntabLastFunc loader.Sym
 // symbols, e.g. the set of all symbols X such that Outer(S) = X for
 // some other text symbol S.
 func (ctxt *Link) pclntab() loader.Bitmap {
-       funcdataBytes := int64(0)
+       // Go 1.2's symtab layout is documented in golang.org/s/go12symtab, but the
+       // layout and data has changed since that time.
+       //
+       // As of July 2020, here's the layout of pclntab:
+       //
+       //  .gopclntab/__gopclntab [elf/macho section]
+       //    runtime.pclntab
+       //      Carrier symbol for the entire pclntab section.
+       //
+       //      runtime.pcheader  (see: runtime/symtab.go:pcHeader)
+       //        8-byte magic
+       //        nfunc [thearch.ptrsize bytes]
+       //        offset to runtime.pclntab_old from beginning of runtime.pcheader
+       //
+       //      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, function names, pcdata tables.
+       //        filetable
+
        ldr := ctxt.loader
-       ftabsym := ldr.LookupOrCreateSym("runtime.pclntab", 0)
-       ftab := ldr.MakeSymbolUpdater(ftabsym)
+       carrier := ldr.CreateSymForUpdate("runtime.pclntab", 0)
+       carrier.SetType(sym.SPCLNTAB)
+       carrier.SetReachable(true)
+
+       // runtime.pclntab_old is just a placeholder,and will eventually be deleted.
+       // It contains the pieces of runtime.pclntab that haven't moved to a more
+       // ration form.
+       pclntabSym := ldr.LookupOrCreateSym("runtime.pclntab_old", 0)
+       generatePCHeader(ctxt, carrier, pclntabSym)
+
+       funcdataBytes := int64(0)
+       ldr.SetCarrierSym(pclntabSym, carrier.Sym())
+       ftab := ldr.MakeSymbolUpdater(pclntabSym)
        ftab.SetType(sym.SPCLNTAB)
-       ldr.SetAttrReachable(ftabsym, true)
+       ftab.SetReachable(true)
 
        state := makepclnState(ctxt)
 
-       // See golang.org/s/go12symtab for the format. Briefly:
-       //      8-byte header
-       //      nfunc [thearch.ptrsize bytes]
-       //      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]
-
        // Find container symbols and mark them as such.
        for _, s := range ctxt.Textp {
                outer := ldr.OuterSym(s)
@@ -290,12 +342,7 @@ func (ctxt *Link) pclntab() loader.Bitmap {
        }
 
        pclntabNfunc = nfunc
-       ftab.Grow(8 + int64(ctxt.Arch.PtrSize) + int64(nfunc)*2*int64(ctxt.Arch.PtrSize) + int64(ctxt.Arch.PtrSize) + 4)
-       ftab.SetUint32(ctxt.Arch, 0, 0xfffffffb)
-       ftab.SetUint8(ctxt.Arch, 6, uint8(ctxt.Arch.MinLC))
-       ftab.SetUint8(ctxt.Arch, 7, uint8(ctxt.Arch.PtrSize))
-       ftab.SetUint(ctxt.Arch, 8, uint64(nfunc))
-       pclntabPclntabOffset = int32(8 + ctxt.Arch.PtrSize)
+       ftab.Grow(int64(nfunc)*2*int64(ctxt.Arch.PtrSize) + int64(ctxt.Arch.PtrSize) + 4)
 
        szHint := len(ctxt.Textp) * 2
        funcnameoff := make(map[string]int32, szHint)
@@ -360,8 +407,8 @@ func (ctxt *Link) pclntab() loader.Bitmap {
                        // invalid funcoff value to mark the hole. See also
                        // runtime/symtab.go:findfunc
                        prevFuncSize := int64(ldr.SymSize(prevFunc))
-                       setAddr(ftab, ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize), prevFunc, prevFuncSize)
-                       ftab.SetUint(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), ^uint64(0))
+                       setAddr(ftab, ctxt.Arch, int64(nfunc)*2*int64(ctxt.Arch.PtrSize), prevFunc, prevFuncSize)
+                       ftab.SetUint(ctxt.Arch, int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), ^uint64(0))
                        nfunc++
                }
                prevFunc = s
@@ -410,8 +457,8 @@ func (ctxt *Link) pclntab() loader.Bitmap {
                funcstart := int32(dSize)
                funcstart += int32(-dSize) & (int32(ctxt.Arch.PtrSize) - 1) // align to ptrsize
 
-               setAddr(ftab, ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize), s, 0)
-               ftab.SetUint(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), uint64(funcstart))
+               setAddr(ftab, ctxt.Arch, int64(nfunc)*2*int64(ctxt.Arch.PtrSize), s, 0)
+               ftab.SetUint(ctxt.Arch, int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), uint64(funcstart))
 
                // Write runtime._func. Keep in sync with ../../../../runtime/runtime2.go:/_func
                // and package debug/gosym.
@@ -523,14 +570,14 @@ func (ctxt *Link) pclntab() loader.Bitmap {
        last := ctxt.Textp[len(ctxt.Textp)-1]
        pclntabLastFunc = last
        // Final entry of table is just end pc.
-       setAddr(ftab, ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize), last, ldr.SymSize(last))
+       setAddr(ftab, ctxt.Arch, int64(nfunc)*2*int64(ctxt.Arch.PtrSize), last, ldr.SymSize(last))
 
        // Start file table.
        dSize := len(ftab.Data())
        start := int32(dSize)
        start += int32(-dSize) & (int32(ctxt.Arch.PtrSize) - 1)
        pclntabFiletabOffset = start
-       ftab.SetUint32(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), uint32(start))
+       ftab.SetUint32(ctxt.Arch, int64(nfunc)*2*int64(ctxt.Arch.PtrSize)+int64(ctxt.Arch.PtrSize), uint32(start))
 
        nf := len(state.numberedFiles)
        ftab.Grow(int64(start) + int64((nf+1)*4))
index fddf85f1c52e7b8a789477cc028d70f658a1b22f..15fa162c601eba75838fb0f39c5f6c106101f559 100644 (file)
@@ -607,13 +607,15 @@ func (ctxt *Link) symtab() []sym.SymKind {
        // the definition of moduledata in runtime/symtab.go.
        // This code uses several global variables that are set by pcln.go:pclntab.
        moduledata := ldr.MakeSymbolUpdater(ctxt.Moduledata)
-       pclntab := ldr.Lookup("runtime.pclntab", 0)
+       // The pcHeader
+       moduledata.AddAddr(ctxt.Arch, ldr.Lookup("runtime.pcheader", 0))
        // The pclntab slice
+       pclntab := ldr.Lookup("runtime.pclntab_old", 0)
        moduledata.AddAddr(ctxt.Arch, pclntab)
        moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pclntab)))
        moduledata.AddUint(ctxt.Arch, uint64(ldr.SymSize(pclntab)))
        // The ftab slice
-       moduledata.AddAddrPlus(ctxt.Arch, pclntab, int64(pclntabPclntabOffset))
+       moduledata.AddAddr(ctxt.Arch, pclntab)
        moduledata.AddUint(ctxt.Arch, uint64(pclntabNfunc+1))
        moduledata.AddUint(ctxt.Arch, uint64(pclntabNfunc+1))
        // The filetab slice
index 5b07d6e884682f7bcff165ffd37450a223f66834..314111d5ea6527148b963dd48ae22bbb69064b97 100644 (file)
@@ -287,6 +287,10 @@ func (sb *SymbolBuilder) SetUint(arch *sys.Arch, r int64, v uint64) int64 {
        return sb.setUintXX(arch, r, v, int64(arch.PtrSize))
 }
 
+func (sb *SymbolBuilder) SetUintptr(arch *sys.Arch, r int64, v uintptr) int64 {
+       return sb.setUintXX(arch, r, uint64(v), int64(arch.PtrSize))
+}
+
 func (sb *SymbolBuilder) SetAddrPlus(arch *sys.Arch, off int64, tgt Sym, add int64) int64 {
        if sb.Type() == 0 {
                sb.SetType(sym.SDATA)
index 7e54a943510947744270a744eb3ff60934c5a3ce..8c7ace17cd476ad71ae7805e80198f9788a3de68 100644 (file)
@@ -14,6 +14,16 @@ import (
        "sync"
 )
 
+// version of the pclntab
+type version int
+
+const (
+       verUnknown version = iota
+       ver11
+       ver12
+       ver116
+)
+
 // A LineTable is a data structure mapping program counters to line numbers.
 //
 // In Go 1.1 and earlier, each function (represented by a Func) had its own LineTable,
@@ -32,12 +42,17 @@ type LineTable struct {
        PC   uint64
        Line int
 
-       // Go 1.2 state
-       mu       sync.Mutex
-       go12     int // is this in Go 1.2 format? -1 no, 0 unknown, 1 yes
+       // This mutex is used to keep parsing of pclntab synchronous.
+       mu sync.Mutex
+
+       // Contains the version of the pclntab section.
+       version version
+
+       // Go 1.2/1.16 state
        binary   binary.ByteOrder
        quantum  uint32
        ptrsize  uint32
+       funcdata []byte
        functab  []byte
        nfunctab uint32
        filetab  []byte
@@ -140,11 +155,12 @@ func NewLineTable(data []byte, text uint64) *LineTable {
 
 // isGo12 reports whether this is a Go 1.2 (or later) symbol table.
 func (t *LineTable) isGo12() bool {
-       t.go12Init()
-       return t.go12 == 1
+       t.parsePclnTab()
+       return t.version >= ver12
 }
 
 const go12magic = 0xfffffffb
+const go116magic = 0xfffffffa
 
 // uintptr returns the pointer-sized value encoded at b.
 // The pointer size is dictated by the table being read.
@@ -155,49 +171,79 @@ func (t *LineTable) uintptr(b []byte) uint64 {
        return t.binary.Uint64(b)
 }
 
-// go12init initializes the Go 1.2 metadata if t is a Go 1.2 symbol table.
-func (t *LineTable) go12Init() {
+// parsePclnTab parses the pclntab, setting the version.
+func (t *LineTable) parsePclnTab() {
        t.mu.Lock()
        defer t.mu.Unlock()
-       if t.go12 != 0 {
+       if t.version != verUnknown {
                return
        }
 
+       // Note that during this function, setting the version is the last thing we do.
+       // If we set the version too early, and parsing failed (likely as a panic on
+       // slice lookups), we'd have a mistaken version.
+       //
+       // Error paths through this code will default the version to 1.1.
+       t.version = ver11
+
        defer func() {
-               // If we panic parsing, assume it's not a Go 1.2 symbol table.
+               // If we panic parsing, assume it's a Go 1.1 pclntab.
                recover()
        }()
 
        // Check header: 4-byte magic, two zeros, pc quantum, pointer size.
-       t.go12 = -1 // not Go 1.2 until proven otherwise
        if len(t.Data) < 16 || t.Data[4] != 0 || t.Data[5] != 0 ||
                (t.Data[6] != 1 && t.Data[6] != 2 && t.Data[6] != 4) || // pc quantum
                (t.Data[7] != 4 && t.Data[7] != 8) { // pointer size
                return
        }
 
-       switch uint32(go12magic) {
-       case binary.LittleEndian.Uint32(t.Data):
-               t.binary = binary.LittleEndian
-       case binary.BigEndian.Uint32(t.Data):
-               t.binary = binary.BigEndian
+       var possibleVersion version
+       leMagic := binary.LittleEndian.Uint32(t.Data)
+       beMagic := binary.BigEndian.Uint32(t.Data)
+       switch {
+       case leMagic == go12magic:
+               t.binary, possibleVersion = binary.LittleEndian, ver12
+       case beMagic == go12magic:
+               t.binary, possibleVersion = binary.BigEndian, ver12
+       case leMagic == go116magic:
+               t.binary, possibleVersion = binary.LittleEndian, ver116
+       case beMagic == go116magic:
+               t.binary, possibleVersion = binary.BigEndian, ver116
        default:
                return
        }
 
+       // quantum and ptrSize are the same between 1.2 and 1.16
        t.quantum = uint32(t.Data[6])
        t.ptrsize = uint32(t.Data[7])
 
-       t.nfunctab = uint32(t.uintptr(t.Data[8:]))
-       t.functab = t.Data[8+t.ptrsize:]
-       functabsize := t.nfunctab*2*t.ptrsize + t.ptrsize
-       fileoff := t.binary.Uint32(t.functab[functabsize:])
-       t.functab = t.functab[:functabsize]
-       t.filetab = t.Data[fileoff:]
-       t.nfiletab = t.binary.Uint32(t.filetab)
-       t.filetab = t.filetab[:t.nfiletab*4]
-
-       t.go12 = 1 // so far so good
+       switch possibleVersion {
+       case ver116:
+               t.nfunctab = uint32(t.uintptr(t.Data[8:]))
+               offset := t.uintptr(t.Data[8+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
+               t.functab = t.Data[8+t.ptrsize:]
+               functabsize := t.nfunctab*2*t.ptrsize + t.ptrsize
+               fileoff := t.binary.Uint32(t.functab[functabsize:])
+               t.functab = t.functab[:functabsize]
+               t.filetab = t.Data[fileoff:]
+               t.nfiletab = t.binary.Uint32(t.filetab)
+               t.filetab = t.filetab[:t.nfiletab*4]
+       default:
+               panic("unreachable")
+       }
+       t.version = possibleVersion
 }
 
 // go12Funcs returns a slice of Funcs derived from the Go 1.2 pcln table.
@@ -213,7 +259,7 @@ func (t *LineTable) go12Funcs() []Func {
                f := &funcs[i]
                f.Entry = t.uintptr(t.functab[2*i*int(t.ptrsize):])
                f.End = t.uintptr(t.functab[(2*i+2)*int(t.ptrsize):])
-               info := t.Data[t.uintptr(t.functab[(2*i+1)*int(t.ptrsize):]):]
+               info := t.funcdata[t.uintptr(t.functab[(2*i+1)*int(t.ptrsize):]):]
                f.LineTable = t
                f.FrameSize = int(t.binary.Uint32(info[t.ptrsize+2*4:]))
                f.Sym = &Sym{
@@ -241,7 +287,7 @@ func (t *LineTable) findFunc(pc uint64) []byte {
                m := nf / 2
                fm := f[2*t.ptrsize*m:]
                if t.uintptr(fm) <= pc && pc < t.uintptr(fm[2*t.ptrsize:]) {
-                       return t.Data[t.uintptr(fm[t.ptrsize:]):]
+                       return t.funcdata[t.uintptr(fm[t.ptrsize:]):]
                } else if pc < t.uintptr(fm) {
                        nf = m
                } else {
@@ -273,8 +319,8 @@ func (t *LineTable) string(off uint32) string {
        if s, ok := t.strings[off]; ok {
                return s
        }
-       i := bytes.IndexByte(t.Data[off:], 0)
-       s := string(t.Data[off : off+uint32(i)])
+       i := bytes.IndexByte(t.funcdata[off:], 0)
+       s := string(t.funcdata[off : off+uint32(i)])
        t.strings[off] = s
        return s
 }
@@ -301,7 +347,7 @@ func (t *LineTable) step(p *[]byte, pc *uint64, val *int32, first bool) bool {
 // off is the offset to the beginning of the pc-value table,
 // and entry is the start PC for the corresponding function.
 func (t *LineTable) pcvalue(off uint32, entry, targetpc uint64) int32 {
-       p := t.Data[off:]
+       p := t.funcdata[off:]
 
        val := int32(-1)
        pc := entry
@@ -324,8 +370,8 @@ func (t *LineTable) findFileLine(entry uint64, filetab, linetab uint32, filenum,
                return 0
        }
 
-       fp := t.Data[filetab:]
-       fl := t.Data[linetab:]
+       fp := t.funcdata[filetab:]
+       fl := t.funcdata[linetab:]
        fileVal := int32(-1)
        filePC := entry
        lineVal := int32(-1)
@@ -412,7 +458,7 @@ func (t *LineTable) go12LineToPC(file string, line int) (pc uint64) {
        // If this turns out to be a bottleneck, we could build a map[int32][]int32
        // mapping file number to a list of functions with code from that file.
        for i := uint32(0); i < t.nfunctab; i++ {
-               f := t.Data[t.uintptr(t.functab[2*t.ptrsize*i+t.ptrsize:]):]
+               f := t.funcdata[t.uintptr(t.functab[2*t.ptrsize*i+t.ptrsize:]):]
                entry := t.uintptr(f)
                filetab := t.binary.Uint32(f[t.ptrsize+4*4:])
                linetab := t.binary.Uint32(f[t.ptrsize+5*4:])
index 6baa53defd15a8048ebb93bbf94d210190d201fb..33772c7813655842f52d73425e3de1aa18a8a96d 100644 (file)
@@ -5,6 +5,8 @@
 package gosym
 
 import (
+       "bytes"
+       "compress/gzip"
        "debug/elf"
        "internal/testenv"
        "io/ioutil"
@@ -264,3 +266,51 @@ func TestPCLine(t *testing.T) {
                off = pc + 1 - text.Addr
        }
 }
+
+// Test that we can parse a pclntab from 1.15.
+// The file was compiled in /tmp/hello.go:
+// [BEGIN]
+// package main
+//
+// func main() {
+//    println("hello")
+// }
+// [END]
+func Test115PclnParsing(t *testing.T) {
+       zippedDat, err := ioutil.ReadFile("testdata/pcln115.gz")
+       if err != nil {
+               t.Fatal(err)
+       }
+       var gzReader *gzip.Reader
+       gzReader, err = gzip.NewReader(bytes.NewBuffer(zippedDat))
+       if err != nil {
+               t.Fatal(err)
+       }
+       var dat []byte
+       dat, err = ioutil.ReadAll(gzReader)
+       if err != nil {
+               t.Fatal(err)
+       }
+       const textStart = 0x1001000
+       pcln := NewLineTable(dat, textStart)
+       var tab *Table
+       tab, err = NewTable(nil, pcln)
+       if err != nil {
+               t.Fatal(err)
+       }
+       var f *Func
+       var pc uint64
+       pc, f, err = tab.LineToPC("/tmp/hello.go", 3)
+       if err != nil {
+               t.Fatal(err)
+       }
+       if pcln.version != ver12 {
+               t.Fatal("Expected pcln to parse as an older version")
+       }
+       if pc != 0x105c280 {
+               t.Fatalf("expect pc = 0x105c280, got 0x%x", pc)
+       }
+       if f.Name != "main.main" {
+               t.Fatalf("expected to parse name as main.main, got %v", f.Name)
+       }
+}
diff --git a/src/debug/gosym/testdata/pcln115.gz b/src/debug/gosym/testdata/pcln115.gz
new file mode 100644 (file)
index 0000000..db5c3d4
Binary files /dev/null and b/src/debug/gosym/testdata/pcln115.gz differ
index 1e86662adc2c8789368e51fef83d13918e955d88..95f01c555b67b5b3da74cd51d70cfc80a27e774f 100644 (file)
@@ -334,12 +334,23 @@ const (
        funcID_wrapper // any autogenerated code (hash/eq algorithms, method wrappers, etc.)
 )
 
+// 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
+       pclnOffset uintptr // offset to the pclntab variable from pcHeader
+}
+
 // moduledata records information about the layout of the executable
 // image. It is written by the linker. Any changes here must be
 // matched changes to the code in cmd/internal/ld/symtab.go:symtab.
 // moduledata is stored in statically allocated non-pointer memory;
 // none of the pointers here are visible to the garbage collector.
 type moduledata struct {
+       pcHeader     *pcHeader
        pclntable    []byte
        ftab         []functab
        filetab      []uint32
@@ -514,13 +525,10 @@ func moduledataverify() {
 const debugPcln = false
 
 func moduledataverify1(datap *moduledata) {
-       // See golang.org/s/go12symtab for header: 0xfffffffb,
-       // two zero bytes, a byte giving the PC quantum,
-       // and a byte giving the pointer width in bytes.
-       pcln := *(**[8]byte)(unsafe.Pointer(&datap.pclntable))
-       pcln32 := *(**[2]uint32)(unsafe.Pointer(&datap.pclntable))
-       if pcln32[0] != 0xfffffffb || pcln[4] != 0 || pcln[5] != 0 || pcln[6] != sys.PCQuantum || pcln[7] != sys.PtrSize {
-               println("runtime: function symbol table header:", hex(pcln32[0]), hex(pcln[4]), hex(pcln[5]), hex(pcln[6]), hex(pcln[7]))
+       // Check that the pclntab's format is valid.
+       hdr := datap.pcHeader
+       if hdr.magic != 0xfffffffa || hdr.pad1 != 0 || hdr.pad2 != 0 || hdr.minLC != sys.PCQuantum || hdr.ptrSize != sys.PtrSize {
+               println("runtime: function symbol table header:", hex(hdr.magic), hex(hdr.pad1), hex(hdr.pad2), hex(hdr.minLC), hex(hdr.ptrSize))
                throw("invalid function symbol table\n")
        }