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:
parent
c306fbaa1c
commit
db48c404cf
@ -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() {
|
||||
|
@ -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
|
||||
|
||||
|
@ -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('_')
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user