// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Parse stabs debug info.
#include "a.h"
int stabsdebug = 1;
// Hash table for type lookup by number.
Type *hash[1024];
// Look up type by number pair.
// TODO(rsc): Iant points out that n1 and n2 are always small and dense,
// so an array of arrays would be a better representation.
Type*
typebynum(uint n1, uint n2)
{
uint h;
Type *t;
h = (n1*53+n2) % nelem(hash);
for(t=hash[h]; t; t=t->next)
if(t->n1 == n1 && t->n2 == n2)
return t;
t = emalloc(sizeof *t);
t->next = hash[h];
hash[h] = t;
t->n1 = n1;
t->n2 = n2;
return t;
}
// Parse name and colon from *pp, leaving copy in *sp.
static int
parsename(char **pp, char **sp)
{
char *p;
char *s;
p = *pp;
while(*p != '\0' && *p != ':')
p++;
if(*p == '\0') {
fprint(2, "parsename expected colon\n");
return -1;
}
s = emalloc(p - *pp + 1);
memmove(s, *pp, p - *pp);
*sp = s;
*pp = p+1;
return 0;
}
// Parse single number from *pp.
static int
parsenum1(char **pp, vlong *np)
{
char *p;
p = *pp;
if(*p != '-' && (*p < '0' || *p > '9')) {
fprint(2, "parsenum expected minus or digit\n");
return -1;
}
*np = strtoll(p, pp, 10);
return 0;
}
// Parse type number - either single number or (n1, n2).
static int
parsetypenum(char **pp, vlong *n1p, vlong *n2p)
{
char *p;
p = *pp;
if(*p == '(') {
p++;
if(parsenum1(&p, n1p) < 0)
return -1;
if(*p++ != ',') {
if(stabsdebug)
fprint(2, "parsetypenum expected comma\n");
return -1;
}
if(parsenum1(&p, n2p) < 0)
return -1;
if(*p++ != ')') {
if(stabsdebug)
fprint(2, "parsetypenum expected right paren\n");
return -1;
}
*pp = p;
return 0;
}
if(parsenum1(&p, n1p) < 0)
return -1;
*n2p = 0;
*pp = p;
return 0;
}
// Integer types are represented in stabs as a "range"
// type with a lo and a hi value. The lo and hi used to
// be lo and hi for the type, but there are now odd
// extensions for floating point and 64-bit numbers.
//
// Have to keep signs separate from values because
// Int64's lo is -0.
typedef struct Intrange Intrange;
struct Intrange
{
int signlo; // sign of lo
vlong lo;
int signhi; // sign of hi
vlong hi;
int kind;
};
// NOTE(rsc): Iant says that these might be different depending
// on the gcc mode, though I haven't observed this yet.
Intrange intranges[] = {
'+', 0, '+', 127, Int8, // char
'-', 128, '+', 127, Int8, // signed char
'+', 0, '+', 255, Uint8,
'-', 32768, '+', 32767, Int16,
'+', 0, '+', 65535, Uint16,
'-', 2147483648LL, '+', 2147483647LL, Int32,
'+', 0, '+', 4294967295LL, Uint32,
// abnormal cases
'-', 0, '+', 4294967295LL, Int64,
'+', 0, '-', 1, Uint64,
'+', 4, '+', 0, Float32,
'+', 8, '+', 0, Float64,
'+', 16, '+', 0, Void,
};
static int kindsize[] = {
0,
0,
8,
8,
16,
16,
32,
32,
64,
64,
};
// Parse a single type definition from *pp.
static Type*
parsedef(char **pp, char *name)
{
char *p;
Type *t, *tt;
int i, signlo, signhi;
vlong n1, n2, lo, hi;
Field *f;
Intrange *r;
p = *pp;
// reference to another type?
if(isdigit(*p) || *p == '(') {
if(parsetypenum(&p, &n1, &n2) < 0)
return nil;
t = typebynum(n1, n2);
if(name && t->name == nil) {
t->name = name;
// save definitions of names beginning with $
if(name[0] == '$' && !t->saved) {
typ = erealloc(typ, (ntyp+1)*sizeof typ[0]);
typ[ntyp] = t;
ntyp++;
}
}
// is there an =def suffix?
if(*p == '=') {
p++;
tt = parsedef(&p, name);
if(tt == nil)
return nil;
if(tt == t) {
tt->kind = Void;
} else {
t->type = tt;
t->kind = Typedef;
}
// assign given name, but do not record in typ.
// assume the name came from a typedef
// which will be recorded.
if(name)
tt->name = name;
}
*pp = p;
return t;
}
// otherwise a type literal. first letter identifies kind
t = emalloc(sizeof *t);
switch(*p) {
default:
fprint(2, "unknown type char %c\n", *p);
*pp = "";
return t;
case '*': // pointer
p++;
t->kind = Ptr;
tt = parsedef(&p, nil);
if(tt == nil)
return nil;
t->type = tt;
break;
case 'a': // array
p++;
t->kind = Array;
// index type
tt = parsedef(&p, nil);
if(tt == nil)
return nil;
t->size = tt->size;
// element type
tt = parsedef(&p, nil);
if(tt == nil)
return nil;
t->type = tt;
break;
case 'e': // enum type - record $names in con array.
p++;
for(;;) {
if(*p == '\0')
return nil;
if(*p == ';') {
p++;
break;
}
if(parsename(&p, &name) < 0)
return nil;
if(parsenum1(&p, &n1) < 0)
return nil;
if(name[0] == '$') {
con = erealloc(con, (ncon+1)*sizeof con[0]);
name++;
con[ncon].name = name;
con[ncon].value = n1;
ncon++;
}
if(*p != ',')
return nil;
p++;
}
break;
case 'f': // function
p++;
if(parsedef(&p, nil) == nil)
return nil;
break;
case 'r': // sub-range (used for integers)
p++;
if(parsedef(&p, nil) == nil)
return nil;
// usually, the return from parsedef == t, but not always.
if(*p != ';' || *++p == ';') {
if(stabsdebug)
fprint(2, "range expected number: %s\n", p);
return nil;
}
if(*p == '-') {
signlo = '-';
p++;
} else
signlo = '+';
lo = strtoll(p, &p, 10);
if(*p != ';' || *++p == ';') {
if(stabsdebug)
fprint(2, "range expected number: %s\n", p);
return nil;
}
if(*p == '-') {
signhi = '-';
p++;
} else
signhi = '+';
hi = strtoll(p, &p, 10);
if(*p != ';') {
if(stabsdebug)
fprint(2, "range expected trailing semi: %s\n", p);
return nil;
}
p++;
t->size = hi+1; // might be array size
for(i=0; i<nelem(intranges); i++) {
r = &intranges[i];
if(r->signlo == signlo && r->signhi == signhi && r->lo == lo && r->hi == hi) {
t->kind = r->kind;
break;
}
}
break;
case 's': // struct
case 'u': // union
t->kind = Struct;
if(*p == 'u')
t->kind = Union;
// assign given name, but do not record in typ.
// assume the name came from a typedef
// which will be recorded.
if(name)
t->name = name;
p++;
if(parsenum1(&p, &n1) < 0)
return nil;
t->size = n1;
for(;;) {
if(*p == '\0')
return nil;
if(*p == ';') {
p++;
break;
}
t->f = erealloc(t->f, (t->nf+1)*sizeof t->f[0]);
f = &t->f[t->nf];
if(parsename(&p, &f->name) < 0)
return nil;
f->type = parsedef(&p, nil);
if(f->type == nil)
return nil;
if(*p != ',') {
fprint(2, "expected comma after def of %s:\n%s\n", f->name, p);
return nil;
}
p++;
if(parsenum1(&p, &n1) < 0)
return nil;
f->offset = n1;
if(*p != ',') {
fprint(2, "expected comma after offset of %s:\n%s\n", f->name, p);
return nil;
}
p++;
if(parsenum1(&p, &n1) < 0)
return nil;
f->size = n1;
if(*p != ';') {
fprint(2, "expected semi after size of %s:\n%s\n", f->name, p);
return nil;
}
// rewrite
// uint32 x : 8;
// into
// uint8 x;
// hooray for bitfields.
while(f->type->kind == Typedef)
f->type = f->type->type;
while(Int16 <= f->type->kind && f->type->kind <= Uint64 && kindsize[f->type->kind] > f->size) {
tt = emalloc(sizeof *tt);
*tt = *f->type;
f->type = tt;
f->type->kind -= 2;
}
p++;
t->nf++;
}
break;
case 'x':
// reference to struct, union not yet defined.
p++;
switch(*p) {
case 's':
t->kind = Struct;
break;
case 'u':
t->kind = Union;
break;
default:
fprint(2, "unknown x type char x%c", *p);
*pp = "";
return t;
}
if(parsename(&p, &t->name) < 0)
return nil;
break;
}
*pp = p;
return t;
}
// Parse a stab type in p, saving info in the type hash table
// and also in the list of recorded types if appropriate.
void
parsestabtype(char *p)
{
char *p0, *name;
p0 = p;
// p is the quoted string output from gcc -gstabs on a .stabs line.
// name:t(1,2)
// name:t(1,2)=def
if(parsename(&p, &name) < 0) {
Bad:
// Use fprint instead of sysfatal to avoid
// sysfatal's internal buffer size limit.
fprint(2, "cannot parse stabs type:\n%s\n(at %s)\n", p0, p);
sysfatal("stabs parse");
}
if(*p != 't' && *p != 'T')
goto Bad;
p++;
// parse the definition.
if(name[0] == '\0')
name = nil;
if(parsedef(&p, name) == nil)
goto Bad;
if(*p != '\0')
goto Bad;
}
|