diff options
Diffstat (limited to 'src/cmd/godefs/stabs.c')
-rw-r--r-- | src/cmd/godefs/stabs.c | 456 |
1 files changed, 456 insertions, 0 deletions
diff --git a/src/cmd/godefs/stabs.c b/src/cmd/godefs/stabs.c new file mode 100644 index 000000000..2c3d431b8 --- /dev/null +++ b/src/cmd/godefs/stabs.c @@ -0,0 +1,456 @@ +// 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; +} + |