mirror of
https://github.com/golang/go
synced 2024-10-04 01:21:21 -06:00
8cd7aac223
Fixes #1466. R=mikioh.mikioh, rsc CC=golang-dev https://golang.org/cl/4564043
457 lines
8.2 KiB
C
457 lines
8.2 KiB
C
// 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;
|
|
}
|
|
|
|
// Written to parse max/min of vlong correctly.
|
|
static vlong
|
|
parseoctal(char **pp)
|
|
{
|
|
char *p;
|
|
vlong n;
|
|
|
|
p = *pp;
|
|
if(*p++ != '0')
|
|
return 0;
|
|
n = 0;
|
|
while(*p >= '0' && *p <= '9')
|
|
n = n << 3 | *p++ - '0';
|
|
*pp = p;
|
|
return n;
|
|
}
|
|
|
|
// 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
|
|
{
|
|
vlong lo;
|
|
vlong hi;
|
|
int kind;
|
|
};
|
|
|
|
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,
|
|
1LL << 63, ~(1LL << 63), Int64,
|
|
0, -1, Uint64,
|
|
4, 0, Float32,
|
|
8, 0, Float64,
|
|
16, 0, Void,
|
|
};
|
|
|
|
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;
|
|
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 in %s\n", *p, p);
|
|
*pp = "";
|
|
return t;
|
|
|
|
case '@': // type attribute
|
|
while (*++p != ';');
|
|
*pp = ++p;
|
|
return parsedef(pp, nil);
|
|
|
|
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 'B': // volatile
|
|
case 'k': // const
|
|
++*pp;
|
|
return parsedef(pp, nil);
|
|
|
|
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 == '0')
|
|
lo = parseoctal(&p);
|
|
else
|
|
lo = strtoll(p, &p, 10);
|
|
if(*p != ';' || *++p == ';') {
|
|
if(stabsdebug)
|
|
fprint(2, "range expected number: %s\n", p);
|
|
return nil;
|
|
}
|
|
if(*p == '0')
|
|
hi = parseoctal(&p);
|
|
else
|
|
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->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;
|
|
}
|
|
|
|
while(f->type->kind == Typedef)
|
|
f->type = f->type->type;
|
|
|
|
// rewrite
|
|
// uint32 x : 8;
|
|
// into
|
|
// uint8 x;
|
|
// hooray for bitfields.
|
|
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;
|
|
}
|
|
|