blob: 15e1d28fabe6c72075068e03a48bff925f1cbd01 [file] [log] [blame]
// Copyright 2009 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.
// Runtime symbol table parsing.
// See http://golang.org/s/go12symtab for an overview.
package runtime
#include "runtime.h"
#include "defs_GOOS_GOARCH.h"
#include "os_GOOS.h"
#include "arch_GOARCH.h"
#include "malloc.h"
#include "funcdata.h"
typedef struct Ftab Ftab;
struct Ftab
{
uintptr entry;
uintptr funcoff;
};
extern byte pclntab[];
static Ftab *ftab;
static uintptr nftab;
static uint32 *filetab;
static uint32 nfiletab;
static String end = { (uint8*)"end", 3 };
void
runtime·symtabinit(void)
{
int32 i, j;
Func *f1, *f2;
// See golang.org/s/go12symtab for header: 0xfffffffb,
// two zero bytes, a byte giving the PC quantum,
// and a byte giving the pointer width in bytes.
if(*(uint32*)pclntab != 0xfffffffb || pclntab[4] != 0 || pclntab[5] != 0 || pclntab[6] != PCQuantum || pclntab[7] != sizeof(void*)) {
runtime·printf("runtime: function symbol table header: %x %x\n", *(uint32*)pclntab, *(uint32*)(pclntab+4));
runtime·throw("invalid function symbol table\n");
}
nftab = *(uintptr*)(pclntab+8);
ftab = (Ftab*)(pclntab+8+sizeof(void*));
for(i=0; i<nftab; i++) {
// NOTE: ftab[nftab].entry is legal; it is the address beyond the final function.
if(ftab[i].entry > ftab[i+1].entry) {
f1 = (Func*)(pclntab + ftab[i].funcoff);
f2 = (Func*)(pclntab + ftab[i+1].funcoff);
runtime·printf("function symbol table not sorted by program counter: %p %s > %p %s", ftab[i].entry, runtime·funcname(f1), ftab[i+1].entry, i+1 == nftab ? "end" : runtime·funcname(f2));
for(j=0; j<=i; j++)
runtime·printf("\t%p %s\n", ftab[j].entry, runtime·funcname((Func*)(pclntab + ftab[j].funcoff)));
runtime·throw("invalid runtime symbol table");
}
}
filetab = (uint32*)(pclntab + *(uint32*)&ftab[nftab].funcoff);
nfiletab = filetab[0];
}
static uint32
readvarint(byte **pp)
{
byte *p;
uint32 v;
int32 shift;
v = 0;
p = *pp;
for(shift = 0;; shift += 7) {
v |= (*p & 0x7F) << shift;
if(!(*p++ & 0x80))
break;
}
*pp = p;
return v;
}
void*
runtime·funcdata(Func *f, int32 i)
{
byte *p;
if(i < 0 || i >= f->nfuncdata)
return nil;
p = (byte*)&f->nfuncdata + 4 + f->npcdata*4;
if(sizeof(void*) == 8 && ((uintptr)p & 4)) {
if(((uintptr)f & 4))
runtime·printf("misaligned func %p\n", f);
p += 4;
}
return ((void**)p)[i];
}
static bool
step(byte **pp, uintptr *pc, int32 *value, bool first)
{
uint32 uvdelta, pcdelta;
int32 vdelta;
uvdelta = readvarint(pp);
if(uvdelta == 0 && !first)
return 0;
if(uvdelta&1)
uvdelta = ~(uvdelta>>1);
else
uvdelta >>= 1;
vdelta = (int32)uvdelta;
pcdelta = readvarint(pp) * PCQuantum;
*value += vdelta;
*pc += pcdelta;
return 1;
}
// Return associated data value for targetpc in func f.
// (Source file is f->src.)
static int32
pcvalue(Func *f, int32 off, uintptr targetpc, bool strict)
{
byte *p;
uintptr pc;
int32 value;
enum {
debug = 0
};
// The table is a delta-encoded sequence of (value, pc) pairs.
// Each pair states the given value is in effect up to pc.
// The value deltas are signed, zig-zag encoded.
// The pc deltas are unsigned.
// The starting value is -1, the starting pc is the function entry.
// The table ends at a value delta of 0 except in the first pair.
if(off == 0)
return -1;
p = pclntab + off;
pc = f->entry;
value = -1;
if(debug && !runtime·panicking)
runtime·printf("pcvalue start f=%s [%p] pc=%p targetpc=%p value=%d tab=%p\n",
runtime·funcname(f), f, pc, targetpc, value, p);
while(step(&p, &pc, &value, pc == f->entry)) {
if(debug)
runtime·printf("\tvalue=%d until pc=%p\n", value, pc);
if(targetpc < pc)
return value;
}
// If there was a table, it should have covered all program counters.
// If not, something is wrong.
if(runtime·panicking || !strict)
return -1;
runtime·printf("runtime: invalid pc-encoded table f=%s pc=%p targetpc=%p tab=%p\n",
runtime·funcname(f), pc, targetpc, p);
p = (byte*)f + off;
pc = f->entry;
value = -1;
while(step(&p, &pc, &value, pc == f->entry))
runtime·printf("\tvalue=%d until pc=%p\n", value, pc);
runtime·throw("invalid runtime symbol table");
return -1;
}
static String unknown = { (uint8*)"?", 1 };
int8*
runtime·funcname(Func *f)
{
if(f == nil || f->nameoff == 0)
return nil;
return (int8*)(pclntab + f->nameoff);
}
static int32
funcline(Func *f, uintptr targetpc, String *file, bool strict)
{
int32 line;
int32 fileno;
*file = unknown;
fileno = pcvalue(f, f->pcfile, targetpc, strict);
line = pcvalue(f, f->pcln, targetpc, strict);
if(fileno == -1 || line == -1 || fileno >= nfiletab) {
// runtime·printf("looking for %p in %S got file=%d line=%d\n", targetpc, *f->name, fileno, line);
return 0;
}
*file = runtime·gostringnocopy(pclntab + filetab[fileno]);
return line;
}
int32
runtime·funcline(Func *f, uintptr targetpc, String *file)
{
return funcline(f, targetpc, file, true);
}
int32
runtime·funcspdelta(Func *f, uintptr targetpc)
{
int32 x;
x = pcvalue(f, f->pcsp, targetpc, true);
if(x&(sizeof(void*)-1))
runtime·printf("invalid spdelta %d %d\n", f->pcsp, x);
return x;
}
int32
runtime·pcdatavalue(Func *f, int32 table, uintptr targetpc)
{
if(table < 0 || table >= f->npcdata)
return -1;
return pcvalue(f, (&f->nfuncdata)[1+table], targetpc, true);
}
int32
runtime·funcarglen(Func *f, uintptr targetpc)
{
if(targetpc == f->entry)
return 0;
return runtime·pcdatavalue(f, PCDATA_ArgSize, targetpc-PCQuantum);
}
func funcline_go(f *Func, targetpc uintptr) (retfile String, retline int) {
// Pass strict=false here, because anyone can call this function,
// and they might just be wrong about targetpc belonging to f.
retline = funcline(f, targetpc, &retfile, false);
}
func funcname_go(f *Func) (ret String) {
ret = runtime·gostringnocopy((uint8*)runtime·funcname(f));
}
func funcentry_go(f *Func) (ret uintptr) {
ret = f->entry;
}
Func*
runtime·findfunc(uintptr addr)
{
Ftab *f;
int32 nf, n;
if(nftab == 0)
return nil;
if(addr < ftab[0].entry || addr >= ftab[nftab].entry)
return nil;
// binary search to find func with entry <= addr.
f = ftab;
nf = nftab;
while(nf > 0) {
n = nf/2;
if(f[n].entry <= addr && addr < f[n+1].entry)
return (Func*)(pclntab + f[n].funcoff);
else if(addr < f[n].entry)
nf = n;
else {
f += n+1;
nf -= n+1;
}
}
// can't get here -- we already checked above
// that the address was in the table bounds.
// this can only happen if the table isn't sorted
// by address or if the binary search above is buggy.
runtime·prints("findfunc unreachable\n");
return nil;
}
func FuncForPC(pc uintptr) (ret *Func) {
ret = runtime·findfunc(pc);
}
static bool
hasprefix(String s, int8 *p)
{
int32 i;
for(i=0; i<s.len; i++) {
if(p[i] == 0)
return 1;
if(p[i] != s.str[i])
return 0;
}
return p[i] == 0;
}
static bool
contains(String s, int8 *p)
{
int32 i;
if(p[0] == 0)
return 1;
for(i=0; i<s.len; i++) {
if(s.str[i] != p[0])
continue;
if(hasprefix((String){s.str + i, s.len - i}, p))
return 1;
}
return 0;
}
bool
runtime·showframe(Func *f, G *gp)
{
static int32 traceback = -1;
String name;
if(m->throwing > 0 && gp != nil && (gp == m->curg || gp == m->caughtsig))
return 1;
if(traceback < 0)
traceback = runtime·gotraceback(nil);
name = runtime·gostringnocopy((uint8*)runtime·funcname(f));
// Special case: always show runtime.panic frame, so that we can
// see where a panic started in the middle of a stack trace.
// See golang.org/issue/5832.
if(name.len == 7+1+5 && hasprefix(name, "runtime.panic"))
return 1;
return traceback > 1 || f != nil && contains(name, ".") && !hasprefix(name, "runtime.");
}