summaryrefslogtreecommitdiff
path: root/src/pkg/runtime/symtab.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/runtime/symtab.c')
-rw-r--r--src/pkg/runtime/symtab.c466
1 files changed, 466 insertions, 0 deletions
diff --git a/src/pkg/runtime/symtab.c b/src/pkg/runtime/symtab.c
new file mode 100644
index 000000000..d2ebf9b40
--- /dev/null
+++ b/src/pkg/runtime/symtab.c
@@ -0,0 +1,466 @@
+// 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 access. Work in progress.
+// The Plan 9 symbol table is not in a particularly convenient form.
+// The routines here massage it into a more usable form; eventually
+// we'll change 6l to do this for us, but it is easier to experiment
+// here than to change 6l and all the other tools.
+//
+// The symbol table also needs to be better integrated with the type
+// strings table in the future. This is just a quick way to get started
+// and figure out exactly what we want.
+
+#include "runtime.h"
+#include "defs.h"
+#include "os.h"
+#include "arch.h"
+
+extern byte pclntab[], epclntab[], symtab[], esymtab[];
+
+typedef struct Sym Sym;
+struct Sym
+{
+ uintptr value;
+ byte symtype;
+ byte *name;
+// byte *gotype;
+};
+
+// Walk over symtab, calling fn(&s) for each symbol.
+static void
+walksymtab(void (*fn)(Sym*))
+{
+ byte *p, *ep, *q;
+ Sym s;
+
+ p = symtab;
+ ep = esymtab;
+ while(p < ep) {
+ if(p + 7 > ep)
+ break;
+ s.value = ((uint32)p[0]<<24) | ((uint32)p[1]<<16) | ((uint32)p[2]<<8) | ((uint32)p[3]);
+
+ if(!(p[4]&0x80))
+ break;
+ s.symtype = p[4] & ~0x80;
+ p += 5;
+ s.name = p;
+ if(s.symtype == 'z' || s.symtype == 'Z') {
+ // path reference string - skip first byte,
+ // then 2-byte pairs ending at two zeros.
+ q = p+1;
+ for(;;) {
+ if(q+2 > ep)
+ return;
+ if(q[0] == '\0' && q[1] == '\0')
+ break;
+ q += 2;
+ }
+ p = q+2;
+ }else{
+ q = runtime·mchr(p, '\0', ep);
+ if(q == nil)
+ break;
+ p = q+1;
+ }
+ p += 4; // go type
+ fn(&s);
+ }
+}
+
+// Symtab walker; accumulates info about functions.
+
+static Func *func;
+static int32 nfunc;
+
+static byte **fname;
+static int32 nfname;
+
+static uint32 funcinit;
+static Lock funclock;
+
+static void
+dofunc(Sym *sym)
+{
+ Func *f;
+
+ switch(sym->symtype) {
+ case 't':
+ case 'T':
+ case 'l':
+ case 'L':
+ if(runtime·strcmp(sym->name, (byte*)"etext") == 0)
+ break;
+ if(func == nil) {
+ nfunc++;
+ break;
+ }
+ f = &func[nfunc++];
+ f->name = runtime·gostringnocopy(sym->name);
+ f->entry = sym->value;
+ if(sym->symtype == 'L' || sym->symtype == 'l')
+ f->frame = -sizeof(uintptr);
+ break;
+ case 'm':
+ if(nfunc > 0 && func != nil)
+ func[nfunc-1].frame += sym->value;
+ break;
+ case 'p':
+ if(nfunc > 0 && func != nil) {
+ f = &func[nfunc-1];
+ // args counts 32-bit words.
+ // sym->value is the arg's offset.
+ // don't know width of this arg, so assume it is 64 bits.
+ if(f->args < sym->value/4 + 2)
+ f->args = sym->value/4 + 2;
+ }
+ break;
+ case 'f':
+ if(fname == nil) {
+ if(sym->value >= nfname) {
+ if(sym->value >= 0x10000) {
+ runtime·printf("invalid symbol file index %p\n", sym->value);
+ runtime·throw("mangled symbol table");
+ }
+ nfname = sym->value+1;
+ }
+ break;
+ }
+ fname[sym->value] = sym->name;
+ break;
+ }
+}
+
+// put together the path name for a z entry.
+// the f entries have been accumulated into fname already.
+static void
+makepath(byte *buf, int32 nbuf, byte *path)
+{
+ int32 n, len;
+ byte *p, *ep, *q;
+
+ if(nbuf <= 0)
+ return;
+
+ p = buf;
+ ep = buf + nbuf;
+ *p = '\0';
+ for(;;) {
+ if(path[0] == 0 && path[1] == 0)
+ break;
+ n = (path[0]<<8) | path[1];
+ path += 2;
+ if(n >= nfname)
+ break;
+ q = fname[n];
+ len = runtime·findnull(q);
+ if(p+1+len >= ep)
+ break;
+ if(p > buf && p[-1] != '/')
+ *p++ = '/';
+ runtime·memmove(p, q, len+1);
+ p += len;
+ }
+}
+
+// walk symtab accumulating path names for use by pc/ln table.
+// don't need the full generality of the z entry history stack because
+// there are no includes in go (and only sensible includes in our c);
+// assume code only appear in top-level files.
+static void
+dosrcline(Sym *sym)
+{
+ static byte srcbuf[1000];
+ static struct {
+ String srcstring;
+ int32 aline;
+ int32 delta;
+ } files[200];
+ static int32 incstart;
+ static int32 nfunc, nfile, nhist;
+ Func *f;
+ int32 i;
+
+ switch(sym->symtype) {
+ case 't':
+ case 'T':
+ if(runtime·strcmp(sym->name, (byte*)"etext") == 0)
+ break;
+ f = &func[nfunc++];
+ // find source file
+ for(i = 0; i < nfile - 1; i++) {
+ if (files[i+1].aline > f->ln0)
+ break;
+ }
+ f->src = files[i].srcstring;
+ f->ln0 -= files[i].delta;
+ break;
+ case 'z':
+ if(sym->value == 1) {
+ // entry for main source file for a new object.
+ makepath(srcbuf, sizeof srcbuf, sym->name+1);
+ nhist = 0;
+ nfile = 0;
+ if(nfile == nelem(files))
+ return;
+ files[nfile].srcstring = runtime·gostring(srcbuf);
+ files[nfile].aline = 0;
+ files[nfile++].delta = 0;
+ } else {
+ // push or pop of included file.
+ makepath(srcbuf, sizeof srcbuf, sym->name+1);
+ if(srcbuf[0] != '\0') {
+ if(nhist++ == 0)
+ incstart = sym->value;
+ if(nhist == 0 && nfile < nelem(files)) {
+ // new top-level file
+ files[nfile].srcstring = runtime·gostring(srcbuf);
+ files[nfile].aline = sym->value;
+ // this is "line 0"
+ files[nfile++].delta = sym->value - 1;
+ }
+ }else{
+ if(--nhist == 0)
+ files[nfile-1].delta += sym->value - incstart;
+ }
+ }
+ }
+}
+
+// Interpret pc/ln table, saving the subpiece for each func.
+static void
+splitpcln(void)
+{
+ int32 line;
+ uintptr pc;
+ byte *p, *ep;
+ Func *f, *ef;
+ int32 pcquant;
+
+ if(pclntab == epclntab || nfunc == 0)
+ return;
+
+ switch(thechar) {
+ case '5':
+ pcquant = 4;
+ break;
+ default: // 6, 8
+ pcquant = 1;
+ break;
+ }
+
+ // pc/ln table bounds
+ p = pclntab;
+ ep = epclntab;
+
+ f = func;
+ ef = func + nfunc;
+ pc = func[0].entry; // text base
+ f->pcln.array = p;
+ f->pc0 = pc;
+ line = 0;
+ for(;;) {
+ while(p < ep && *p > 128)
+ pc += pcquant * (*p++ - 128);
+ // runtime·printf("pc<%p targetpc=%p line=%d\n", pc, targetpc, line);
+ if(*p == 0) {
+ if(p+5 > ep)
+ break;
+ // 4 byte add to line
+ line += (p[1]<<24) | (p[2]<<16) | (p[3]<<8) | p[4];
+ p += 5;
+ } else if(*p <= 64)
+ line += *p++;
+ else
+ line -= *p++ - 64;
+
+ // pc, line now match.
+ // Because the state machine begins at pc==entry and line==0,
+ // it can happen - just at the beginning! - that the update may
+ // have updated line but left pc alone, to tell us the true line
+ // number for pc==entry. In that case, update f->ln0.
+ // Having the correct initial line number is important for choosing
+ // the correct file in dosrcline above.
+ if(f == func && pc == f->pc0) {
+ f->pcln.array = p;
+ f->pc0 = pc + pcquant;
+ f->ln0 = line;
+ }
+
+ if(f < ef && pc >= (f+1)->entry) {
+ f->pcln.len = p - f->pcln.array;
+ f->pcln.cap = f->pcln.len;
+ do
+ f++;
+ while(f < ef && pc >= (f+1)->entry);
+ f->pcln.array = p;
+ // pc0 and ln0 are the starting values for
+ // the loop over f->pcln, so pc must be
+ // adjusted by the same pcquant update
+ // that we're going to do as we continue our loop.
+ f->pc0 = pc + pcquant;
+ f->ln0 = line;
+ }
+
+ pc += pcquant;
+ }
+ if(f < ef) {
+ f->pcln.len = p - f->pcln.array;
+ f->pcln.cap = f->pcln.len;
+ }
+}
+
+
+// Return actual file line number for targetpc in func f.
+// (Source file is f->src.)
+// NOTE(rsc): If you edit this function, also edit extern.go:/FileLine
+int32
+runtime·funcline(Func *f, uintptr targetpc)
+{
+ byte *p, *ep;
+ uintptr pc;
+ int32 line;
+ int32 pcquant;
+
+ enum {
+ debug = 0
+ };
+
+ switch(thechar) {
+ case '5':
+ pcquant = 4;
+ break;
+ default: // 6, 8
+ pcquant = 1;
+ break;
+ }
+
+ p = f->pcln.array;
+ ep = p + f->pcln.len;
+ pc = f->pc0;
+ line = f->ln0;
+ if(debug && !runtime·panicking)
+ runtime·printf("funcline start pc=%p targetpc=%p line=%d tab=%p+%d\n",
+ pc, targetpc, line, p, (int32)f->pcln.len);
+ for(;;) {
+ // Table is a sequence of updates.
+
+ // Each update says first how to adjust the pc,
+ // in possibly multiple instructions...
+ while(p < ep && *p > 128)
+ pc += pcquant * (*p++ - 128);
+
+ if(debug && !runtime·panicking)
+ runtime·printf("pc<%p targetpc=%p line=%d\n", pc, targetpc, line);
+
+ // If the pc has advanced too far or we're out of data,
+ // stop and the last known line number.
+ if(pc > targetpc || p >= ep)
+ break;
+
+ // ... and then how to adjust the line number,
+ // in a single instruction.
+ if(*p == 0) {
+ if(p+5 > ep)
+ break;
+ line += (p[1]<<24) | (p[2]<<16) | (p[3]<<8) | p[4];
+ p += 5;
+ } else if(*p <= 64)
+ line += *p++;
+ else
+ line -= *p++ - 64;
+ // Now pc, line pair is consistent.
+ if(debug && !runtime·panicking)
+ runtime·printf("pc=%p targetpc=%p line=%d\n", pc, targetpc, line);
+
+ // PC increments implicitly on each iteration.
+ pc += pcquant;
+ }
+ return line;
+}
+
+static void
+buildfuncs(void)
+{
+ extern byte etext[];
+
+ if(func != nil)
+ return;
+
+ // Memory profiling uses this code;
+ // can deadlock if the profiler ends
+ // up back here.
+ m->nomemprof++;
+
+ // count funcs, fnames
+ nfunc = 0;
+ nfname = 0;
+ walksymtab(dofunc);
+
+ // initialize tables
+ func = runtime·mal((nfunc+1)*sizeof func[0]);
+ func[nfunc].entry = (uint64)etext;
+ fname = runtime·mal(nfname*sizeof fname[0]);
+ nfunc = 0;
+ walksymtab(dofunc);
+
+ // split pc/ln table by func
+ splitpcln();
+
+ // record src file and line info for each func
+ walksymtab(dosrcline);
+
+ m->nomemprof--;
+}
+
+Func*
+runtime·findfunc(uintptr addr)
+{
+ Func *f;
+ int32 nf, n;
+
+ // Use atomic double-checked locking,
+ // because when called from pprof signal
+ // handler, findfunc must run without
+ // grabbing any locks.
+ // (Before enabling the signal handler,
+ // SetCPUProfileRate calls findfunc to trigger
+ // the initialization outside the handler.)
+ if(runtime·atomicload(&funcinit) == 0) {
+ runtime·lock(&funclock);
+ if(funcinit == 0) {
+ buildfuncs();
+ runtime·atomicstore(&funcinit, 1);
+ }
+ runtime·unlock(&funclock);
+ }
+
+ if(nfunc == 0)
+ return nil;
+ if(addr < func[0].entry || addr >= func[nfunc].entry)
+ return nil;
+
+ // binary search to find func with entry <= addr.
+ f = func;
+ nf = nfunc;
+ while(nf > 0) {
+ n = nf/2;
+ if(f[n].entry <= addr && addr < f[n+1].entry)
+ return &f[n];
+ 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;
+}