mirror of
https://github.com/golang/go
synced 2024-10-04 15:11:20 -06:00
57a9bd0ee3
character string to machine address. not filled in, just carved out. R=austin DELTA=77 (11 added, 34 deleted, 32 changed) OCL=33122 CL=33124
1446 lines
29 KiB
C
1446 lines
29 KiB
C
// 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_amd64.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; 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(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
|