|
|
|
|
@ -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)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|