mirror of
https://github.com/golang/go
synced 2024-11-18 01:54:45 -07:00
[dev.link] cmd/link: add runtime.pcheader
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>
This commit is contained in:
parent
b3e3c339ff
commit
ba9c639470
@ -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))
|
||||
|
@ -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,21 +270,45 @@ 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)
|
||||
ldr := ctxt.loader
|
||||
ftabsym := ldr.LookupOrCreateSym("runtime.pclntab", 0)
|
||||
ftab := ldr.MakeSymbolUpdater(ftabsym)
|
||||
ftab.SetType(sym.SPCLNTAB)
|
||||
ldr.SetAttrReachable(ftabsym, true)
|
||||
|
||||
state := makepclnState(ctxt)
|
||||
|
||||
// See golang.org/s/go12symtab for the format. Briefly:
|
||||
// 8-byte header
|
||||
// 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
|
||||
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)
|
||||
ftab.SetReachable(true)
|
||||
|
||||
state := makepclnState(ctxt)
|
||||
|
||||
// Find container symbols and mark them as such.
|
||||
for _, s := range ctxt.Textp {
|
||||
@ -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))
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
// This mutex is used to keep parsing of pclntab synchronous.
|
||||
mu sync.Mutex
|
||||
go12 int // is this in Go 1.2 format? -1 no, 0 unknown, 1 yes
|
||||
|
||||
// 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,40 +171,68 @@ 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])
|
||||
|
||||
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:])
|
||||
@ -196,8 +240,10 @@ func (t *LineTable) go12Init() {
|
||||
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
|
||||
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:])
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
BIN
src/debug/gosym/testdata/pcln115.gz
vendored
Normal file
BIN
src/debug/gosym/testdata/pcln115.gz
vendored
Normal file
Binary file not shown.
@ -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")
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user