summaryrefslogtreecommitdiff
path: root/src/libmach/sym.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libmach/sym.c')
-rw-r--r--src/libmach/sym.c1443
1 files changed, 1443 insertions, 0 deletions
diff --git a/src/libmach/sym.c b/src/libmach/sym.c
new file mode 100644
index 000000000..5e4fdd867
--- /dev/null
+++ b/src/libmach/sym.c
@@ -0,0 +1,1443 @@
+// Inferno libmach/sym.c
+// http://code.google.com/p/inferno-os/source/browse/utils/libmach/sym.c
+//
+// Copyright © 1994-1999 Lucent Technologies Inc.
+// Power PC support Copyright © 1995-2004 C H Forsyth (forsyth@terzarima.net).
+// Portions Copyright © 1997-1999 Vita Nuova Limited.
+// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com).
+// Revisions Copyright © 2000-2004 Lucent Technologies Inc. and others.
+// Portions Copyright © 2009 The Go Authors. All rights reserved.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mach.h>
+
+#define HUGEINT 0x7fffffff
+#define NNAME 20 /* a relic of the past */
+
+typedef struct txtsym Txtsym;
+typedef struct file File;
+typedef struct hist Hist;
+
+struct txtsym { /* Text Symbol table */
+ int n; /* number of local vars */
+ Sym **locals; /* array of ptrs to autos */
+ Sym *sym; /* function symbol entry */
+};
+
+struct hist { /* Stack of include files & #line directives */
+ char *name; /* Assumes names Null terminated in file */
+ int32 line; /* line # where it was included */
+ int32 offset; /* line # of #line directive */
+};
+
+struct file { /* Per input file header to history stack */
+ uvlong addr; /* address of first text sym */
+ union {
+ Txtsym *txt; /* first text symbol */
+ Sym *sym; /* only during initilization */
+ };
+ int n; /* size of history stack */
+ Hist *hist; /* history stack */
+};
+
+static int debug = 0;
+
+static Sym **autos; /* Base of auto variables */
+static File *files; /* Base of file arena */
+static int fmaxi; /* largest file path index */
+static Sym **fnames; /* file names path component table */
+static Sym **globals; /* globals by addr table */
+static Hist *hist; /* base of history stack */
+static int isbuilt; /* internal table init flag */
+static int32 nauto; /* number of automatics */
+static int32 nfiles; /* number of files */
+static int32 nglob; /* number of globals */
+static int32 nhist; /* number of history stack entries */
+static int32 nsym; /* number of symbols */
+static int ntxt; /* number of text symbols */
+static uchar *pcline; /* start of pc-line state table */
+static uchar *pclineend; /* end of pc-line table */
+static uchar *spoff; /* start of pc-sp state table */
+static uchar *spoffend; /* end of pc-sp offset table */
+static Sym *symbols; /* symbol table */
+static Txtsym *txt; /* Base of text symbol table */
+static uvlong txtstart; /* start of text segment */
+static uvlong txtend; /* end of text segment */
+static uvlong firstinstr; /* as found from symtab; needed for amd64 */
+
+static void cleansyms(void);
+static int32 decodename(Biobuf*, Sym*);
+static short *encfname(char*);
+static int fline(char*, int, int32, Hist*, Hist**);
+static void fillsym(Sym*, Symbol*);
+static int findglobal(char*, Symbol*);
+static int findlocvar(Symbol*, char *, Symbol*);
+static int findtext(char*, Symbol*);
+static int hcomp(Hist*, short*);
+static int hline(File*, short*, int32*);
+static void printhist(char*, Hist*, int);
+static int buildtbls(void);
+static int symcomp(const void*, const void*);
+static int symerrmsg(int, char*);
+static int txtcomp(const void*, const void*);
+static int filecomp(const void*, const void*);
+
+/*
+ * initialize the symbol tables
+ */
+int
+syminit(int fd, Fhdr *fp)
+{
+ Sym *p;
+ int32 i, l, size;
+ vlong vl;
+ Biobuf b;
+ int svalsz;
+
+ if(fp->symsz == 0)
+ return 0;
+ if(fp->type == FNONE)
+ return 0;
+
+ cleansyms();
+ textseg(fp->txtaddr, fp);
+ /* minimum symbol record size = 4+1+2 bytes */
+ symbols = malloc((fp->symsz/(4+1+2)+1)*sizeof(Sym));
+ if(symbols == 0) {
+ werrstr("can't malloc %ld bytes", fp->symsz);
+ return -1;
+ }
+ Binit(&b, fd, OREAD);
+ Bseek(&b, fp->symoff, 0);
+ nsym = 0;
+ size = 0;
+ for(p = symbols; size < fp->symsz; p++, nsym++) {
+ if(fp->_magic && (fp->magic & HDR_MAGIC)){
+ svalsz = 8;
+ if(Bread(&b, &vl, 8) != 8)
+ return symerrmsg(8, "symbol");
+ p->value = beswav(vl);
+ }
+ else{
+ svalsz = 4;
+ if(Bread(&b, &l, 4) != 4)
+ return symerrmsg(4, "symbol");
+ p->value = (u32int)beswal(l);
+ }
+ if(Bread(&b, &p->type, sizeof(p->type)) != sizeof(p->type))
+ return symerrmsg(sizeof(p->value), "symbol");
+
+ i = decodename(&b, p);
+ if(i < 0)
+ return -1;
+ size += i+svalsz+sizeof(p->type);
+
+ if(svalsz == 8){
+ if(Bread(&b, &vl, 8) != 8)
+ return symerrmsg(8, "symbol");
+ p->gotype = beswav(vl);
+ }
+ else{
+ if(Bread(&b, &l, 4) != 4)
+ return symerrmsg(4, "symbol");
+ p->gotype = (u32int)beswal(l);
+ }
+ size += svalsz;
+
+ /* count global & auto vars, text symbols, and file names */
+ switch (p->type) {
+ case 'l':
+ case 'L':
+ case 't':
+ case 'T':
+ ntxt++;
+ break;
+ case 'd':
+ case 'D':
+ case 'b':
+ case 'B':
+ nglob++;
+ break;
+ case 'f':
+ if(strcmp(p->name, ".frame") == 0) {
+ p->type = 'm';
+ nauto++;
+ }
+ else if(p->value > fmaxi)
+ fmaxi = p->value; /* highest path index */
+ break;
+ case 'a':
+ case 'p':
+ case 'm':
+ nauto++;
+ break;
+ case 'z':
+ if(p->value == 1) { /* one extra per file */
+ nhist++;
+ nfiles++;
+ }
+ nhist++;
+ break;
+ default:
+ break;
+ }
+ }
+ if (debug)
+ print("NG: %ld NT: %d NF: %d\n", nglob, ntxt, fmaxi);
+ if (fp->sppcsz) { /* pc-sp offset table */
+ spoff = (uchar *)malloc(fp->sppcsz);
+ if(spoff == 0) {
+ werrstr("can't malloc %ld bytes", fp->sppcsz);
+ return -1;
+ }
+ Bseek(&b, fp->sppcoff, 0);
+ if(Bread(&b, spoff, fp->sppcsz) != fp->sppcsz){
+ spoff = 0;
+ return symerrmsg(fp->sppcsz, "sp-pc");
+ }
+ spoffend = spoff+fp->sppcsz;
+ }
+ if (fp->lnpcsz) { /* pc-line number table */
+ pcline = (uchar *)malloc(fp->lnpcsz);
+ if(pcline == 0) {
+ werrstr("can't malloc %ld bytes", fp->lnpcsz);
+ return -1;
+ }
+ Bseek(&b, fp->lnpcoff, 0);
+ if(Bread(&b, pcline, fp->lnpcsz) != fp->lnpcsz){
+ pcline = 0;
+ return symerrmsg(fp->lnpcsz, "pc-line");
+ }
+ pclineend = pcline+fp->lnpcsz;
+ }
+ return nsym;
+}
+
+static int
+symerrmsg(int n, char *table)
+{
+ werrstr("can't read %d bytes of %s table", n, table);
+ return -1;
+}
+
+static int32
+decodename(Biobuf *bp, Sym *p)
+{
+ char *cp;
+ int c1, c2;
+ int32 n;
+ vlong o;
+
+ if((p->type & 0x80) == 0) { /* old-style, fixed length names */
+ p->name = malloc(NNAME);
+ if(p->name == 0) {
+ werrstr("can't malloc %d bytes", NNAME);
+ return -1;
+ }
+ if(Bread(bp, p->name, NNAME) != NNAME)
+ return symerrmsg(NNAME, "symbol");
+ Bseek(bp, 3, 1);
+ return NNAME+3;
+ }
+
+ p->type &= ~0x80;
+ if(p->type == 'z' || p->type == 'Z') {
+ o = Bseek(bp, 0, 1);
+ if(Bgetc(bp) < 0) {
+ werrstr("can't read symbol name");
+ return -1;
+ }
+ for(;;) {
+ c1 = Bgetc(bp);
+ c2 = Bgetc(bp);
+ if(c1 < 0 || c2 < 0) {
+ werrstr("can't read symbol name");
+ return -1;
+ }
+ if(c1 == 0 && c2 == 0)
+ break;
+ }
+ n = Bseek(bp, 0, 1)-o;
+ p->name = malloc(n);
+ if(p->name == 0) {
+ werrstr("can't malloc %ld bytes", n);
+ return -1;
+ }
+ Bseek(bp, -n, 1);
+ if(Bread(bp, p->name, n) != n) {
+ werrstr("can't read %ld bytes of symbol name", n);
+ return -1;
+ }
+ } else {
+ cp = Brdline(bp, '\0');
+ if(cp == 0) {
+ werrstr("can't read symbol name");
+ return -1;
+ }
+ n = Blinelen(bp);
+ p->name = malloc(n);
+ if(p->name == 0) {
+ werrstr("can't malloc %ld bytes", n);
+ return -1;
+ }
+ strcpy(p->name, cp);
+ }
+ return n;
+}
+
+/*
+ * free any previously loaded symbol tables
+ */
+static void
+cleansyms(void)
+{
+ if(globals)
+ free(globals);
+ globals = 0;
+ nglob = 0;
+ if(txt)
+ free(txt);
+ txt = 0;
+ ntxt = 0;
+ if(fnames)
+ free(fnames);
+ fnames = 0;
+ fmaxi = 0;
+
+ if(files)
+ free(files);
+ files = 0;
+ nfiles = 0;
+ if(hist)
+ free(hist);
+ hist = 0;
+ nhist = 0;
+ if(autos)
+ free(autos);
+ autos = 0;
+ nauto = 0;
+ isbuilt = 0;
+ if(symbols)
+ free(symbols);
+ symbols = 0;
+ nsym = 0;
+ if(spoff)
+ free(spoff);
+ spoff = 0;
+ if(pcline)
+ free(pcline);
+ pcline = 0;
+}
+
+/*
+ * delimit the text segment
+ */
+void
+textseg(uvlong base, Fhdr *fp)
+{
+ txtstart = base;
+ txtend = base+fp->txtsz;
+}
+
+/*
+ * symbase: return base and size of raw symbol table
+ * (special hack for high access rate operations)
+ */
+Sym *
+symbase(int32 *n)
+{
+ *n = nsym;
+ return symbols;
+}
+
+/*
+ * Get the ith symbol table entry
+ */
+Sym *
+getsym(int index)
+{
+ if(index >= 0 && index < nsym)
+ return &symbols[index];
+ return 0;
+}
+
+/*
+ * initialize internal symbol tables
+ */
+static int
+buildtbls(void)
+{
+ int32 i;
+ int j, nh, ng, nt;
+ File *f;
+ Txtsym *tp;
+ Hist *hp;
+ Sym *p, **ap;
+
+ if(isbuilt)
+ return 1;
+ isbuilt = 1;
+ /* allocate the tables */
+ firstinstr = 0;
+ if(nglob) {
+ globals = malloc(nglob*sizeof(*globals));
+ if(!globals) {
+ werrstr("can't malloc global symbol table");
+ return 0;
+ }
+ }
+ if(ntxt) {
+ txt = malloc(ntxt*sizeof(*txt));
+ if (!txt) {
+ werrstr("can't malloc text symbol table");
+ return 0;
+ }
+ }
+ fnames = malloc((fmaxi+1)*sizeof(*fnames));
+ if (!fnames) {
+ werrstr("can't malloc file name table");
+ return 0;
+ }
+ memset(fnames, 0, (fmaxi+1)*sizeof(*fnames));
+ files = malloc(nfiles*sizeof(*files));
+ if(!files) {
+ werrstr("can't malloc file table");
+ return 0;
+ }
+ hist = malloc(nhist*sizeof(Hist));
+ if(hist == 0) {
+ werrstr("can't malloc history stack");
+ return 0;
+ }
+ autos = malloc(nauto*sizeof(Sym*));
+ if(autos == 0) {
+ werrstr("can't malloc auto symbol table");
+ return 0;
+ }
+ /* load the tables */
+ ng = nt = nh = 0;
+ f = 0;
+ tp = 0;
+ i = nsym;
+ hp = hist;
+ ap = autos;
+ for(p = symbols; i-- > 0; p++) {
+//print("sym %d type %c name %s value %llux\n", p-symbols, p->type, p->name, p->value);
+ switch(p->type) {
+ case 'D':
+ case 'd':
+ case 'B':
+ case 'b':
+ if(debug)
+ print("Global: %s %llux\n", p->name, p->value);
+ globals[ng++] = p;
+ break;
+ case 'z':
+ if(p->value == 1) { /* New file */
+ if(f) {
+ f->n = nh;
+ f->hist[nh].name = 0; /* one extra */
+ hp += nh+1;
+ f++;
+ }
+ else
+ f = files;
+ f->hist = hp;
+ f->sym = 0;
+ f->addr = 0;
+ nh = 0;
+ }
+ /* alloc one slot extra as terminator */
+ f->hist[nh].name = p->name;
+ f->hist[nh].line = p->value;
+ f->hist[nh].offset = 0;
+ if(debug)
+ printhist("-> ", &f->hist[nh], 1);
+ nh++;
+ break;
+ case 'Z':
+ if(f && nh > 0)
+ f->hist[nh-1].offset = p->value;
+ break;
+ case 'T':
+ case 't': /* Text: terminate history if first in file */
+ case 'L':
+ case 'l':
+ tp = &txt[nt++];
+ tp->n = 0;
+ tp->sym = p;
+ tp->locals = ap;
+ if(debug)
+ print("TEXT: %s at %llux\n", p->name, p->value);
+ if (firstinstr == 0 || p->value < firstinstr)
+ firstinstr = p->value;
+ if(f && !f->sym) { /* first */
+ f->sym = p;
+ f->addr = p->value;
+ }
+ break;
+ case 'a':
+ case 'p':
+ case 'm': /* Local Vars */
+ if(!tp)
+ print("Warning: Free floating local var: %s\n",
+ p->name);
+ else {
+ if(debug)
+ print("Local: %s %llux\n", p->name, p->value);
+ tp->locals[tp->n] = p;
+ tp->n++;
+ ap++;
+ }
+ break;
+ case 'f': /* File names */
+ if(debug)
+ print("Fname: %s\n", p->name);
+ fnames[p->value] = p;
+ break;
+ default:
+ break;
+ }
+ }
+ /* sort global and text tables into ascending address order */
+ qsort(globals, nglob, sizeof(Sym*), symcomp);
+ qsort(txt, ntxt, sizeof(Txtsym), txtcomp);
+ qsort(files, nfiles, sizeof(File), filecomp);
+ tp = txt;
+ for(i = 0, f = files; i < nfiles; i++, f++) {
+ for(j = 0; j < ntxt; j++) {
+ if(f->sym == tp->sym) {
+ if(debug) {
+ print("LINK: %s to at %llux", f->sym->name, f->addr);
+ printhist("... ", f->hist, 1);
+ }
+ f->txt = tp++;
+ break;
+ }
+ if(++tp >= txt+ntxt) /* wrap around */
+ tp = txt;
+ }
+ }
+ return 1;
+}
+
+/*
+ * find symbol function.var by name.
+ * fn != 0 && var != 0 => look for fn in text, var in data
+ * fn != 0 && var == 0 => look for fn in text
+ * fn == 0 && var != 0 => look for var first in text then in data space.
+ */
+int
+lookup(char *fn, char *var, Symbol *s)
+{
+ int found;
+
+ if(buildtbls() == 0)
+ return 0;
+ if(fn) {
+ found = findtext(fn, s);
+ if(var == 0) /* case 2: fn not in text */
+ return found;
+ else if(!found) /* case 1: fn not found */
+ return 0;
+ } else if(var) {
+ found = findtext(var, s);
+ if(found)
+ return 1; /* case 3: var found in text */
+ } else return 0; /* case 4: fn & var == zero */
+
+ if(found)
+ return findlocal(s, var, s); /* case 1: fn found */
+ return findglobal(var, s); /* case 3: var not found */
+
+}
+
+/*
+ * strcmp, but allow '_' to match center dot (rune 00b7 == bytes c2 b7)
+ */
+int
+cdotstrcmp(char *sym, char *user) {
+ for (;;) {
+ while (*sym == *user) {
+ if (*sym++ == '\0')
+ return 0;
+ user++;
+ }
+ /* unequal - but maybe '_' matches center dot */
+ if (user[0] == '_' && (sym[0]&0xFF) == 0xc2 && (sym[1]&0xFF) == 0xb7) {
+ /* '_' matches center dot - advance and continue */
+ user++;
+ sym += 2;
+ continue;
+ }
+ break;
+ }
+ return *user - *sym;
+}
+
+/*
+ * find a function by name
+ */
+static int
+findtext(char *name, Symbol *s)
+{
+ int i;
+
+ for(i = 0; i < ntxt; i++) {
+ if(cdotstrcmp(txt[i].sym->name, name) == 0) {
+ fillsym(txt[i].sym, s);
+ s->handle = (void *) &txt[i];
+ s->index = i;
+ return 1;
+ }
+ }
+ return 0;
+}
+/*
+ * find global variable by name
+ */
+static int
+findglobal(char *name, Symbol *s)
+{
+ int32 i;
+
+ for(i = 0; i < nglob; i++) {
+ if(cdotstrcmp(globals[i]->name, name) == 0) {
+ fillsym(globals[i], s);
+ s->index = i;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * find the local variable by name within a given function
+ */
+int
+findlocal(Symbol *s1, char *name, Symbol *s2)
+{
+ if(s1 == 0)
+ return 0;
+ if(buildtbls() == 0)
+ return 0;
+ return findlocvar(s1, name, s2);
+}
+
+/*
+ * find the local variable by name within a given function
+ * (internal function - does no parameter validation)
+ */
+static int
+findlocvar(Symbol *s1, char *name, Symbol *s2)
+{
+ Txtsym *tp;
+ int i;
+
+ tp = (Txtsym *)s1->handle;
+ if(tp && tp->locals) {
+ for(i = 0; i < tp->n; i++)
+ if (cdotstrcmp(tp->locals[i]->name, name) == 0) {
+ fillsym(tp->locals[i], s2);
+ s2->handle = (void *)tp;
+ s2->index = tp->n-1 - i;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Get ith text symbol
+ */
+int
+textsym(Symbol *s, int index)
+{
+
+ if(buildtbls() == 0)
+ return 0;
+ if(index < 0 || index >= ntxt)
+ return 0;
+ fillsym(txt[index].sym, s);
+ s->handle = (void *)&txt[index];
+ s->index = index;
+ return 1;
+}
+
+/*
+ * Get ith file name
+ */
+int
+filesym(int index, char *buf, int n)
+{
+ Hist *hp;
+
+ if(buildtbls() == 0)
+ return 0;
+ if(index < 0 || index >= nfiles)
+ return 0;
+ hp = files[index].hist;
+ if(!hp || !hp->name)
+ return 0;
+ return fileelem(fnames, (uchar*)hp->name, buf, n);
+}
+
+/*
+ * Lookup name of local variable located at an offset into the frame.
+ * The type selects either a parameter or automatic.
+ */
+int
+getauto(Symbol *s1, int off, int type, Symbol *s2)
+{
+ Txtsym *tp;
+ Sym *p;
+ int i, t;
+
+ if(s1 == 0)
+ return 0;
+ if(type == CPARAM)
+ t = 'p';
+ else if(type == CAUTO)
+ t = 'a';
+ else
+ return 0;
+ if(buildtbls() == 0)
+ return 0;
+ tp = (Txtsym *)s1->handle;
+ if(tp == 0)
+ return 0;
+ for(i = 0; i < tp->n; i++) {
+ p = tp->locals[i];
+ if(p->type == t && p->value == off) {
+ fillsym(p, s2);
+ s2->handle = s1->handle;
+ s2->index = tp->n-1 - i;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Find text symbol containing addr; binary search assumes text array is sorted by addr
+ */
+static int
+srchtext(uvlong addr)
+{
+ uvlong val;
+ int top, bot, mid;
+ Sym *sp;
+
+ val = addr;
+ bot = 0;
+ top = ntxt;
+ for (mid = (bot+top)/2; mid < top; mid = (bot+top)/2) {
+ sp = txt[mid].sym;
+ if(val < sp->value)
+ top = mid;
+ else if(mid != ntxt-1 && val >= txt[mid+1].sym->value)
+ bot = mid;
+ else
+ return mid;
+ }
+ return -1;
+}
+
+/*
+ * Find data symbol containing addr; binary search assumes data array is sorted by addr
+ */
+static int
+srchdata(uvlong addr)
+{
+ uvlong val;
+ int top, bot, mid;
+ Sym *sp;
+
+ bot = 0;
+ top = nglob;
+ val = addr;
+ for(mid = (bot+top)/2; mid < top; mid = (bot+top)/2) {
+ sp = globals[mid];
+ if(val < sp->value)
+ top = mid;
+ else if(mid < nglob-1 && val >= globals[mid+1]->value)
+ bot = mid;
+ else
+ return mid;
+ }
+ return -1;
+}
+
+/*
+ * Find symbol containing val in specified search space
+ * There is a special case when a value falls beyond the end
+ * of the text segment; if the search space is CTEXT, that value
+ * (usually etext) is returned. If the search space is CANY, symbols in the
+ * data space are searched for a match.
+ */
+int
+findsym(uvlong val, int type, Symbol *s)
+{
+ int i;
+
+ if(buildtbls() == 0)
+ return 0;
+
+ if(type == CTEXT || type == CANY) {
+ i = srchtext(val);
+ if(i >= 0) {
+ if(type == CTEXT || i != ntxt-1) {
+ fillsym(txt[i].sym, s);
+ s->handle = (void *) &txt[i];
+ s->index = i;
+ return 1;
+ }
+ }
+ }
+ if(type == CDATA || type == CANY) {
+ i = srchdata(val);
+ if(i >= 0) {
+ fillsym(globals[i], s);
+ s->index = i;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Find the start and end address of the function containing addr
+ */
+int
+fnbound(uvlong addr, uvlong *bounds)
+{
+ int i;
+
+ if(buildtbls() == 0)
+ return 0;
+
+ i = srchtext(addr);
+ if(0 <= i && i < ntxt-1) {
+ bounds[0] = txt[i].sym->value;
+ bounds[1] = txt[i+1].sym->value;
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * get the ith local symbol for a function
+ * the input symbol table is reverse ordered, so we reverse
+ * accesses here to maintain approx. parameter ordering in a stack trace.
+ */
+int
+localsym(Symbol *s, int index)
+{
+ Txtsym *tp;
+
+ if(s == 0 || index < 0)
+ return 0;
+ if(buildtbls() == 0)
+ return 0;
+
+ tp = (Txtsym *)s->handle;
+ if(tp && tp->locals && index < tp->n) {
+ fillsym(tp->locals[tp->n-index-1], s); /* reverse */
+ s->handle = (void *)tp;
+ s->index = index;
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * get the ith global symbol
+ */
+int
+globalsym(Symbol *s, int index)
+{
+ if(s == 0)
+ return 0;
+ if(buildtbls() == 0)
+ return 0;
+
+ if(index >=0 && index < nglob) {
+ fillsym(globals[index], s);
+ s->index = index;
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * find the pc given a file name and line offset into it.
+ */
+uvlong
+file2pc(char *file, int32 line)
+{
+ File *fp;
+ int32 i;
+ uvlong pc, start, end;
+ short *name;
+
+ if(buildtbls() == 0 || files == 0)
+ return ~0;
+ name = encfname(file);
+ if(name == 0) { /* encode the file name */
+ werrstr("file %s not found", file);
+ return ~0;
+ }
+ /* find this history stack */
+ for(i = 0, fp = files; i < nfiles; i++, fp++)
+ if (hline(fp, name, &line))
+ break;
+ free(name);
+ if(i >= nfiles) {
+ werrstr("line %ld in file %s not found", line, file);
+ return ~0;
+ }
+ start = fp->addr; /* first text addr this file */
+ if(i < nfiles-1)
+ end = (fp+1)->addr; /* first text addr next file */
+ else
+ end = 0; /* last file in load module */
+ /*
+ * At this point, line contains the offset into the file.
+ * run the state machine to locate the pc closest to that value.
+ */
+ if(debug)
+ print("find pc for %ld - between: %llux and %llux\n", line, start, end);
+ pc = line2addr(line, start, end);
+ if(pc == ~0) {
+ werrstr("line %ld not in file %s", line, file);
+ return ~0;
+ }
+ return pc;
+}
+
+/*
+ * search for a path component index
+ */
+static int
+pathcomp(char *s, int n)
+{
+ int i;
+
+ for(i = 0; i <= fmaxi; i++)
+ if(fnames[i] && strncmp(s, fnames[i]->name, n) == 0)
+ return i;
+ return -1;
+}
+
+/*
+ * Encode a char file name as a sequence of short indices
+ * into the file name dictionary.
+ */
+static short*
+encfname(char *file)
+{
+ int i, j;
+ char *cp, *cp2;
+ short *dest;
+
+ if(*file == '/') /* always check first '/' */
+ cp2 = file+1;
+ else {
+ cp2 = strchr(file, '/');
+ if(!cp2)
+ cp2 = strchr(file, 0);
+ }
+ cp = file;
+ dest = 0;
+ for(i = 0; *cp; i++) {
+ j = pathcomp(cp, cp2-cp);
+ if(j < 0)
+ return 0; /* not found */
+ dest = realloc(dest, (i+1)*sizeof(short));
+ dest[i] = j;
+ cp = cp2;
+ while(*cp == '/') /* skip embedded '/'s */
+ cp++;
+ cp2 = strchr(cp, '/');
+ if(!cp2)
+ cp2 = strchr(cp, 0);
+ }
+ dest = realloc(dest, (i+1)*sizeof(short));
+ dest[i] = 0;
+ return dest;
+}
+
+/*
+ * Search a history stack for a matching file name accumulating
+ * the size of intervening files in the stack.
+ */
+static int
+hline(File *fp, short *name, int32 *line)
+{
+ Hist *hp;
+ int offset, depth;
+ int32 ln;
+
+ for(hp = fp->hist; hp->name; hp++) /* find name in stack */
+ if(hp->name[1] || hp->name[2]) {
+ if(hcomp(hp, name))
+ break;
+ }
+ if(!hp->name) /* match not found */
+ return 0;
+ if(debug)
+ printhist("hline found ... ", hp, 1);
+ /*
+ * unwind the stack until empty or we hit an entry beyond our line
+ */
+ ln = *line;
+ offset = hp->line-1;
+ depth = 1;
+ for(hp++; depth && hp->name; hp++) {
+ if(debug)
+ printhist("hline inspect ... ", hp, 1);
+ if(hp->name[1] || hp->name[2]) {
+ if(hp->offset){ /* Z record */
+ offset = 0;
+ if(hcomp(hp, name)) {
+ if(*line <= hp->offset)
+ break;
+ ln = *line+hp->line-hp->offset;
+ depth = 1; /* implicit pop */
+ } else
+ depth = 2; /* implicit push */
+ } else if(depth == 1 && ln < hp->line-offset)
+ break; /* Beyond our line */
+ else if(depth++ == 1) /* push */
+ offset -= hp->line;
+ } else if(--depth == 1) /* pop */
+ offset += hp->line;
+ }
+ *line = ln+offset;
+ return 1;
+}
+
+/*
+ * compare two encoded file names
+ */
+static int
+hcomp(Hist *hp, short *sp)
+{
+ uchar *cp;
+ int i, j;
+ short *s;
+
+ cp = (uchar *)hp->name;
+ s = sp;
+ if (*s == 0)
+ return 0;
+ for (i = 1; j = (cp[i]<<8)|cp[i+1]; i += 2) {
+ if(j == 0)
+ break;
+ if(*s == j)
+ s++;
+ else
+ s = sp;
+ }
+ return *s == 0;
+}
+
+/*
+ * Convert a pc to a "file:line {file:line}" string.
+ */
+int32
+fileline(char *str, int n, uvlong dot)
+{
+ int32 line, top, bot, mid;
+ File *f;
+
+ *str = 0;
+ if(buildtbls() == 0)
+ return 0;
+ /* binary search assumes file list is sorted by addr */
+ bot = 0;
+ top = nfiles;
+ for (mid = (bot+top)/2; mid < top; mid = (bot+top)/2) {
+ f = &files[mid];
+ if(dot < f->addr)
+ top = mid;
+ else if(mid < nfiles-1 && dot >= (f+1)->addr)
+ bot = mid;
+ else {
+ line = pc2line(dot);
+ if(line > 0 && fline(str, n, line, f->hist, 0) >= 0)
+ return 1;
+ break;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Convert a line number within a composite file to relative line
+ * number in a source file. A composite file is the source
+ * file with included files inserted in line.
+ */
+static int
+fline(char *str, int n, int32 line, Hist *base, Hist **ret)
+{
+ Hist *start; /* start of current level */
+ Hist *h; /* current entry */
+ int32 delta; /* sum of size of files this level */
+ int k;
+
+ start = base;
+ h = base;
+ delta = h->line;
+ while(h && h->name && line > h->line) {
+ if(h->name[1] || h->name[2]) {
+ if(h->offset != 0) { /* #line Directive */
+ delta = h->line-h->offset+1;
+ start = h;
+ base = h++;
+ } else { /* beginning of File */
+ if(start == base)
+ start = h++;
+ else {
+ k = fline(str, n, line, start, &h);
+ if(k <= 0)
+ return k;
+ }
+ }
+ } else {
+ if(start == base && ret) { /* end of recursion level */
+ *ret = h;
+ return 1;
+ } else { /* end of included file */
+ delta += h->line-start->line;
+ h++;
+ start = base;
+ }
+ }
+ }
+ if(!h)
+ return -1;
+ if(start != base)
+ line = line-start->line+1;
+ else
+ line = line-delta+1;
+ if(!h->name)
+ strncpy(str, "<eof>", n);
+ else {
+ k = fileelem(fnames, (uchar*)start->name, str, n);
+ if(k+8 < n)
+ sprint(str+k, ":%ld", line);
+ }
+/**********Remove comments for complete back-trace of include sequence
+ * if(start != base) {
+ * k = strlen(str);
+ * if(k+2 < n) {
+ * str[k++] = ' ';
+ * str[k++] = '{';
+ * }
+ * k += fileelem(fnames, (uchar*) base->name, str+k, n-k);
+ * if(k+10 < n)
+ * sprint(str+k, ":%ld}", start->line-delta);
+ * }
+ ********************/
+ return 0;
+}
+
+/*
+ * convert an encoded file name to a string.
+ */
+int
+fileelem(Sym **fp, uchar *cp, char *buf, int n)
+{
+ int i, j;
+ char *c, *bp, *end;
+
+ bp = buf;
+ end = buf+n-1;
+ for(i = 1; j = (cp[i]<<8)|cp[i+1]; i+=2){
+ c = fp[j]->name;
+ if(bp != buf && bp[-1] != '/' && bp < end)
+ *bp++ = '/';
+ while(bp < end && *c)
+ *bp++ = *c++;
+ }
+ *bp = 0;
+ i = bp-buf;
+ if(i > 1) {
+ cleanname(buf);
+ i = strlen(buf);
+ }
+ return i;
+}
+
+/*
+ * compare the values of two symbol table entries.
+ */
+static int
+symcomp(const void *a, const void *b)
+{
+ int i;
+
+ i = (*(Sym**)a)->value - (*(Sym**)b)->value;
+ if (i)
+ return i;
+ return strcmp((*(Sym**)a)->name, (*(Sym**)b)->name);
+}
+
+/*
+ * compare the values of the symbols referenced by two text table entries
+ */
+static int
+txtcomp(const void *a, const void *b)
+{
+ return ((Txtsym*)a)->sym->value - ((Txtsym*)b)->sym->value;
+}
+
+/*
+ * compare the values of the symbols referenced by two file table entries
+ */
+static int
+filecomp(const void *a, const void *b)
+{
+ return ((File*)a)->addr - ((File*)b)->addr;
+}
+
+/*
+ * fill an interface Symbol structure from a symbol table entry
+ */
+static void
+fillsym(Sym *sp, Symbol *s)
+{
+ s->type = sp->type;
+ s->value = sp->value;
+ s->name = sp->name;
+ s->index = 0;
+ switch(sp->type) {
+ case 'b':
+ case 'B':
+ case 'D':
+ case 'd':
+ s->class = CDATA;
+ break;
+ case 't':
+ case 'T':
+ case 'l':
+ case 'L':
+ s->class = CTEXT;
+ break;
+ case 'a':
+ s->class = CAUTO;
+ break;
+ case 'p':
+ s->class = CPARAM;
+ break;
+ case 'm':
+ s->class = CSTAB;
+ break;
+ default:
+ s->class = CNONE;
+ break;
+ }
+ s->handle = 0;
+}
+
+/*
+ * find the stack frame, given the pc
+ */
+uvlong
+pc2sp(uvlong pc)
+{
+ uchar *c, u;
+ uvlong currpc, currsp;
+
+ if(spoff == 0)
+ return ~0;
+ currsp = 0;
+ currpc = txtstart - mach->pcquant;
+
+ if(pc<currpc || pc>txtend)
+ return ~0;
+ for(c = spoff; c < spoffend; c++) {
+ if (currpc >= pc)
+ return currsp;
+ u = *c;
+ if (u == 0) {
+ currsp += (c[1]<<24)|(c[2]<<16)|(c[3]<<8)|c[4];
+ c += 4;
+ }
+ else if (u < 65)
+ currsp += 4*u;
+ else if (u < 129)
+ currsp -= 4*(u-64);
+ else
+ currpc += mach->pcquant*(u-129);
+ currpc += mach->pcquant;
+ }
+ return ~0;
+}
+
+/*
+ * find the source file line number for a given value of the pc
+ */
+int32
+pc2line(uvlong pc)
+{
+ uchar *c, u;
+ uvlong currpc;
+ int32 currline;
+
+ if(pcline == 0)
+ return -1;
+ currline = 0;
+ if (firstinstr != 0)
+ currpc = firstinstr-mach->pcquant;
+ else
+ currpc = txtstart-mach->pcquant;
+ if(pc<currpc || pc>txtend)
+ return ~0;
+
+ for(c = pcline; c < pclineend && currpc < pc; c++) {
+ u = *c;
+ if(u == 0) {
+ currline += (c[1]<<24)|(c[2]<<16)|(c[3]<<8)|c[4];
+ c += 4;
+ }
+ else if(u < 65)
+ currline += u;
+ else if(u < 129)
+ currline -= (u-64);
+ else
+ currpc += mach->pcquant*(u-129);
+ currpc += mach->pcquant;
+ }
+ return currline;
+}
+
+/*
+ * find the pc associated with a line number
+ * basepc and endpc are text addresses bounding the search.
+ * if endpc == 0, the end of the table is used (i.e., no upper bound).
+ * usually, basepc and endpc contain the first text address in
+ * a file and the first text address in the following file, respectively.
+ */
+uvlong
+line2addr(int32 line, uvlong basepc, uvlong endpc)
+{
+ uchar *c, u;
+ uvlong currpc, pc;
+ int32 currline;
+ int32 delta, d;
+ int found;
+
+ if(pcline == 0 || line == 0)
+ return ~0;
+
+ currline = 0;
+ currpc = txtstart-mach->pcquant;
+ pc = ~0;
+ found = 0;
+ delta = HUGEINT;
+
+ for(c = pcline; c < pclineend; c++) {
+ if(endpc && currpc >= endpc) /* end of file of interest */
+ break;
+ if(currpc >= basepc) { /* proper file */
+ if(currline >= line) {
+ d = currline-line;
+ found = 1;
+ } else
+ d = line-currline;
+ if(d < delta) {
+ delta = d;
+ pc = currpc;
+ }
+ }
+ u = *c;
+ if(u == 0) {
+ currline += (c[1]<<24)|(c[2]<<16)|(c[3]<<8)|c[4];
+ c += 4;
+ }
+ else if(u < 65)
+ currline += u;
+ else if(u < 129)
+ currline -= (u-64);
+ else
+ currpc += mach->pcquant*(u-129);
+ currpc += mach->pcquant;
+ }
+ if(found)
+ return pc;
+ return ~0;
+}
+
+/*
+ * Print a history stack (debug). if count is 0, prints the whole stack
+ */
+static void
+printhist(char *msg, Hist *hp, int count)
+{
+ int i;
+ uchar *cp;
+ char buf[128];
+
+ i = 0;
+ while(hp->name) {
+ if(count && ++i > count)
+ break;
+ print("%s Line: %lx (%ld) Offset: %lx (%ld) Name: ", msg,
+ hp->line, hp->line, hp->offset, hp->offset);
+ for(cp = (uchar *)hp->name+1; (*cp<<8)|cp[1]; cp += 2) {
+ if (cp != (uchar *)hp->name+1)
+ print("/");
+ print("%x", (*cp<<8)|cp[1]);
+ }
+ fileelem(fnames, (uchar *) hp->name, buf, sizeof(buf));
+ print(" (%s)\n", buf);
+ hp++;
+ }
+}
+
+#ifdef DEBUG
+/*
+ * print the history stack for a file. (debug only)
+ * if (name == 0) => print all history stacks.
+ */
+void
+dumphist(char *name)
+{
+ int i;
+ File *f;
+ short *fname;
+
+ if(buildtbls() == 0)
+ return;
+ if(name)
+ fname = encfname(name);
+ for(i = 0, f = files; i < nfiles; i++, f++)
+ if(fname == 0 || hcomp(f->hist, fname))
+ printhist("> ", f->hist, f->n);
+
+ if(fname)
+ free(fname);
+}
+#endif