1
0
mirror of https://github.com/golang/go synced 2024-11-18 10:24:42 -07:00

[dev.link] cmd/link: convert pcln linker phase to use loader APIs

Rework the linker's pcln phase to work with the new loader. As part of
this set of changes the handling of "go.file..." symbols has been
revised somewhat -- previously they were treated as always live in the
loader, and now we no longer do this.

The original plan had been to have the new implementation generate
nameless "inltree" symbols, however the plan now is to keep them
named for now and convert them to nameless in a subsequent patch.

Change-Id: If71c93ff1f146dbb63b6ee2546308acdc94b643c
Reviewed-on: https://go-review.googlesource.com/c/go/+/227759
Run-TryBot: Than McIntosh <thanm@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
Reviewed-by: Jeremy Faller <jeremy@golang.org>
This commit is contained in:
Than McIntosh 2020-04-07 17:08:00 -04:00
parent c306fbaa1c
commit db48c404cf
7 changed files with 311 additions and 183 deletions

View File

@ -2808,6 +2808,10 @@ func (ctxt *Link) loadlibfull() {
// Set special global symbols.
ctxt.setArchSyms(AfterLoadlibFull)
// Convert special symbols created by pcln.
pclntabFirstFunc = ctxt.loader.Syms[pclntabFirstFunc2]
pclntabLastFunc = ctxt.loader.Syms[pclntabLastFunc2]
}
func (ctxt *Link) dumpsyms() {

View File

@ -73,7 +73,7 @@ type Link struct {
Shlibs []Shlib
Textp []*sym.Symbol
Textp2 []loader.Sym
Filesyms []*sym.Symbol
NumFilesyms int
Moduledata *sym.Symbol
Moduledata2 loader.Sym

View File

@ -890,7 +890,7 @@ func machosymtab(ctxt *Link) {
// symbols like crosscall2 are in pclntab and end up
// pointing at the host binary, breaking unwinding.
// See Issue #18190.
cexport := !isGoSymbol && (ctxt.BuildMode != BuildModePlugin || onlycsymbol(s))
cexport := !isGoSymbol && (ctxt.BuildMode != BuildModePlugin || onlycsymbol(s.Name))
if cexport || export || isGoSymbol {
symstr.AddUint8('_')
}

View File

@ -294,10 +294,10 @@ func Main(arch *sys.Arch, theArch Arch) {
ctxt.typelink()
bench.Start("buildinfo")
ctxt.buildinfo()
bench.Start("loadlibfull")
ctxt.loadlibfull() // XXX do it here for now
bench.Start("pclntab")
ctxt.pclntab()
bench.Start("loadlibfull")
ctxt.loadlibfull() // XXX do it here for now
bench.Start("findfunctab")
ctxt.findfunctab()
bench.Start("symtab")

View File

@ -9,6 +9,7 @@ import (
"cmd/internal/objabi"
"cmd/internal/src"
"cmd/internal/sys"
"cmd/link/internal/loader"
"cmd/link/internal/sym"
"encoding/binary"
"fmt"
@ -18,28 +19,71 @@ import (
"strings"
)
func ftabaddstring(ftab *sym.Symbol, s string) int32 {
start := len(ftab.P)
// pclnState holds state information used during pclntab generation.
// Here 'ldr' is just a pointer to the context's loader, 'container'
// is a bitmap holding whether a given symbol index is an outer or
// container symbol, '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).
type pclnState struct {
ldr *loader.Loader
container loader.Bitmap
deferReturnSym loader.Sym
nameToOffset func(name string) int32
numberedFiles map[loader.Sym]int64
filepaths []string
}
func makepclnState(ctxt *Link) pclnState {
ldr := ctxt.loader
drs := ldr.Lookup("runtime.deferreturn", sym.SymVerABIInternal)
return pclnState{
container: loader.MakeBitmap(ldr.NSym()),
ldr: ldr,
deferReturnSym: drs,
numberedFiles: make(map[loader.Sym]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{""},
}
}
func (state *pclnState) ftabaddstring(ftab *loader.SymbolBuilder, s string) int32 {
start := len(ftab.Data())
ftab.Grow(int64(start + len(s) + 1)) // make room for s plus trailing NUL
copy(ftab.P[start:], s)
ftd := ftab.Data()
copy(ftd[start:], s)
return int32(start)
}
// numberfile assigns a file number to the file if it hasn't been assigned already.
func numberfile(ctxt *Link, file *sym.Symbol) {
if file.Type != sym.SFILEPATH {
ctxt.Filesyms = append(ctxt.Filesyms, file)
file.Value = int64(len(ctxt.Filesyms))
file.Type = sym.SFILEPATH
path := file.Name[len(src.FileSymPrefix):]
file.Name = expandGoroot(path)
func (state *pclnState) numberfile(file loader.Sym) int64 {
if val, ok := state.numberedFiles[file]; ok {
return val
}
sn := state.ldr.SymName(file)
path := sn[len(src.FileSymPrefix):]
val := int64(len(state.filepaths))
state.numberedFiles[file] = val
state.filepaths = append(state.filepaths, expandGoroot(path))
return val
}
func renumberfiles(ctxt *Link, files []*sym.Symbol, d *sym.Pcdata) {
func (state *pclnState) fileVal(file loader.Sym) int64 {
if val, ok := state.numberedFiles[file]; ok {
return val
}
panic("should have been numbered first")
}
func (state *pclnState) renumberfiles(ctxt *Link, fi loader.FuncInfo, d *sym.Pcdata) {
// Give files numbers.
for _, f := range files {
numberfile(ctxt, f)
nf := fi.NumFile()
for i := uint32(0); i < nf; i++ {
state.numberfile(fi.File(int(i)))
}
buf := make([]byte, binary.MaxVarintLen32)
@ -54,10 +98,10 @@ func renumberfiles(ctxt *Link, files []*sym.Symbol, d *sym.Pcdata) {
if oldval == -1 {
val = -1
} else {
if oldval < 0 || oldval >= int32(len(files)) {
if oldval < 0 || oldval >= int32(nf) {
log.Fatalf("bad pcdata %d", oldval)
}
val = int32(files[oldval].Value)
val = int32(state.fileVal(fi.File(int(oldval))))
}
dv := val - newval
@ -80,23 +124,33 @@ func renumberfiles(ctxt *Link, files []*sym.Symbol, d *sym.Pcdata) {
*d = out
}
// onlycsymbol reports whether this is a symbol that is referenced by C code.
func onlycsymbol(s *sym.Symbol) bool {
switch s.Name {
// 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 {
switch sname {
case "_cgo_topofstack", "__cgo_topofstack", "_cgo_panic", "crosscall2":
return true
}
if strings.HasPrefix(s.Name, "_cgoexp_") {
if strings.HasPrefix(sname, "_cgoexp_") {
return true
}
return false
}
func (state *pclnState) emitPcln(ctxt *Link, s loader.Sym) bool {
if ctxt.BuildMode == BuildModePlugin && ctxt.HeadType == objabi.Hdarwin && onlycsymbol(state.ldr.SymName(s)) {
return false
}
// We want to generate func table entries only for the "lowest
// level" symbols, not containers of subsymbols.
return !state.container.Has(s)
}
func emitPcln(ctxt *Link, s *sym.Symbol) bool {
if s == nil {
return true
}
if ctxt.BuildMode == BuildModePlugin && ctxt.HeadType == objabi.Hdarwin && onlycsymbol(s) {
if ctxt.BuildMode == BuildModePlugin && ctxt.HeadType == objabi.Hdarwin && onlycsymbol(s.Name) {
return false
}
// We want to generate func table entries only for the "lowest level" symbols,
@ -104,23 +158,108 @@ func emitPcln(ctxt *Link, s *sym.Symbol) bool {
return !s.Attr.Container()
}
func (state *pclnState) computeDeferReturn(target *Target, s loader.Sym) uint32 {
deferreturn := uint32(0)
lastWasmAddr := uint32(0)
relocs := state.ldr.Relocs(s)
for ri := 0; ri < relocs.Count(); ri++ {
r := relocs.At2(ri)
if target.IsWasm() && r.Type() == objabi.R_ADDR {
// Wasm does not have a live variable set at the deferreturn
// call itself. Instead it has one identified by the
// resumption point immediately preceding the deferreturn.
// The wasm code has a R_ADDR relocation which is used to
// set the resumption point to PC_B.
lastWasmAddr = uint32(r.Add())
}
if r.Type().IsDirectCall() && r.Sym() == state.deferReturnSym {
if target.IsWasm() {
deferreturn = lastWasmAddr - 1
} else {
// Note: the relocation target is in the call instruction, but
// is not necessarily the whole instruction (for instance, on
// x86 the relocation applies to bytes [1:5] of the 5 byte call
// instruction).
deferreturn = uint32(r.Off())
switch target.Arch.Family {
case sys.AMD64, sys.I386:
deferreturn--
case sys.PPC64, sys.ARM, sys.ARM64, sys.MIPS, sys.MIPS64:
// no change
case sys.RISCV64:
// TODO(jsing): The JALR instruction is marked with
// R_CALLRISCV, whereas the actual reloc is currently
// one instruction earlier starting with the AUIPC.
deferreturn -= 4
case sys.S390X:
deferreturn -= 2
default:
panic(fmt.Sprint("Unhandled architecture:", target.Arch.Family))
}
}
break // only need one
}
}
return deferreturn
}
// genInlTreeSym generates the InlTree sym for the a given function symbol
// with name 'sn'.
func (state *pclnState) genInlTreeSym(sn string, fi loader.FuncInfo, arch *sys.Arch) loader.Sym {
itsName := "inltree." + sn
ldr := state.ldr
if ldr.Lookup(itsName, 0) != 0 {
panic("should not exist yet")
}
its := ldr.LookupOrCreateSym(itsName, 0)
inlTreeSym := ldr.MakeSymbolUpdater(its)
inlTreeSym.SetType(sym.SRODATA)
ldr.SetAttrReachable(its, true)
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(call.File)
fn := ldr.SymName(call.Func)
nameoff := state.nameToOffset(fn)
inlTreeSym.SetUint16(arch, int64(i*20+0), uint16(call.Parent))
inlTreeSym.SetUint8(arch, int64(i*20+2), uint8(objabi.GetFuncID(fn, "")))
// byte 3 is unused
inlTreeSym.SetUint32(arch, int64(i*20+4), uint32(val))
inlTreeSym.SetUint32(arch, int64(i*20+8), uint32(call.Line))
inlTreeSym.SetUint32(arch, int64(i*20+12), uint32(nameoff))
inlTreeSym.SetUint32(arch, int64(i*20+16), uint32(call.ParentPC))
}
return its
}
// pclntab initializes the pclntab symbol with
// runtime function and file name information.
var pclntabZpcln sym.FuncInfo
// These variables are used to initialize runtime.firstmoduledata, see symtab.go:symtab.
var pclntabNfunc int32
var pclntabFiletabOffset int32
var pclntabPclntabOffset int32
var pclntabFirstFunc *sym.Symbol
var pclntabLastFunc *sym.Symbol
var pclntabFirstFunc2 loader.Sym
var pclntabLastFunc2 loader.Sym
func (ctxt *Link) pclntab() {
funcdataBytes := int64(0)
ftab := ctxt.Syms.Lookup("runtime.pclntab", 0)
ftab.Type = sym.SPCLNTAB
ftab.Attr |= sym.AttrReachable
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
@ -130,30 +269,33 @@ func (ctxt *Link) pclntab() {
// offset to file table [4 bytes]
// Find container symbols and mark them as such.
for _, s := range ctxt.Textp {
if s.Outer != nil {
s.Outer.Attr |= sym.AttrContainer
for _, s := range ctxt.Textp2 {
outer := ldr.OuterSym(s)
if outer != 0 {
state.container.Set(outer)
}
}
// Gather some basic stats and info.
var nfunc int32
prevSect := ctxt.Textp[0].Sect
for _, s := range ctxt.Textp {
if !emitPcln(ctxt, s) {
prevSect := ldr.SymSect(ctxt.Textp2[0])
for _, s := range ctxt.Textp2 {
if !state.emitPcln(ctxt, s) {
continue
}
nfunc++
if pclntabFirstFunc == nil {
pclntabFirstFunc = s
if pclntabFirstFunc2 == 0 {
pclntabFirstFunc2 = s
}
if s.Sect != prevSect {
// With multiple text sections, the external linker may insert functions
// between the sections, which are not known by Go. This leaves holes in
// the PC range covered by the func table. We need to generate an entry
// to mark the hole.
ss := ldr.SymSect(s)
if ss != prevSect {
// With multiple text sections, the external linker may
// insert functions between the sections, which are not
// known by Go. This leaves holes in the PC range covered
// by the func table. We need to generate an entry to mark
// the hole.
nfunc++
prevSect = s.Sect
prevSect = ss
}
}
@ -165,22 +307,24 @@ func (ctxt *Link) pclntab() {
ftab.SetUint(ctxt.Arch, 8, uint64(nfunc))
pclntabPclntabOffset = int32(8 + ctxt.Arch.PtrSize)
funcnameoff := make(map[string]int32)
szHint := len(ctxt.Textp2) * 2
funcnameoff := make(map[string]int32, szHint)
nameToOffset := func(name string) int32 {
nameoff, ok := funcnameoff[name]
if !ok {
nameoff = ftabaddstring(ftab, name)
nameoff = state.ftabaddstring(ftab, name)
funcnameoff[name] = nameoff
}
return nameoff
}
state.nameToOffset = nameToOffset
pctaboff := make(map[string]uint32)
pctaboff := make(map[string]uint32, szHint)
writepctab := func(off int32, p []byte) int32 {
start, ok := pctaboff[string(p)]
if !ok {
if len(p) > 0 {
start = uint32(len(ftab.P))
start = uint32(len(ftab.Data()))
ftab.AddBytes(p)
}
pctaboff[string(p)] = start
@ -189,52 +333,82 @@ func (ctxt *Link) pclntab() {
return newoff
}
pcsp := sym.Pcdata{}
pcfile := sym.Pcdata{}
pcline := sym.Pcdata{}
pcdata := []sym.Pcdata{}
funcdata := []loader.Sym{}
funcdataoff := []int64{}
nfunc = 0 // repurpose nfunc as a running index
prevFunc := ctxt.Textp[0]
for _, s := range ctxt.Textp {
if !emitPcln(ctxt, s) {
prevFunc := ctxt.Textp2[0]
for _, s := range ctxt.Textp2 {
if !state.emitPcln(ctxt, s) {
continue
}
if s.Sect != prevFunc.Sect {
// With multiple text sections, there may be a hole here in the address
// space (see the comment above). We use an invalid funcoff value to
// mark the hole.
// See also runtime/symtab.go:findfunc
ftab.SetAddrPlus(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize), prevFunc, prevFunc.Size)
thisSect := ldr.SymSect(s)
prevSect := ldr.SymSect(prevFunc)
if thisSect != prevSect {
// With multiple text sections, there may be a hole here
// in the address space (see the comment above). We use an
// invalid funcoff value to mark the hole. See also
// runtime/symtab.go:findfunc
prevFuncSize := int64(ldr.SymSize(prevFunc))
ftab.SetAddrPlus(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))
nfunc++
}
prevFunc = s
pcln := s.FuncInfo
if pcln == nil {
pcln = &pclntabZpcln
pcsp.P = pcsp.P[:0]
pcline.P = pcline.P[:0]
pcfile.P = pcfile.P[:0]
pcdata = pcdata[:0]
funcdataoff = funcdataoff[:0]
funcdata = funcdata[:0]
fi := ldr.FuncInfo(s)
if fi.Valid() {
fi.Preload()
npc := fi.NumPcdata()
for i := uint32(0); i < npc; i++ {
pcdata = append(pcdata, sym.Pcdata{P: fi.Pcdata(int(i))})
}
nfd := fi.NumFuncdataoff()
for i := uint32(0); i < nfd; i++ {
funcdataoff = append(funcdataoff, fi.Funcdataoff(int(i)))
}
funcdata = fi.Funcdata(s, funcdata)
}
if len(pcln.InlTree) > 0 {
if len(pcln.Pcdata) <= objabi.PCDATA_InlTreeIndex {
if fi.Valid() && fi.NumInlTree() > 0 {
if len(pcdata) <= objabi.PCDATA_InlTreeIndex {
// Create inlining pcdata table.
pcdata := make([]sym.Pcdata, objabi.PCDATA_InlTreeIndex+1)
copy(pcdata, pcln.Pcdata)
pcln.Pcdata = pcdata
newpcdata := make([]sym.Pcdata, objabi.PCDATA_InlTreeIndex+1)
copy(newpcdata, pcdata)
pcdata = newpcdata
}
if len(pcln.Funcdataoff) <= objabi.FUNCDATA_InlTree {
if len(funcdataoff) <= objabi.FUNCDATA_InlTree {
// Create inline tree funcdata.
funcdata := make([]*sym.Symbol, objabi.FUNCDATA_InlTree+1)
funcdataoff := make([]int64, objabi.FUNCDATA_InlTree+1)
copy(funcdata, pcln.Funcdata)
copy(funcdataoff, pcln.Funcdataoff)
pcln.Funcdata = funcdata
pcln.Funcdataoff = funcdataoff
newfuncdata := make([]loader.Sym, objabi.FUNCDATA_InlTree+1)
newfuncdataoff := make([]int64, objabi.FUNCDATA_InlTree+1)
copy(newfuncdata, funcdata)
copy(newfuncdataoff, funcdataoff)
funcdata = newfuncdata
funcdataoff = newfuncdataoff
}
}
funcstart := int32(len(ftab.P))
funcstart += int32(-len(ftab.P)) & (int32(ctxt.Arch.PtrSize) - 1) // align to ptrsize
dSize := len(ftab.Data())
funcstart := int32(dSize)
funcstart += int32(-dSize) & (int32(ctxt.Arch.PtrSize) - 1) // align to ptrsize
ftab.SetAddr(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize), s)
// NB: for the static binary internal-link case, we could just
// emit the symbol value instead of creating a relocation here
// (might speed things up for that case).
ftab.SetAddrPlus(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))
// Write runtime._func. Keep in sync with ../../../../runtime/runtime2.go:/_func
@ -243,122 +417,68 @@ func (ctxt *Link) pclntab() {
// fixed size of struct, checked below
off := funcstart
end := funcstart + int32(ctxt.Arch.PtrSize) + 3*4 + 5*4 + int32(len(pcln.Pcdata))*4 + int32(len(pcln.Funcdata))*int32(ctxt.Arch.PtrSize)
if len(pcln.Funcdata) > 0 && (end&int32(ctxt.Arch.PtrSize-1) != 0) {
end := funcstart + int32(ctxt.Arch.PtrSize) + 3*4 + 5*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
}
ftab.Grow(int64(end))
// entry uintptr
off = int32(ftab.SetAddr(ctxt.Arch, int64(off), s))
off = int32(ftab.SetAddrPlus(ctxt.Arch, int64(off), s, 0))
// name int32
nameoff := nameToOffset(s.Name)
sn := ldr.SymName(s)
nameoff := nameToOffset(sn)
off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(nameoff)))
// args int32
// TODO: Move into funcinfo.
args := uint32(0)
if s.FuncInfo != nil {
args = uint32(s.FuncInfo.Args)
if fi.Valid() {
args = uint32(fi.Args())
}
off = int32(ftab.SetUint32(ctxt.Arch, int64(off), args))
// deferreturn
deferreturn := uint32(0)
lastWasmAddr := uint32(0)
for _, r := range s.R {
if ctxt.Arch.Family == sys.Wasm && r.Type == objabi.R_ADDR {
// Wasm does not have a live variable set at the deferreturn
// call itself. Instead it has one identified by the
// resumption point immediately preceding the deferreturn.
// The wasm code has a R_ADDR relocation which is used to
// set the resumption point to PC_B.
lastWasmAddr = uint32(r.Add)
}
if r.Type.IsDirectCall() && r.Sym != nil && r.Sym.Name == "runtime.deferreturn" {
if ctxt.Arch.Family == sys.Wasm {
deferreturn = lastWasmAddr - 1
} else {
// Note: the relocation target is in the call instruction, but
// is not necessarily the whole instruction (for instance, on
// x86 the relocation applies to bytes [1:5] of the 5 byte call
// instruction).
deferreturn = uint32(r.Off)
switch ctxt.Arch.Family {
case sys.AMD64, sys.I386:
deferreturn--
case sys.PPC64, sys.ARM, sys.ARM64, sys.MIPS, sys.MIPS64:
// no change
case sys.RISCV64:
// TODO(jsing): The JALR instruction is marked with
// R_CALLRISCV, whereas the actual reloc is currently
// one instruction earlier starting with the AUIPC.
deferreturn -= 4
case sys.S390X:
deferreturn -= 2
default:
panic(fmt.Sprint("Unhandled architecture:", ctxt.Arch.Family))
}
}
break // only need one
}
}
deferreturn := state.computeDeferReturn(&ctxt.Target, s)
off = int32(ftab.SetUint32(ctxt.Arch, int64(off), deferreturn))
if pcln != &pclntabZpcln {
renumberfiles(ctxt, pcln.File, &pcln.Pcfile)
if fi.Valid() {
pcsp = sym.Pcdata{P: fi.Pcsp()}
pcfile = sym.Pcdata{P: fi.Pcfile()}
pcline = sym.Pcdata{P: fi.Pcline()}
state.renumberfiles(ctxt, fi, &pcfile)
if false {
// Sanity check the new numbering
it := obj.NewPCIter(uint32(ctxt.Arch.MinLC))
for it.Init(pcln.Pcfile.P); !it.Done; it.Next() {
if it.Value < 1 || it.Value > int32(len(ctxt.Filesyms)) {
Errorf(s, "bad file number in pcfile: %d not in range [1, %d]\n", it.Value, len(ctxt.Filesyms))
for it.Init(pcfile.P); !it.Done; it.Next() {
if it.Value < 1 || it.Value > int32(len(state.numberedFiles)) {
ctxt.Errorf(s, "bad file number in pcfile: %d not in range [1, %d]\n", it.Value, len(state.numberedFiles))
errorexit()
}
}
}
}
if len(pcln.InlTree) > 0 {
inlTreeSym := ctxt.Syms.Lookup("inltree."+s.Name, 0)
inlTreeSym.Type = sym.SRODATA
inlTreeSym.Attr |= sym.AttrReachable | sym.AttrDuplicateOK
for i, call := range pcln.InlTree {
// 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.
numberfile(ctxt, call.File)
nameoff := nameToOffset(call.Func)
inlTreeSym.SetUint16(ctxt.Arch, int64(i*20+0), uint16(call.Parent))
inlTreeSym.SetUint8(ctxt.Arch, int64(i*20+2), uint8(objabi.GetFuncID(call.Func, "")))
// byte 3 is unused
inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+4), uint32(call.File.Value))
inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+8), uint32(call.Line))
inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+12), uint32(nameoff))
inlTreeSym.SetUint32(ctxt.Arch, int64(i*20+16), uint32(call.ParentPC))
}
pcln.Funcdata[objabi.FUNCDATA_InlTree] = inlTreeSym
pcln.Pcdata[objabi.PCDATA_InlTreeIndex] = pcln.Pcinline
if fi.Valid() && fi.NumInlTree() > 0 {
its := state.genInlTreeSym(sn, fi, ctxt.Arch)
funcdata[objabi.FUNCDATA_InlTree] = its
pcdata[objabi.PCDATA_InlTreeIndex] = sym.Pcdata{P: fi.Pcinline()}
}
// pcdata
off = writepctab(off, pcln.Pcsp.P)
off = writepctab(off, pcln.Pcfile.P)
off = writepctab(off, pcln.Pcline.P)
off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(len(pcln.Pcdata))))
off = writepctab(off, pcsp.P)
off = writepctab(off, pcfile.P)
off = writepctab(off, pcline.P)
off = int32(ftab.SetUint32(ctxt.Arch, int64(off), uint32(len(pcdata))))
// funcID uint8
var file string
if s.FuncInfo != nil && len(s.FuncInfo.File) > 0 {
file = s.FuncInfo.File[0].Name
if fi.Valid() && fi.NumFile() > 0 {
filesymname := ldr.SymName(fi.File(0))
file = filesymname[len(src.FileSymPrefix):]
}
funcID := objabi.GetFuncID(s.Name, file)
funcID := objabi.GetFuncID(sn, file)
off = int32(ftab.SetUint8(ctxt.Arch, int64(off), uint8(funcID)))
@ -366,61 +486,65 @@ func (ctxt *Link) pclntab() {
off += 2
// nfuncdata must be the final entry.
off = int32(ftab.SetUint8(ctxt.Arch, int64(off), uint8(len(pcln.Funcdata))))
for i := range pcln.Pcdata {
off = writepctab(off, pcln.Pcdata[i].P)
off = int32(ftab.SetUint8(ctxt.Arch, int64(off), uint8(len(funcdata))))
for i := range pcdata {
off = writepctab(off, pcdata[i].P)
}
// funcdata, must be pointer-aligned and we're only int32-aligned.
// Missing funcdata will be 0 (nil pointer).
if len(pcln.Funcdata) > 0 {
if len(funcdata) > 0 {
if off&int32(ctxt.Arch.PtrSize-1) != 0 {
off += 4
}
for i := range pcln.Funcdata {
for i := range funcdata {
dataoff := int64(off) + int64(ctxt.Arch.PtrSize)*int64(i)
if pcln.Funcdata[i] == nil {
ftab.SetUint(ctxt.Arch, dataoff, uint64(pcln.Funcdataoff[i]))
if funcdata[i] == 0 {
ftab.SetUint(ctxt.Arch, dataoff, uint64(funcdataoff[i]))
continue
}
// TODO: Dedup.
funcdataBytes += pcln.Funcdata[i].Size
ftab.SetAddrPlus(ctxt.Arch, dataoff, pcln.Funcdata[i], pcln.Funcdataoff[i])
funcdataBytes += int64(len(ldr.Data(funcdata[i])))
ftab.SetAddrPlus(ctxt.Arch, dataoff, funcdata[i], funcdataoff[i])
}
off += int32(len(pcln.Funcdata)) * int32(ctxt.Arch.PtrSize)
off += int32(len(funcdata)) * int32(ctxt.Arch.PtrSize)
}
if off != end {
Errorf(s, "bad math in functab: funcstart=%d off=%d but end=%d (npcdata=%d nfuncdata=%d ptrsize=%d)", funcstart, off, end, len(pcln.Pcdata), len(pcln.Funcdata), ctxt.Arch.PtrSize)
ctxt.Errorf(s, "bad math in functab: funcstart=%d off=%d but end=%d (npcdata=%d nfuncdata=%d ptrsize=%d)", funcstart, off, end, len(pcdata), len(funcdata), ctxt.Arch.PtrSize)
errorexit()
}
nfunc++
}
last := ctxt.Textp[len(ctxt.Textp)-1]
pclntabLastFunc = last
last := ctxt.Textp2[len(ctxt.Textp2)-1]
pclntabLastFunc2 = last
// Final entry of table is just end pc.
ftab.SetAddrPlus(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize), last, last.Size)
ftab.SetAddrPlus(ctxt.Arch, 8+int64(ctxt.Arch.PtrSize)+int64(nfunc)*2*int64(ctxt.Arch.PtrSize), last, ldr.SymSize(last))
// Start file table.
start := int32(len(ftab.P))
start += int32(-len(ftab.P)) & (int32(ctxt.Arch.PtrSize) - 1)
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.Grow(int64(start) + (int64(len(ctxt.Filesyms))+1)*4)
ftab.SetUint32(ctxt.Arch, int64(start), uint32(len(ctxt.Filesyms)+1))
for i := len(ctxt.Filesyms) - 1; i >= 0; i-- {
s := ctxt.Filesyms[i]
ftab.SetUint32(ctxt.Arch, int64(start)+s.Value*4, uint32(ftabaddstring(ftab, s.Name)))
nf := len(state.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 := state.filepaths[i]
val := int64(i)
ftab.SetUint32(ctxt.Arch, int64(start)+val*4, uint32(state.ftabaddstring(ftab, path)))
}
ftab.Size = int64(len(ftab.P))
ftab.SetSize(int64(len(ftab.Data())))
ctxt.NumFilesyms = len(state.numberedFiles)
if ctxt.Debugvlog != 0 {
ctxt.Logf("pclntab=%d bytes, funcdata total %d bytes\n", ftab.Size, funcdataBytes)
ctxt.Logf("pclntab=%d bytes, funcdata total %d bytes\n", ftab.Size(), funcdataBytes)
}
}

View File

@ -554,8 +554,8 @@ func (ctxt *Link) symtab() {
moduledata.AddUint(ctxt.Arch, uint64(pclntabNfunc+1))
// The filetab slice
moduledata.AddAddrPlus(ctxt.Arch, ctxt.Syms.Lookup("runtime.pclntab", 0), int64(pclntabFiletabOffset))
moduledata.AddUint(ctxt.Arch, uint64(len(ctxt.Filesyms))+1)
moduledata.AddUint(ctxt.Arch, uint64(len(ctxt.Filesyms))+1)
moduledata.AddUint(ctxt.Arch, uint64(ctxt.NumFilesyms)+1)
moduledata.AddUint(ctxt.Arch, uint64(ctxt.NumFilesyms)+1)
// findfunctab
moduledata.AddAddr(ctxt.Arch, ctxt.Syms.Lookup("runtime.findfunctab", 0))
// minpc, maxpc

View File

@ -1861,7 +1861,7 @@ func (l *Loader) LoadFull(arch *sys.Arch, syms *sym.Symbols) {
toConvert := make([]Sym, 0, len(l.payloads))
for _, i := range l.extReader.syms {
sname := l.RawSymName(i)
if !l.attrReachable.Has(i) && !strings.HasPrefix(sname, "gofile..") { // XXX file symbols are used but not marked
if !l.attrReachable.Has(i) {
continue
}
pp := l.getPayload(i)