]> Cypherpunks repositories - gostls13.git/commitdiff
new tool godefs.
authorRuss Cox <rsc@golang.org>
Mon, 30 Mar 2009 07:21:25 +0000 (00:21 -0700)
committerRuss Cox <rsc@golang.org>
Mon, 30 Mar 2009 07:21:25 +0000 (00:21 -0700)
uses gcc to determine system header layouts and
emits simple C or Go.  see comment in main.c.

R=r
DELTA=1069  (1067 added, 0 deleted, 2 changed)
OCL=26682
CL=26880

src/cmd/clean.bash
src/cmd/godefs/Makefile [new file with mode: 0644]
src/cmd/godefs/a.h [new file with mode: 0644]
src/cmd/godefs/main.c [new file with mode: 0644]
src/cmd/godefs/stabs.c [new file with mode: 0644]
src/cmd/godefs/util.c [new file with mode: 0644]
src/cmd/make.bash

index 5b541bca116f00a5cbd763733caefc312fc86642..81cbbec1e700f8e8d6cfb191c1eede181c5af578 100644 (file)
@@ -3,7 +3,7 @@
 # Use of this source code is governed by a BSD-style
 # license that can be found in the LICENSE file.
 
-for i in cc 6l 6a 6c 8l 8a 8c 5l 5a 5c gc 6g ar db nm acid cov gobuild prof gotest
+for i in cc 6l 6a 6c 8l 8a 8c 5l 5a 5c gc 6g ar db nm acid cov gobuild godefs prof gotest
 do
        cd $i
        make clean
diff --git a/src/cmd/godefs/Makefile b/src/cmd/godefs/Makefile
new file mode 100644 (file)
index 0000000..0d02769
--- /dev/null
@@ -0,0 +1,24 @@
+# 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.
+
+include ../../Make.conf
+
+TARG=godefs
+OFILES=\
+       main.$O\
+       stabs.$O\
+       util.$O\
+
+HFILES=a.h
+
+$(TARG): $(OFILES)
+       $(LD) -o $(TARG) -L$(GOROOT)/lib $(OFILES) -lbio -l9
+
+clean:
+       rm -f $(OFILES) $(TARG)
+
+install: $(TARG)
+       cp $(TARG) $(BIN)/$(TARG)
+
+$(OFILES): $(HFILES)
diff --git a/src/cmd/godefs/a.h b/src/cmd/godefs/a.h
new file mode 100644 (file)
index 0000000..c33b10b
--- /dev/null
@@ -0,0 +1,101 @@
+// 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.
+
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+
+enum
+{
+       Void = 1,
+       Int8,
+       Uint8,
+       Int16,
+       Uint16,
+       Int32,
+       Uint32,
+       Int64,
+       Uint64,
+       Float32,
+       Float64,
+       Ptr,
+       Struct,
+       Array,
+       Union,
+       Typedef,
+};
+
+typedef struct Field Field;
+typedef struct Type Type;
+
+struct Type
+{
+       Type *next;     // next in hash table
+
+       // stabs name and two-integer id
+       char *name;
+       int n1;
+       int n2;
+
+       // int kind
+       int kind;
+
+       // sub-type for ptr, array
+       Type *type;
+
+       // struct fields
+       Field *f;
+       int nf;
+       int size;
+
+       int saved;      // recorded in typ array
+       int warned;     // warned about needing type
+};
+
+struct Field
+{
+       char *name;
+       Type *type;
+       int offset;
+       int size;
+};
+
+// Constants
+typedef struct Const Const;
+struct Const
+{
+       char *name;
+       vlong value;
+};
+
+// Recorded constants and types, to be printed.
+extern Const *con;
+extern int ncon;
+extern Type **typ;
+extern int ntyp;
+
+// Language output
+typedef struct Lang Lang;
+struct Lang
+{
+       char *constbegin;
+       char *constfmt;
+       char *constend;
+
+       char *typdef;
+
+       char *structbegin;
+       char *unionbegin;
+       char *structpadfmt;
+       char *structend;
+
+       int (*typefmt)(Fmt*);
+};
+
+extern Lang go, c;
+
+void*  emalloc(int);
+char*  estrdup(char*);
+void*  erealloc(void*, int);
+void           parsestabtype(char*);
diff --git a/src/cmd/godefs/main.c b/src/cmd/godefs/main.c
new file mode 100644 (file)
index 0000000..a5818ff
--- /dev/null
@@ -0,0 +1,497 @@
+// 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.
+
+// Godefs takes as input a host-compilable C file that includes
+// standard system headers.  From that input file, it generates
+// a standalone (no #includes) C or Go file containing equivalent
+// definitions.
+//
+// The input C file is expected to define new types and enumerated
+// constants whose names begin with $ (a legal identifier character
+// in gcc).  The output is the standalone definitions of those names,
+// with the $ removed.
+//
+// For example, if this is x.c:
+//
+//     #include <sys/stat.h>
+//
+//     typedef struct timespec $Timespec;
+//     typedef struct stat $Stat;
+//     enum {
+//             $S_IFMT = S_IFMT,
+//             $S_IFIFO = S_IFIFO,
+//             $S_IFCHR = S_IFCHR,
+//     };
+//
+// then "godefs x.c" generates:
+//
+//     // godefs x.c
+//
+//     // MACHINE GENERATED - DO NOT EDIT.
+//
+//     // Constants
+//     enum {
+//             S_IFMT = 0xf000,
+//             S_IFIFO = 0x1000,
+//             S_IFCHR = 0x2000,
+//     };
+//
+//     // Types
+//     #pragma pack on
+//
+//     typedef struct Timespec Timespec;
+//     struct Timespec {
+//             int32 tv_sec;
+//             int32 tv_nsec;
+//     };
+//
+//     typedef struct Stat Stat;
+//     struct Stat {
+//             int32 st_dev;
+//             uint32 st_ino;
+//             uint16 st_mode;
+//             uint16 st_nlink;
+//             uint32 st_uid;
+//             uint32 st_gid;
+//             int32 st_rdev;
+//             Timespec st_atimespec;
+//             Timespec st_mtimespec;
+//             Timespec st_ctimespec;
+//             int64 st_size;
+//             int64 st_blocks;
+//             int32 st_blksize;
+//             uint32 st_flags;
+//             uint32 st_gen;
+//             int32 st_lspare;
+//             int64 st_qspare[2];
+//     };
+//     #pragma pack off
+//
+// The -g flag to godefs causes it to generate Go output, not C.
+// In the Go output, struct fields have leading xx_ prefixes removed
+// and the first character capitalized (exported).
+//
+// Godefs works by invoking gcc to compile the given input file
+// and then parses the debug info embedded in the assembly output.
+// This is far easier than reading system headers on most machines.
+//
+// The -c flag sets the compiler (default "gcc").
+//
+// The -f flag adds a flag to pass to the compiler (e.g., -f -m64).
+
+#include "a.h"
+
+void
+usage(void)
+{
+       fprint(2, "usage: godefs [-g] [-c cc] [-f cc-flag] defs.c\n");
+       exit(1);
+}
+
+int gotypefmt(Fmt*);
+int ctypefmt(Fmt*);
+int prefixlen(Type*);
+
+Lang go =
+{
+       "const (\n",
+       "\t%s = %#llx;\n",
+       ")\n",
+
+       "type",
+
+       "type %s struct {\n",
+       "type %s union {\n",    // not really, but readable
+       "\tpad%d [%d]byte;\n",
+       "}\n",
+
+       gotypefmt,
+};
+
+Lang c =
+{
+       "enum {\n",
+       "\t%s = %#llx,\n",
+       "};\n",
+
+       "typedef",
+
+       "typedef struct %s %s;\nstruct %s {\n",
+       "typedef union %s %s;\nunion %s {\n",
+       "\tbyte pad%d[%d];\n",
+       "};\n",
+
+       ctypefmt,
+};
+
+int oargc;
+char **oargv;
+Lang *lang = &c;
+
+Const *con;
+int ncon;
+
+Type **typ;
+int ntyp;
+
+void
+main(int argc, char **argv)
+{
+       int p[2], pid, i, j, n, off, npad, prefix;
+       char *av[30], *q, *r, *tofree, *name;
+       Biobuf *bin, *bout;
+       Type *t;
+       Field *f;
+
+       quotefmtinstall();
+
+       oargc = argc;
+       oargv = argv;
+
+       n = 0;
+       av[n++] = "gcc";
+       av[n++] = "-S"; // write assembly
+       av[n++] = "-gstabs";    // include stabs info
+       av[n++] = "-o-";        // to stdout
+
+       ARGBEGIN{
+       case 'g':
+               lang = &go;
+               break;
+       case 'c':
+               av[0] = EARGF(usage());
+               break;
+       case 'f':
+               if(n+2 >= nelem(av))
+                       sysfatal("too many -f options");
+               av[n++] = EARGF(usage());
+               break;
+       default:
+               usage();
+       }ARGEND
+
+       if(argc != 1)
+               usage();
+       av[n++] = argv[0];
+       av[n] = nil;
+
+       // Run gcc writing assembly and stabs debugging to p[1].
+       if(pipe(p) < 0)
+               sysfatal("pipe: %r");
+
+       pid = fork();
+       if(pid < 0)
+               sysfatal("fork: %r");
+       if(pid == 0) {
+               close(p[0]);
+               dup(p[1], 1);
+               close(0);
+               open("/dev/null", OREAD);
+               exec(av[0], av);
+               fprint(2, "exec gcc: %r\n");
+               exit(1);
+       }
+       close(p[1]);
+
+       // Read assembly, pulling out .stabs lines.
+       bin = Bfdopen(p[0], OREAD);
+       while((q = Brdstr(bin, '\n', 1)) != nil) {
+               //      .stabs  "float:t(0,12)=r(0,1);4;0;",128,0,0,0
+               tofree = q;
+               while(*q == ' ' || *q == '\t')
+                       q++;
+               if(strncmp(q, ".stabs", 6) != 0)
+                       goto Continue;
+               q += 6;
+               while(*q == ' ' || *q == '\t')
+                       q++;
+               if(*q++ != '\"') {
+               Bad:
+                       sysfatal("cannot parse .stabs line:\n%s", tofree);
+               }
+
+               r = strchr(q, '\"');
+               if(r == nil)
+                       goto Bad;
+               *r++ = '\0';
+               if(*r++ != ',')
+                       goto Bad;
+               if(*r < '0' || *r > '9')
+                       goto Bad;
+               if(atoi(r) != 128)      // stabs kind = local symbol
+                       goto Continue;
+
+               parsestabtype(q);
+
+       Continue:
+               free(tofree);
+       }
+       Bterm(bin);
+       waitpid();
+
+       // Write defs to standard output.
+       bout = Bfdopen(1, OWRITE);
+       fmtinstall('T', lang->typefmt);
+
+       // Echo original command line in header.
+       Bprint(bout, "//");
+       for(i=0; i<oargc; i++)
+               Bprint(bout, " %q", oargv[i]);
+       Bprint(bout, "\n");
+       Bprint(bout, "\n");
+       Bprint(bout, "// MACHINE GENERATED - DO NOT EDIT.\n");
+       Bprint(bout, "\n");
+
+       // Constants.
+       Bprint(bout, "// Constants\n");
+       if(ncon > 0) {
+               Bprint(bout, lang->constbegin);
+               for(i=0; i<ncon; i++)
+                       Bprint(bout, lang->constfmt, con[i].name, con[i].value);
+               Bprint(bout, lang->constend);
+       }
+       Bprint(bout, "\n");
+
+       // Types
+
+       // push our names down
+       for(i=0; i<ntyp; i++) {
+               t = typ[i];
+               name = t->name;
+               while(t && t->kind == Typedef)
+                       t = t->type;
+               if(t)
+                       t->name = name;
+       }
+
+       Bprint(bout, "// Types\n");
+
+       // Have to turn off structure padding in Plan 9 compiler,
+       // mainly because it is more aggressive than gcc tends to be.
+       if(lang == &c)
+               Bprint(bout, "#pragma pack on\n");
+
+       for(i=0; i<ntyp; i++) {
+               Bprint(bout, "\n");
+               t = typ[i];
+               while(t && t->kind == Typedef)
+                       t = t->type;
+               name = t->name;
+               if(name[0] == '$')
+                       name++;
+               npad = 0;
+               off = 0;
+               switch(t->kind) {
+               case 0:
+                       fprint(2, "unknown type definition for %s\n", name);
+                       break;
+               default:        // numeric, array, or pointer
+               case Array:
+               case Ptr:
+                       Bprint(bout, "%s %lT\n", lang->typdef, name, t);
+                       break;
+               case Union:
+                       if(lang == &go) {
+                               fprint(2, "%s: cannot emit unions in go\n", name);
+                               continue;
+                       }
+                       Bprint(bout, lang->unionbegin, name, name, name);
+                       goto StructBody;
+               case Struct:
+                       Bprint(bout, lang->structbegin, name, name, name);
+               StructBody:
+                       prefix = 0;
+                       if(lang == &go)
+                               prefix = prefixlen(t);
+                       for(j=0; j<t->nf; j++) {
+                               f = &t->f[j];
+                               // padding
+                               if(t->kind == Struct) {
+                                       if(f->offset%8 != 0 || f->size%8 != 0) {
+                                               fprint(2, "ignoring bitfield %s.%s\n", t->name, f->name);
+                                               continue;
+                                       }
+                                       if(f->offset < off)
+                                               sysfatal("%s: struct fields went backward", t->name);
+                                       if(off < f->offset) {
+                                               Bprint(bout, lang->structpadfmt, npad++, (f->offset - off) / 8);
+                                               off = f->offset;
+                                       }
+                                       off += f->size;
+                               }
+                               Bprint(bout, "\t%lT;\n", f->name+prefix, f->type);
+                       }
+                       // final padding
+                       if(t->kind == Struct) {
+                               if(off/8 < t->size)
+                                       Bprint(bout, lang->structpadfmt, npad++, t->size - off/8);
+                       }
+                       Bprint(bout, lang->structend);
+               }
+       }
+       if(lang == &c)
+               Bprint(bout, "#pragma pack off\n");
+       Bterm(bout);
+       exit(0);
+}
+
+char *kindnames[] = {
+       "void", // actually unknown, but byte is good for pointers
+       "void",
+       "int8",
+       "uint8",
+       "int16",
+       "uint16",
+       "int32",
+       "uint32",
+       "int64",
+       "uint64",
+       "float32",
+       "float64",
+       "ptr",
+       "struct",
+       "array",
+       "union",
+       "typedef",
+};
+
+int
+ctypefmt(Fmt *f)
+{
+       char *name, *s;
+       Type *t;
+
+       name = nil;
+       if(f->flags & FmtLong) {
+               name = va_arg(f->args, char*);
+               if(name == nil || name[0] == '\0')
+                       name = "_anon_";
+       }
+       t = va_arg(f->args, Type*);
+       while(t && t->kind == Typedef)
+               t = t->type;
+       switch(t->kind) {
+       case Struct:
+       case Union:
+               // must be named
+               s = t->name;
+               if(s == nil) {
+                       fprint(2, "need name for anonymous struct\n");
+                       goto bad;
+               }
+               else if(s[0] != '$')
+                       fprint(2, "need name for struct %s\n", s);
+               else
+                       s++;
+               fmtprint(f, "%s", s);
+               if(name)
+                       fmtprint(f, " %s", name);
+               break;
+
+       case Array:
+               if(name)
+                       fmtprint(f, "%T %s[%d]", t->type, name, t->size);
+               else
+                       fmtprint(f, "%T[%d]", t->type, t->size);
+               break;
+
+       case Ptr:
+               if(name)
+                       fmtprint(f, "%T *%s", t->type, name);
+               else
+                       fmtprint(f, "%T*", t->type);
+               break;
+
+       default:
+               fmtprint(f, "%s", kindnames[t->kind]);
+               if(name)
+                       fmtprint(f, " %s", name);
+               break;
+
+       bad:
+               if(name)
+                       fmtprint(f, "byte %s[%d]", name, t->size);
+               else
+                       fmtprint(f, "byte[%d]", t->size);
+               break;
+       }
+
+       return 0;
+}
+
+int
+gotypefmt(Fmt *f)
+{
+       char *name, *s;
+       Type *t;
+
+       if(f->flags & FmtLong) {
+               name = va_arg(f->args, char*);
+               if('a' <= name[0] && name[0] <= 'z')
+                       name[0] += 'A' - 'a';
+               fmtprint(f, "%s ", name);
+       }
+       t = va_arg(f->args, Type*);
+       while(t && t->kind == Typedef)
+               t = t->type;
+
+       switch(t->kind) {
+       case Struct:
+       case Union:
+               // must be named
+               s = t->name;
+               if(s == nil) {
+                       fprint(2, "need name for anonymous struct\n");
+                       s = "STRUCT";
+               }
+               else if(s[0] != '$')
+                       fprint(2, "need name for struct %s\n", s);
+               else
+                       s++;
+               fmtprint(f, "%s", s);
+               break;
+
+       case Array:
+               fmtprint(f, "[%d]%T", t->size, t->type);
+               break;
+
+       case Ptr:
+               fmtprint(f, "*%T", t->type);
+               break;
+
+       default:
+               s = kindnames[t->kind];
+               if(strcmp(s, "void") == 0)
+                       s = "byte";
+               fmtprint(f, "%s", s);
+       }
+
+       return 0;
+}
+
+// Figure out common struct prefix len
+int
+prefixlen(Type *t)
+{
+       int i;
+       int len;
+       char *p, *name;
+       Field *f;
+
+       len = 0;
+       name = nil;
+       for(i=0; i<t->nf; i++) {
+               f = &t->f[i];
+               p = strchr(f->name, '_');
+               if(p == nil)
+                       return 0;
+               if(name == nil) {
+                       name = f->name;
+                       len = p+1 - name;
+               }
+               else if(strncmp(f->name, name, len) != 0)
+                       return 0;
+       }
+       return len;
+}
diff --git a/src/cmd/godefs/stabs.c b/src/cmd/godefs/stabs.c
new file mode 100644 (file)
index 0000000..5a6f18b
--- /dev/null
@@ -0,0 +1,418 @@
+// 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,
+       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:
+               *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(Int16 <= f->type->kind && f->type->kind <= Uint64 && kindsize[f->type->kind] > f->size) {
+                               t = emalloc(sizeof *t);
+                               *t = *f->type;
+                               f->type = t;
+                               f->type->kind -= 2;
+                       }
+                       p++;
+                       t->nf++;
+               }
+               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;
+}
+
diff --git a/src/cmd/godefs/util.c b/src/cmd/godefs/util.c
new file mode 100644 (file)
index 0000000..18be004
--- /dev/null
@@ -0,0 +1,36 @@
+// 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.
+
+#include "a.h"
+
+void*
+emalloc(int n)
+{
+       void *p;
+
+       p = malloc(n);
+       if(p == nil)
+               sysfatal("out of memory");
+       memset(p, 0, n);
+       return p;
+}
+
+char*
+estrdup(char *s)
+{
+       s = strdup(s);
+       if(s == nil)
+               sysfatal("out of memory");
+       return s;
+}
+
+void*
+erealloc(void *v, int n)
+{
+       v = realloc(v, n);
+       if(v == nil)
+               sysfatal("out of memory");
+       return v;
+}
+
index 36dd2bd2470c1eabc031a67519706484223885aa..3d812c414bfb48611ac568a41890585bbcb1ce94 100644 (file)
@@ -12,7 +12,7 @@ bash mkenam
 make enam.o
 cd ..
 
-for i in cc 6l 6a 6c gc 6g ar db nm acid cov gobuild prof gotest
+for i in cc 6l 6a 6c gc 6g ar db nm acid cov gobuild godefs prof gotest
 do
        echo; echo; echo %%%% making $i %%%%; echo
        cd $i