summaryrefslogtreecommitdiff
path: root/src/cmd/godefs/stabs.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/godefs/stabs.c')
-rw-r--r--src/cmd/godefs/stabs.c456
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;
+}
+