mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
488 lines
12 KiB
Go
488 lines
12 KiB
Go
|
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||
|
|
// Use of this source code is governed by a BSD-style
|
||
|
|
// license that can be found in the LICENSE file.
|
||
|
|
|
||
|
|
package ld
|
||
|
|
|
||
|
|
import (
|
||
|
|
"cmd/internal/obj"
|
||
|
|
"fmt"
|
||
|
|
"log"
|
||
|
|
)
|
||
|
|
|
||
|
|
// funcpctab writes to dst a pc-value table mapping the code in func to the values
|
||
|
|
// returned by valfunc parameterized by arg. The invocation of valfunc to update the
|
||
|
|
// current value is, for each p,
|
||
|
|
//
|
||
|
|
// val = valfunc(func, val, p, 0, arg);
|
||
|
|
// record val as value at p->pc;
|
||
|
|
// val = valfunc(func, val, p, 1, arg);
|
||
|
|
//
|
||
|
|
// where func is the function, val is the current value, p is the instruction being
|
||
|
|
// considered, and arg can be used to further parameterize valfunc.
|
||
|
|
|
||
|
|
// pctofileline computes either the file number (arg == 0)
|
||
|
|
// or the line number (arg == 1) to use at p.
|
||
|
|
// Because p->lineno applies to p, phase == 0 (before p)
|
||
|
|
// takes care of the update.
|
||
|
|
|
||
|
|
// pctospadj computes the sp adjustment in effect.
|
||
|
|
// It is oldval plus any adjustment made by p itself.
|
||
|
|
// The adjustment by p takes effect only after p, so we
|
||
|
|
// apply the change during phase == 1.
|
||
|
|
|
||
|
|
// pctopcdata computes the pcdata value in effect at p.
|
||
|
|
// A PCDATA instruction sets the value in effect at future
|
||
|
|
// non-PCDATA instructions.
|
||
|
|
// Since PCDATA instructions have no width in the final code,
|
||
|
|
// it does not matter which phase we use for the update.
|
||
|
|
|
||
|
|
// iteration over encoded pcdata tables.
|
||
|
|
|
||
|
|
func getvarint(pp *[]byte) uint32 {
|
||
|
|
var p []byte
|
||
|
|
var shift int
|
||
|
|
var v uint32
|
||
|
|
|
||
|
|
v = 0
|
||
|
|
p = *pp
|
||
|
|
for shift = 0; ; shift += 7 {
|
||
|
|
v |= uint32(p[0]&0x7F) << uint(shift)
|
||
|
|
tmp4 := p
|
||
|
|
p = p[1:]
|
||
|
|
if tmp4[0]&0x80 == 0 {
|
||
|
|
break
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
*pp = p
|
||
|
|
return v
|
||
|
|
}
|
||
|
|
|
||
|
|
func pciternext(it *Pciter) {
|
||
|
|
var v uint32
|
||
|
|
var dv int32
|
||
|
|
|
||
|
|
it.pc = it.nextpc
|
||
|
|
if it.done != 0 {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if -cap(it.p) >= -cap(it.d.P[len(it.d.P):]) {
|
||
|
|
it.done = 1
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
// value delta
|
||
|
|
v = getvarint(&it.p)
|
||
|
|
|
||
|
|
if v == 0 && it.start == 0 {
|
||
|
|
it.done = 1
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
it.start = 0
|
||
|
|
dv = int32(v>>1) ^ (int32(v<<31) >> 31)
|
||
|
|
it.value += dv
|
||
|
|
|
||
|
|
// pc delta
|
||
|
|
v = getvarint(&it.p)
|
||
|
|
|
||
|
|
it.nextpc = it.pc + v*it.pcscale
|
||
|
|
}
|
||
|
|
|
||
|
|
func pciterinit(ctxt *Link, it *Pciter, d *Pcdata) {
|
||
|
|
it.d = *d
|
||
|
|
it.p = it.d.P
|
||
|
|
it.pc = 0
|
||
|
|
it.nextpc = 0
|
||
|
|
it.value = -1
|
||
|
|
it.start = 1
|
||
|
|
it.done = 0
|
||
|
|
it.pcscale = uint32(ctxt.Arch.Minlc)
|
||
|
|
pciternext(it)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Copyright 2013 The Go Authors. All rights reserved.
|
||
|
|
// Use of this source code is governed by a BSD-style
|
||
|
|
// license that can be found in the LICENSE file.
|
||
|
|
|
||
|
|
func addvarint(d *Pcdata, val uint32) {
|
||
|
|
var n int32
|
||
|
|
var v uint32
|
||
|
|
var p []byte
|
||
|
|
|
||
|
|
n = 0
|
||
|
|
for v = val; v >= 0x80; v >>= 7 {
|
||
|
|
n++
|
||
|
|
}
|
||
|
|
n++
|
||
|
|
|
||
|
|
old := len(d.P)
|
||
|
|
for cap(d.P) < len(d.P)+int(n) {
|
||
|
|
d.P = append(d.P[:cap(d.P)], 0)
|
||
|
|
}
|
||
|
|
d.P = d.P[:old+int(n)]
|
||
|
|
|
||
|
|
p = d.P[old:]
|
||
|
|
for v = val; v >= 0x80; v >>= 7 {
|
||
|
|
p[0] = byte(v | 0x80)
|
||
|
|
p = p[1:]
|
||
|
|
}
|
||
|
|
p[0] = byte(v)
|
||
|
|
}
|
||
|
|
|
||
|
|
func addpctab(ftab *LSym, off int32, d *Pcdata) int32 {
|
||
|
|
var start int32
|
||
|
|
|
||
|
|
start = int32(len(ftab.P))
|
||
|
|
Symgrow(Ctxt, ftab, int64(start)+int64(len(d.P)))
|
||
|
|
copy(ftab.P[start:], d.P)
|
||
|
|
|
||
|
|
return int32(setuint32(Ctxt, ftab, int64(off), uint32(start)))
|
||
|
|
}
|
||
|
|
|
||
|
|
func ftabaddstring(ftab *LSym, s string) int32 {
|
||
|
|
var n int32
|
||
|
|
var start int32
|
||
|
|
|
||
|
|
n = int32(len(s)) + 1
|
||
|
|
start = int32(len(ftab.P))
|
||
|
|
Symgrow(Ctxt, ftab, int64(start)+int64(n)+1)
|
||
|
|
copy(ftab.P[start:], s)
|
||
|
|
return start
|
||
|
|
}
|
||
|
|
|
||
|
|
func renumberfiles(ctxt *Link, files []*LSym, d *Pcdata) {
|
||
|
|
var i int
|
||
|
|
var f *LSym
|
||
|
|
var out Pcdata
|
||
|
|
var it Pciter
|
||
|
|
var v uint32
|
||
|
|
var oldval int32
|
||
|
|
var newval int32
|
||
|
|
var val int32
|
||
|
|
var dv int32
|
||
|
|
|
||
|
|
// Give files numbers.
|
||
|
|
for i = 0; i < len(files); i++ {
|
||
|
|
f = files[i]
|
||
|
|
if f.Type != SFILEPATH {
|
||
|
|
ctxt.Nhistfile++
|
||
|
|
f.Value = int64(ctxt.Nhistfile)
|
||
|
|
f.Type = SFILEPATH
|
||
|
|
f.Next = ctxt.Filesyms
|
||
|
|
ctxt.Filesyms = f
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
newval = -1
|
||
|
|
out = Pcdata{}
|
||
|
|
|
||
|
|
for pciterinit(ctxt, &it, d); it.done == 0; pciternext(&it) {
|
||
|
|
// value delta
|
||
|
|
oldval = it.value
|
||
|
|
|
||
|
|
if oldval == -1 {
|
||
|
|
val = -1
|
||
|
|
} else {
|
||
|
|
if oldval < 0 || oldval >= int32(len(files)) {
|
||
|
|
log.Fatalf("bad pcdata %d", oldval)
|
||
|
|
}
|
||
|
|
val = int32(files[oldval].Value)
|
||
|
|
}
|
||
|
|
|
||
|
|
dv = val - newval
|
||
|
|
newval = val
|
||
|
|
v = (uint32(dv) << 1) ^ uint32(int32(dv>>31))
|
||
|
|
addvarint(&out, v)
|
||
|
|
|
||
|
|
// pc delta
|
||
|
|
addvarint(&out, (it.nextpc-it.pc)/it.pcscale)
|
||
|
|
}
|
||
|
|
|
||
|
|
// terminating value delta
|
||
|
|
addvarint(&out, 0)
|
||
|
|
|
||
|
|
*d = out
|
||
|
|
}
|
||
|
|
|
||
|
|
func container(s *LSym) int {
|
||
|
|
// We want to generate func table entries only for the "lowest level" symbols,
|
||
|
|
// not containers of subsymbols.
|
||
|
|
if s != nil && s.Sub != nil {
|
||
|
|
return 1
|
||
|
|
}
|
||
|
|
return 0
|
||
|
|
}
|
||
|
|
|
||
|
|
// pclntab initializes the pclntab symbol with
|
||
|
|
// runtime function and file name information.
|
||
|
|
|
||
|
|
var pclntab_zpcln Pcln
|
||
|
|
|
||
|
|
func pclntab() {
|
||
|
|
var i int32
|
||
|
|
var nfunc int32
|
||
|
|
var start int32
|
||
|
|
var funcstart int32
|
||
|
|
var ftab *LSym
|
||
|
|
var s *LSym
|
||
|
|
var last *LSym
|
||
|
|
var off int32
|
||
|
|
var end int32
|
||
|
|
var frameptrsize int32
|
||
|
|
var funcdata_bytes int64
|
||
|
|
var pcln *Pcln
|
||
|
|
var it Pciter
|
||
|
|
|
||
|
|
funcdata_bytes = 0
|
||
|
|
ftab = Linklookup(Ctxt, "runtime.pclntab", 0)
|
||
|
|
ftab.Type = SPCLNTAB
|
||
|
|
ftab.Reachable = true
|
||
|
|
|
||
|
|
// See golang.org/s/go12symtab for the format. Briefly:
|
||
|
|
// 8-byte header
|
||
|
|
// nfunc [thearch.ptrsize bytes]
|
||
|
|
// function table, alternating PC and offset to func struct [each entry thearch.ptrsize bytes]
|
||
|
|
// end PC [thearch.ptrsize bytes]
|
||
|
|
// offset to file table [4 bytes]
|
||
|
|
nfunc = 0
|
||
|
|
|
||
|
|
for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next {
|
||
|
|
if container(Ctxt.Cursym) == 0 {
|
||
|
|
nfunc++
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Symgrow(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize)+4)
|
||
|
|
setuint32(Ctxt, ftab, 0, 0xfffffffb)
|
||
|
|
setuint8(Ctxt, ftab, 6, uint8(Thearch.Minlc))
|
||
|
|
setuint8(Ctxt, ftab, 7, uint8(Thearch.Ptrsize))
|
||
|
|
setuintxx(Ctxt, ftab, 8, uint64(nfunc), int64(Thearch.Ptrsize))
|
||
|
|
|
||
|
|
nfunc = 0
|
||
|
|
last = nil
|
||
|
|
for Ctxt.Cursym = Ctxt.Textp; Ctxt.Cursym != nil; Ctxt.Cursym = Ctxt.Cursym.Next {
|
||
|
|
last = Ctxt.Cursym
|
||
|
|
if container(Ctxt.Cursym) != 0 {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
pcln = Ctxt.Cursym.Pcln
|
||
|
|
if pcln == nil {
|
||
|
|
pcln = &pclntab_zpcln
|
||
|
|
}
|
||
|
|
|
||
|
|
funcstart = int32(len(ftab.P))
|
||
|
|
funcstart += int32(-len(ftab.P)) & (int32(Thearch.Ptrsize) - 1)
|
||
|
|
|
||
|
|
setaddr(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize), Ctxt.Cursym)
|
||
|
|
setuintxx(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize), uint64(funcstart), int64(Thearch.Ptrsize))
|
||
|
|
|
||
|
|
// fixed size of struct, checked below
|
||
|
|
off = funcstart
|
||
|
|
|
||
|
|
end = funcstart + int32(Thearch.Ptrsize) + 3*4 + 5*4 + int32(pcln.Npcdata)*4 + int32(pcln.Nfuncdata)*int32(Thearch.Ptrsize)
|
||
|
|
if pcln.Nfuncdata > 0 && (end&int32(Thearch.Ptrsize-1) != 0) {
|
||
|
|
end += 4
|
||
|
|
}
|
||
|
|
Symgrow(Ctxt, ftab, int64(end))
|
||
|
|
|
||
|
|
// entry uintptr
|
||
|
|
off = int32(setaddr(Ctxt, ftab, int64(off), Ctxt.Cursym))
|
||
|
|
|
||
|
|
// name int32
|
||
|
|
off = int32(setuint32(Ctxt, ftab, int64(off), uint32(ftabaddstring(ftab, Ctxt.Cursym.Name))))
|
||
|
|
|
||
|
|
// args int32
|
||
|
|
// TODO: Move into funcinfo.
|
||
|
|
off = int32(setuint32(Ctxt, ftab, int64(off), uint32(Ctxt.Cursym.Args)))
|
||
|
|
|
||
|
|
// frame int32
|
||
|
|
// TODO: Remove entirely. The pcsp table is more precise.
|
||
|
|
// This is only used by a fallback case during stack walking
|
||
|
|
// when a called function doesn't have argument information.
|
||
|
|
// We need to make sure everything has argument information
|
||
|
|
// and then remove this.
|
||
|
|
frameptrsize = int32(Thearch.Ptrsize)
|
||
|
|
|
||
|
|
if Ctxt.Cursym.Leaf != 0 {
|
||
|
|
frameptrsize = 0
|
||
|
|
}
|
||
|
|
off = int32(setuint32(Ctxt, ftab, int64(off), uint32(Ctxt.Cursym.Locals)+uint32(frameptrsize)))
|
||
|
|
|
||
|
|
if pcln != &pclntab_zpcln {
|
||
|
|
renumberfiles(Ctxt, pcln.File, &pcln.Pcfile)
|
||
|
|
if false {
|
||
|
|
// Sanity check the new numbering
|
||
|
|
for pciterinit(Ctxt, &it, &pcln.Pcfile); it.done == 0; pciternext(&it) {
|
||
|
|
if it.value < 1 || it.value > Ctxt.Nhistfile {
|
||
|
|
Diag("bad file number in pcfile: %d not in range [1, %d]\n", it.value, Ctxt.Nhistfile)
|
||
|
|
Errorexit()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// pcdata
|
||
|
|
off = addpctab(ftab, off, &pcln.Pcsp)
|
||
|
|
|
||
|
|
off = addpctab(ftab, off, &pcln.Pcfile)
|
||
|
|
off = addpctab(ftab, off, &pcln.Pcline)
|
||
|
|
off = int32(setuint32(Ctxt, ftab, int64(off), uint32(pcln.Npcdata)))
|
||
|
|
off = int32(setuint32(Ctxt, ftab, int64(off), uint32(pcln.Nfuncdata)))
|
||
|
|
for i = 0; i < int32(pcln.Npcdata); i++ {
|
||
|
|
off = addpctab(ftab, off, &pcln.Pcdata[i])
|
||
|
|
}
|
||
|
|
|
||
|
|
// funcdata, must be pointer-aligned and we're only int32-aligned.
|
||
|
|
// Missing funcdata will be 0 (nil pointer).
|
||
|
|
if pcln.Nfuncdata > 0 {
|
||
|
|
if off&int32(Thearch.Ptrsize-1) != 0 {
|
||
|
|
off += 4
|
||
|
|
}
|
||
|
|
for i = 0; i < int32(pcln.Nfuncdata); i++ {
|
||
|
|
if pcln.Funcdata[i] == nil {
|
||
|
|
setuintxx(Ctxt, ftab, int64(off)+int64(Thearch.Ptrsize)*int64(i), uint64(pcln.Funcdataoff[i]), int64(Thearch.Ptrsize))
|
||
|
|
} else {
|
||
|
|
// TODO: Dedup.
|
||
|
|
funcdata_bytes += pcln.Funcdata[i].Size
|
||
|
|
|
||
|
|
setaddrplus(Ctxt, ftab, int64(off)+int64(Thearch.Ptrsize)*int64(i), pcln.Funcdata[i], pcln.Funcdataoff[i])
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
off += int32(pcln.Nfuncdata) * int32(Thearch.Ptrsize)
|
||
|
|
}
|
||
|
|
|
||
|
|
if off != end {
|
||
|
|
Diag("bad math in functab: funcstart=%d off=%d but end=%d (npcdata=%d nfuncdata=%d ptrsize=%d)", funcstart, off, end, pcln.Npcdata, pcln.Nfuncdata, Thearch.Ptrsize)
|
||
|
|
Errorexit()
|
||
|
|
}
|
||
|
|
|
||
|
|
nfunc++
|
||
|
|
}
|
||
|
|
|
||
|
|
// Final entry of table is just end pc.
|
||
|
|
setaddrplus(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize), last, last.Size)
|
||
|
|
|
||
|
|
// Start file table.
|
||
|
|
start = int32(len(ftab.P))
|
||
|
|
|
||
|
|
start += int32(-len(ftab.P)) & (int32(Thearch.Ptrsize) - 1)
|
||
|
|
setuint32(Ctxt, ftab, 8+int64(Thearch.Ptrsize)+int64(nfunc)*2*int64(Thearch.Ptrsize)+int64(Thearch.Ptrsize), uint32(start))
|
||
|
|
|
||
|
|
Symgrow(Ctxt, ftab, int64(start)+(int64(Ctxt.Nhistfile)+1)*4)
|
||
|
|
setuint32(Ctxt, ftab, int64(start), uint32(Ctxt.Nhistfile))
|
||
|
|
for s = Ctxt.Filesyms; s != nil; s = s.Next {
|
||
|
|
setuint32(Ctxt, ftab, int64(start)+s.Value*4, uint32(ftabaddstring(ftab, s.Name)))
|
||
|
|
}
|
||
|
|
|
||
|
|
ftab.Size = int64(len(ftab.P))
|
||
|
|
|
||
|
|
if Debug['v'] != 0 {
|
||
|
|
fmt.Fprintf(&Bso, "%5.2f pclntab=%d bytes, funcdata total %d bytes\n", obj.Cputime(), int64(ftab.Size), int64(funcdata_bytes))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
const (
|
||
|
|
BUCKETSIZE = 256 * MINFUNC
|
||
|
|
SUBBUCKETS = 16
|
||
|
|
SUBBUCKETSIZE = BUCKETSIZE / SUBBUCKETS
|
||
|
|
NOIDX = 0x7fffffff
|
||
|
|
)
|
||
|
|
|
||
|
|
// findfunctab generates a lookup table to quickly find the containing
|
||
|
|
// function for a pc. See src/runtime/symtab.go:findfunc for details.
|
||
|
|
func findfunctab() {
|
||
|
|
var t *LSym
|
||
|
|
var s *LSym
|
||
|
|
var e *LSym
|
||
|
|
var idx int32
|
||
|
|
var i int32
|
||
|
|
var j int32
|
||
|
|
var nbuckets int32
|
||
|
|
var n int32
|
||
|
|
var base int32
|
||
|
|
var min int64
|
||
|
|
var max int64
|
||
|
|
var p int64
|
||
|
|
var q int64
|
||
|
|
var indexes []int32
|
||
|
|
|
||
|
|
t = Linklookup(Ctxt, "runtime.findfunctab", 0)
|
||
|
|
t.Type = SRODATA
|
||
|
|
t.Reachable = true
|
||
|
|
|
||
|
|
// find min and max address
|
||
|
|
min = Ctxt.Textp.Value
|
||
|
|
|
||
|
|
max = 0
|
||
|
|
for s = Ctxt.Textp; s != nil; s = s.Next {
|
||
|
|
max = s.Value + s.Size
|
||
|
|
}
|
||
|
|
|
||
|
|
// for each subbucket, compute the minimum of all symbol indexes
|
||
|
|
// that map to that subbucket.
|
||
|
|
n = int32((max - min + SUBBUCKETSIZE - 1) / SUBBUCKETSIZE)
|
||
|
|
|
||
|
|
indexes = make([]int32, n)
|
||
|
|
for i = 0; i < n; i++ {
|
||
|
|
indexes[i] = NOIDX
|
||
|
|
}
|
||
|
|
idx = 0
|
||
|
|
for s = Ctxt.Textp; s != nil; s = s.Next {
|
||
|
|
if container(s) != 0 {
|
||
|
|
continue
|
||
|
|
}
|
||
|
|
p = s.Value
|
||
|
|
e = s.Next
|
||
|
|
for container(e) != 0 {
|
||
|
|
e = e.Next
|
||
|
|
}
|
||
|
|
if e != nil {
|
||
|
|
q = e.Value
|
||
|
|
} else {
|
||
|
|
q = max
|
||
|
|
}
|
||
|
|
|
||
|
|
//print("%d: [%lld %lld] %s\n", idx, p, q, s->name);
|
||
|
|
for ; p < q; p += SUBBUCKETSIZE {
|
||
|
|
i = int32((p - min) / SUBBUCKETSIZE)
|
||
|
|
if indexes[i] > idx {
|
||
|
|
indexes[i] = idx
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
i = int32((q - 1 - min) / SUBBUCKETSIZE)
|
||
|
|
if indexes[i] > idx {
|
||
|
|
indexes[i] = idx
|
||
|
|
}
|
||
|
|
idx++
|
||
|
|
}
|
||
|
|
|
||
|
|
// allocate table
|
||
|
|
nbuckets = int32((max - min + BUCKETSIZE - 1) / BUCKETSIZE)
|
||
|
|
|
||
|
|
Symgrow(Ctxt, t, 4*int64(nbuckets)+int64(n))
|
||
|
|
|
||
|
|
// fill in table
|
||
|
|
for i = 0; i < nbuckets; i++ {
|
||
|
|
base = indexes[i*SUBBUCKETS]
|
||
|
|
if base == NOIDX {
|
||
|
|
Diag("hole in findfunctab")
|
||
|
|
}
|
||
|
|
setuint32(Ctxt, t, int64(i)*(4+SUBBUCKETS), uint32(base))
|
||
|
|
for j = 0; j < SUBBUCKETS && i*SUBBUCKETS+j < n; j++ {
|
||
|
|
idx = indexes[i*SUBBUCKETS+j]
|
||
|
|
if idx == NOIDX {
|
||
|
|
Diag("hole in findfunctab")
|
||
|
|
}
|
||
|
|
if idx-base >= 256 {
|
||
|
|
Diag("too many functions in a findfunc bucket! %d/%d %d %d", i, nbuckets, j, idx-base)
|
||
|
|
}
|
||
|
|
|
||
|
|
setuint8(Ctxt, t, int64(i)*(4+SUBBUCKETS)+4+int64(j), uint8(idx-base))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|