2004-04-19 19:29:25 +00:00
|
|
|
#include <u.h>
|
|
|
|
|
#include <libc.h>
|
|
|
|
|
#include <mach.h>
|
|
|
|
|
#include "stabs.h"
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
strcmpcolon(char *a, char *bcolon)
|
|
|
|
|
{
|
|
|
|
|
int i, len;
|
|
|
|
|
char *p;
|
|
|
|
|
|
|
|
|
|
p = strchr(bcolon, ':');
|
|
|
|
|
if(p == nil)
|
|
|
|
|
return strcmp(a, bcolon);
|
|
|
|
|
len = p-bcolon;
|
|
|
|
|
i = strncmp(a, bcolon, len);
|
|
|
|
|
if(i)
|
|
|
|
|
return i;
|
|
|
|
|
if(a[len] == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
stabcvtsym(StabSym *stab, Symbol *sym, char *dir, char *file, int i)
|
|
|
|
|
{
|
|
|
|
|
char *p;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Zero out the : to avoid allocating a new name string.
|
|
|
|
|
* The type info can be found by looking past the NUL.
|
|
|
|
|
* This is going to get us in trouble...
|
|
|
|
|
*/
|
|
|
|
|
if((p = strchr(stab->name, ':')) != nil)
|
|
|
|
|
*p++ = 0;
|
|
|
|
|
else
|
|
|
|
|
p = stab->name+strlen(stab->name)+1;
|
|
|
|
|
|
|
|
|
|
sym->name = stab->name;
|
|
|
|
|
sym->u.stabs.dir = dir;
|
|
|
|
|
sym->u.stabs.file = file;
|
|
|
|
|
sym->u.stabs.i = i;
|
|
|
|
|
switch(stab->type){
|
|
|
|
|
default:
|
|
|
|
|
return -1;
|
|
|
|
|
case N_FUN:
|
|
|
|
|
sym->class = CTEXT;
|
|
|
|
|
switch(*p){
|
|
|
|
|
default:
|
|
|
|
|
return -1;
|
|
|
|
|
case 'F': /* global function */
|
|
|
|
|
sym->type = 'T';
|
|
|
|
|
break;
|
|
|
|
|
case 'Q': /* static procedure */
|
|
|
|
|
case 'f': /* static function */
|
|
|
|
|
case 'I': /* nested procedure */
|
|
|
|
|
case 'J': /* nested function */
|
|
|
|
|
sym->type = 't';
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
sym->loc.type = LADDR;
|
|
|
|
|
sym->loc.addr = stab->value;
|
|
|
|
|
break;
|
|
|
|
|
case N_GSYM:
|
|
|
|
|
case N_PSYM:
|
|
|
|
|
case N_LSYM:
|
|
|
|
|
case N_LCSYM:
|
|
|
|
|
sym->class = CDATA;
|
|
|
|
|
sym->loc.type = LADDR;
|
|
|
|
|
sym->loc.addr = stab->value;
|
|
|
|
|
switch(*p){
|
|
|
|
|
default:
|
|
|
|
|
return -1;
|
|
|
|
|
case 'S': /* file-scope static variable */
|
|
|
|
|
sym->type = 'd';
|
|
|
|
|
break;
|
|
|
|
|
case 'G': /* global variable */
|
|
|
|
|
sym->type = 'D';
|
|
|
|
|
sym->loc.type = LNONE;
|
|
|
|
|
break;
|
|
|
|
|
case 'r': /* register variable */
|
|
|
|
|
sym->class = CAUTO;
|
|
|
|
|
sym->type = 'a';
|
|
|
|
|
sym->loc.type = LREG;
|
|
|
|
|
sym->loc.reg = "XXX";
|
|
|
|
|
break;
|
|
|
|
|
case 's': /* local variable */
|
|
|
|
|
sym->class = CAUTO;
|
|
|
|
|
sym->type = 'a';
|
|
|
|
|
sym->loc.type = LOFFSET;
|
|
|
|
|
sym->loc.offset = stab->value;
|
|
|
|
|
sym->loc.reg = "XXX";
|
|
|
|
|
break;
|
|
|
|
|
case 'a': /* by reference */
|
|
|
|
|
case 'D': /* f.p. parameter */
|
|
|
|
|
case 'i': /* register parameter */
|
|
|
|
|
case 'p': /* "normal" parameter */
|
|
|
|
|
case 'P': /* register parameter */
|
|
|
|
|
case 'v': /* by reference */
|
|
|
|
|
case 'X': /* function return variable */
|
|
|
|
|
sym->class = CPARAM;
|
|
|
|
|
sym->type = 'p';
|
|
|
|
|
if(*p == 'i'){
|
|
|
|
|
sym->loc.type = LREG;
|
|
|
|
|
sym->loc.reg = "XXX";
|
|
|
|
|
}else{
|
|
|
|
|
sym->loc.type = LOFFSET;
|
|
|
|
|
sym->loc.offset = stab->value;
|
|
|
|
|
sym->loc.reg = "XXX";
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
stabssyminit(Fhdr *fp)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
char *dir, *file;
|
|
|
|
|
Stab *stabs;
|
|
|
|
|
StabSym sym, lastfun;
|
|
|
|
|
Symbol s, *fun;
|
|
|
|
|
char **inc, **xinc;
|
|
|
|
|
int ninc, minc;
|
|
|
|
|
int locals, autos, params;
|
|
|
|
|
|
|
|
|
|
stabs = &fp->stabs;
|
|
|
|
|
if(stabs == nil){
|
|
|
|
|
werrstr("no stabs info");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dir = nil;
|
|
|
|
|
file = nil;
|
|
|
|
|
inc = nil;
|
|
|
|
|
fun = nil;
|
|
|
|
|
ninc = 0;
|
|
|
|
|
minc = 0;
|
|
|
|
|
locals = 0;
|
|
|
|
|
params = 0;
|
|
|
|
|
autos = 0;
|
|
|
|
|
memset(&lastfun, 0, sizeof lastfun);
|
|
|
|
|
for(i=0; stabsym(stabs, i, &sym)>=0; i++){
|
|
|
|
|
switch(sym.type){
|
|
|
|
|
case N_SO:
|
|
|
|
|
if(sym.name == nil || *sym.name == 0){
|
|
|
|
|
file = nil;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if(sym.name[strlen(sym.name)-1] == '/')
|
|
|
|
|
dir = sym.name;
|
|
|
|
|
else
|
|
|
|
|
file = sym.name;
|
|
|
|
|
break;
|
|
|
|
|
case N_BINCL:
|
|
|
|
|
if(ninc >= minc){
|
|
|
|
|
xinc = realloc(inc, (ninc+32)*sizeof(inc[0]));
|
|
|
|
|
if(xinc){
|
|
|
|
|
memset(xinc+ninc, 0, 32*sizeof(inc[0]));
|
|
|
|
|
inc = xinc;
|
|
|
|
|
}
|
|
|
|
|
ninc += 32;
|
|
|
|
|
}
|
|
|
|
|
if(ninc < minc)
|
|
|
|
|
inc[ninc] = sym.name;
|
|
|
|
|
ninc++;
|
|
|
|
|
break;
|
|
|
|
|
case N_EINCL:
|
|
|
|
|
if(ninc > 0)
|
|
|
|
|
ninc--;
|
|
|
|
|
break;
|
|
|
|
|
case N_EXCL:
|
|
|
|
|
/* condensed include - same effect as previous BINCL/EINCL pair */
|
|
|
|
|
break;
|
|
|
|
|
case N_GSYM: /* global variable */
|
|
|
|
|
/* only includes type, so useless for now */
|
|
|
|
|
break;
|
|
|
|
|
case N_FUN:
|
|
|
|
|
if(sym.name == nil){
|
|
|
|
|
/* marks end of function */
|
|
|
|
|
if(fun){
|
|
|
|
|
fun->hiloc.type = LADDR;
|
|
|
|
|
fun->hiloc.addr = fun->loc.addr + sym.value;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if(fun && lastfun.value==sym.value && lastfun.name==sym.name){
|
|
|
|
|
fun->u.stabs.locals = i;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
/* create new symbol, add it */
|
|
|
|
|
lastfun = sym;
|
|
|
|
|
fun = nil;
|
|
|
|
|
if(stabcvtsym(&sym, &s, dir, file, i) < 0)
|
|
|
|
|
continue;
|
2005-01-07 18:45:17 +00:00
|
|
|
if((fun = _addsym(fp, &s)) == nil)
|
2004-04-19 19:29:25 +00:00
|
|
|
goto err;
|
|
|
|
|
locals = 0;
|
|
|
|
|
params = 0;
|
|
|
|
|
autos = 0;
|
|
|
|
|
break;
|
|
|
|
|
case N_PSYM:
|
|
|
|
|
case N_LSYM:
|
|
|
|
|
case N_LCSYM:
|
|
|
|
|
if(fun){
|
|
|
|
|
if(fun->u.stabs.frameptr == -1){
|
|
|
|
|
/*
|
|
|
|
|
* Try to distinguish functions with a real frame pointer
|
|
|
|
|
* from functions with a virtual frame pointer, based on
|
|
|
|
|
* whether the first parameter is in the right location and
|
|
|
|
|
* whether the autos have negative offsets.
|
|
|
|
|
*
|
|
|
|
|
* This heuristic works most of the time. On the 386, we
|
|
|
|
|
* cannot distinguish between a v. function with no autos
|
|
|
|
|
* but a frame of size 4 and a f.p. function with no autos and
|
|
|
|
|
* no frame. Anything else we'll get right.
|
|
|
|
|
*
|
|
|
|
|
* Another way to go about this would be to have
|
|
|
|
|
* mach-specific functions to inspect the function
|
|
|
|
|
* prologues when we're not sure. What we have
|
|
|
|
|
* already should be enough, though.
|
|
|
|
|
*/
|
|
|
|
|
if(params==0 && sym.type == N_PSYM){
|
|
|
|
|
if(sym.value != 8 && sym.value >= 4){
|
|
|
|
|
/* XXX 386 specific, but let's find another system before generalizing */
|
|
|
|
|
fun->u.stabs.frameptr = 0;
|
|
|
|
|
fun->u.stabs.framesize = sym.value - 4;
|
|
|
|
|
}
|
|
|
|
|
}else if(sym.type == N_LSYM){
|
2012-10-21 11:25:08 -04:00
|
|
|
if((int32)sym.value >= 0){
|
2004-04-19 19:29:25 +00:00
|
|
|
fun->u.stabs.frameptr = 0;
|
|
|
|
|
if(params)
|
|
|
|
|
fun->u.stabs.framesize = 8 - 4;
|
|
|
|
|
}else
|
|
|
|
|
fun->u.stabs.frameptr = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if(sym.type == N_PSYM)
|
|
|
|
|
params++;
|
|
|
|
|
if(sym.type == N_LSYM)
|
|
|
|
|
autos++;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case N_STSYM: /* static file-scope variable */
|
|
|
|
|
/* create new symbol, add it */
|
|
|
|
|
if(stabcvtsym(&sym, &s, dir, file, i) < 0)
|
|
|
|
|
continue;
|
2005-01-07 18:45:17 +00:00
|
|
|
if(_addsym(fp, &s) == nil)
|
2004-04-19 19:29:25 +00:00
|
|
|
goto err;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-02-19 18:11:39 -05:00
|
|
|
USED(locals);
|
2004-04-19 19:29:25 +00:00
|
|
|
free(inc);
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
err:
|
|
|
|
|
free(inc);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
2012-02-19 18:11:39 -05:00
|
|
|
stabspc2file(Fhdr *fhdr, u64int pc, char *buf, uint nbuf, ulong *pline)
|
2004-04-19 19:29:25 +00:00
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
Symbol *s;
|
|
|
|
|
StabSym ss;
|
|
|
|
|
ulong line, basepc;
|
|
|
|
|
Loc l;
|
|
|
|
|
|
|
|
|
|
l.type = LADDR;
|
|
|
|
|
l.addr = pc;
|
|
|
|
|
if((s = ffindsym(fhdr, l, CTEXT)) == nil
|
|
|
|
|
|| stabsym(&fhdr->stabs, s->u.stabs.i, &ss) < 0)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
line = ss.desc;
|
|
|
|
|
basepc = ss.value;
|
|
|
|
|
for(i=s->u.stabs.i+1; stabsym(&fhdr->stabs, i, &ss) >= 0; i++){
|
|
|
|
|
if(ss.type == N_FUN && ss.name == nil)
|
|
|
|
|
break;
|
|
|
|
|
if(ss.type == N_SLINE){
|
|
|
|
|
if(basepc+ss.value > pc)
|
|
|
|
|
break;
|
|
|
|
|
else
|
|
|
|
|
line = ss.desc;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
*pline = line;
|
|
|
|
|
if(s->u.stabs.dir)
|
|
|
|
|
snprint(buf, nbuf, "%s%s", s->u.stabs.dir, s->u.stabs.file);
|
|
|
|
|
else
|
|
|
|
|
snprint(buf, nbuf, "%s", s->u.stabs.file);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
2012-02-19 18:11:39 -05:00
|
|
|
stabsline2pc(Fhdr *fhdr, u64int startpc, ulong line, u64int *pc)
|
2004-04-19 19:29:25 +00:00
|
|
|
{
|
|
|
|
|
int i, trigger;
|
|
|
|
|
Symbol *s;
|
|
|
|
|
StabSym ss;
|
|
|
|
|
ulong basepc;
|
|
|
|
|
Loc l;
|
|
|
|
|
|
|
|
|
|
l.type = LADDR;
|
|
|
|
|
l.addr = startpc;
|
|
|
|
|
if((s = ffindsym(fhdr, l, CTEXT)) == nil)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
trigger = 0;
|
|
|
|
|
line = ss.desc;
|
|
|
|
|
basepc = ss.value;
|
|
|
|
|
for(i=s->u.stabs.i+1; stabsym(&fhdr->stabs, i, &ss) >= 0; i++){
|
|
|
|
|
if(ss.type == N_FUN)
|
|
|
|
|
basepc = ss.value;
|
|
|
|
|
if(ss.type == N_SLINE){
|
|
|
|
|
if(basepc+ss.value >= startpc)
|
|
|
|
|
trigger = 1;
|
|
|
|
|
if(trigger && ss.desc >= line){
|
|
|
|
|
*pc = basepc+ss.value;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
stabslenum(Fhdr *fhdr, Symbol *p, char *name, uint j, Loc l, Symbol *s)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
StabSym ss;
|
|
|
|
|
|
|
|
|
|
for(i=p->u.stabs.locals; stabsym(&fhdr->stabs, i, &ss)>=0; i++){
|
|
|
|
|
if(ss.type == N_FUN && ss.name == nil)
|
|
|
|
|
break;
|
|
|
|
|
switch(ss.type){
|
|
|
|
|
case N_PSYM:
|
|
|
|
|
case N_LSYM:
|
|
|
|
|
case N_LCSYM:
|
|
|
|
|
if(name){
|
|
|
|
|
if(strcmpcolon(name, ss.name) != 0)
|
|
|
|
|
break;
|
|
|
|
|
}else if(l.type){
|
|
|
|
|
/* wait for now */
|
|
|
|
|
}else{
|
|
|
|
|
if(j-- > 0)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if(stabcvtsym(&ss, s, p->u.stabs.dir, p->u.stabs.file, i) < 0)
|
|
|
|
|
return -1;
|
|
|
|
|
if(s->loc.type == LOFFSET){
|
|
|
|
|
if(p->u.stabs.frameptr == 0)
|
|
|
|
|
s->loc.reg = mach->sp;
|
|
|
|
|
else
|
|
|
|
|
s->loc.reg = mach->fp;
|
|
|
|
|
}
|
|
|
|
|
if(l.type && loccmp(&l, &s->loc) != 0)
|
|
|
|
|
break;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static Loc zl;
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
stabslookuplsym(Fhdr *fhdr, Symbol *p, char *name, Symbol *s)
|
|
|
|
|
{
|
|
|
|
|
return stabslenum(fhdr, p, name, 0, zl, s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
stabsindexlsym(Fhdr *fhdr, Symbol *p, uint i, Symbol *s)
|
|
|
|
|
{
|
|
|
|
|
return stabslenum(fhdr, p, nil, i, zl, s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
|
stabsfindlsym(Fhdr *fhdr, Symbol *p, Loc l, Symbol *s)
|
|
|
|
|
{
|
|
|
|
|
return stabslenum(fhdr, p, nil, 0, l, s);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
symstabs(Fhdr *fp)
|
|
|
|
|
{
|
|
|
|
|
if(stabssyminit(fp) < 0)
|
|
|
|
|
return -1;
|
|
|
|
|
fp->pc2file = stabspc2file;
|
|
|
|
|
fp->line2pc = stabsline2pc;
|
|
|
|
|
fp->lookuplsym = stabslookuplsym;
|
|
|
|
|
fp->indexlsym = stabsindexlsym;
|
|
|
|
|
fp->findlsym = stabsfindlsym;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|