blob: 77c6bab75405460bd2b48d4101ecb4efc76c0ab1 [file] [log] [blame]
#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;
if((fun = _addsym(fp, &s)) == nil)
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){
if((int32)sym.value >= 0){
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;
if(_addsym(fp, &s) == nil)
goto err;
break;
}
}
USED(locals);
free(inc);
return 0;
err:
free(inc);
return -1;
}
static int
stabspc2file(Fhdr *fhdr, u64int pc, char *buf, uint nbuf, ulong *pline)
{
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
stabsline2pc(Fhdr *fhdr, u64int startpc, ulong line, u64int *pc)
{
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;
}