#include #include #include #include #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 */ long line; /* line # where it was included */ long 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 fmax; /* 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 long nauto; /* number of automatics */ static long nfiles; /* number of files */ static long nglob; /* number of globals */ static long nhist; /* number of history stack entries */ static long 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 void cleansyms(void); static long decodename(Biobuf*, Sym*); static short *encfname(char*); static int fline(char*, int, long, 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*, long*); static void printhist(char*, Hist*, int); static int buildtbls(void); static int symcomp(void*, void*); static int symerrmsg(int, char*); static int txtcomp(void*, void*); static int filecomp(void*, void*); /* * initialize the symbol tables */ int syminit(int fd, Fhdr *fp) { Sym *p; long 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; if((fp->_magic && (fp->magic & HDR_MAGIC)) || mach->szaddr == 8) svalsz = 8; else svalsz = 4; for(p = symbols; size < fp->symsz; p++, nsym++) { if(svalsz == 8){ if(Bread(&b, &vl, 8) != 8) return symerrmsg(8, "symbol"); p->value = beswav(vl); } else{ 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); /* 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 > fmax) fmax = 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, fmax); 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 long decodename(Biobuf *bp, Sym *p) { char *cp; int c1, c2; long 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; fmax = 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(long *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) { long i; int j, nh, ng, nt; File *f; Txtsym *tp; Hist *hp; Sym *p, **ap; if(isbuilt) return 1; isbuilt = 1; /* allocate the tables */ 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((fmax+1)*sizeof(*fnames)); if (!fnames) { werrstr("can't malloc file name table"); return 0; } memset(fnames, 0, (fmax+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++) { 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(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 */ } /* * find a function by name */ static int findtext(char *name, Symbol *s) { int i; for(i = 0; i < ntxt; i++) { if(strcmp(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) { long i; for(i = 0; i < nglob; i++) { if(strcmp(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 (strcmp(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(sp == nil) return -1; 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(sp == nil) return -1; 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, long line) { File *fp; long 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 <= fmax; 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, long *line) { Hist *hp; int offset, depth; long 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. */ long fileline(char *str, int n, uvlong dot) { long 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, long line, Hist *base, Hist **ret) { Hist *start; /* start of current level */ Hist *h; /* current entry */ long 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, "", 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; Sym *sym; bp = buf; end = buf+n-1; for(i = 1; j = (cp[i]<<8)|cp[i+1]; i+=2){ sym = fp[j]; if (sym == nil) break; c = sym->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(void *a, 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(void *a, 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(void *a, 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(pctxtend) 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 */ long pc2line(uvlong pc) { uchar *c, u; uvlong currpc; long currline; if(pcline == 0) return -1; currline = 0; currpc = txtstart-mach->pcquant; if(pctxtend) return ~0; for(c = pcline; c < pclineend; c++) { if(currpc >= pc) return currline; 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 ~0; } /* * 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(long line, uvlong basepc, uvlong endpc) { uchar *c, u; uvlong currpc, pc; long currline; long 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