From 47fbb7639ac6ea9de1ced9dd241c79e90bc9d1fe Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Mon, 30 Mar 2009 00:21:25 -0700 Subject: [PATCH] new tool godefs. 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 | 2 +- src/cmd/godefs/Makefile | 24 ++ src/cmd/godefs/a.h | 101 ++++++++ src/cmd/godefs/main.c | 497 ++++++++++++++++++++++++++++++++++++++++ src/cmd/godefs/stabs.c | 418 +++++++++++++++++++++++++++++++++ src/cmd/godefs/util.c | 36 +++ src/cmd/make.bash | 2 +- 7 files changed, 1078 insertions(+), 2 deletions(-) create mode 100644 src/cmd/godefs/Makefile create mode 100644 src/cmd/godefs/a.h create mode 100644 src/cmd/godefs/main.c create mode 100644 src/cmd/godefs/stabs.c create mode 100644 src/cmd/godefs/util.c diff --git a/src/cmd/clean.bash b/src/cmd/clean.bash index 5b541bca11..81cbbec1e7 100644 --- a/src/cmd/clean.bash +++ b/src/cmd/clean.bash @@ -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 index 0000000000..0d02769c90 --- /dev/null +++ b/src/cmd/godefs/Makefile @@ -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 index 0000000000..c33b10b54c --- /dev/null +++ b/src/cmd/godefs/a.h @@ -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 +#include +#include + +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 index 0000000000..a5818ff899 --- /dev/null +++ b/src/cmd/godefs/main.c @@ -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 +// +// 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 0) { + Bprint(bout, lang->constbegin); + for(i=0; iconstfmt, con[i].name, con[i].value); + Bprint(bout, lang->constend); + } + Bprint(bout, "\n"); + + // Types + + // push our names down + for(i=0; iname; + 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; ikind == 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; jnf; 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; inf; 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 index 0000000000..5a6f18b15c --- /dev/null +++ b/src/cmd/godefs/stabs.c @@ -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; isignlo == 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 index 0000000000..18be004532 --- /dev/null +++ b/src/cmd/godefs/util.c @@ -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; +} + diff --git a/src/cmd/make.bash b/src/cmd/make.bash index 36dd2bd247..3d812c414b 100644 --- a/src/cmd/make.bash +++ b/src/cmd/make.bash @@ -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