diff options
Diffstat (limited to 'src/pkg/runtime/symtab.goc')
-rw-r--r-- | src/pkg/runtime/symtab.goc | 332 |
1 files changed, 332 insertions, 0 deletions
diff --git a/src/pkg/runtime/symtab.goc b/src/pkg/runtime/symtab.goc new file mode 100644 index 000000000..15e1d28fa --- /dev/null +++ b/src/pkg/runtime/symtab.goc @@ -0,0 +1,332 @@ +// 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."); +} |