| // 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."); |
| } |