summaryrefslogtreecommitdiff
path: root/src/cmd/godefs/main.c
diff options
context:
space:
mode:
authorOndřej Surý <ondrej@sury.org>2011-09-13 13:13:40 +0200
committerOndřej Surý <ondrej@sury.org>2011-09-13 13:13:40 +0200
commit5ff4c17907d5b19510a62e08fd8d3b11e62b431d (patch)
treec0650497e988f47be9c6f2324fa692a52dea82e1 /src/cmd/godefs/main.c
parent80f18fc933cf3f3e829c5455a1023d69f7b86e52 (diff)
downloadgolang-upstream/60.tar.gz
Imported Upstream version 60upstream/60
Diffstat (limited to 'src/cmd/godefs/main.c')
-rw-r--r--src/cmd/godefs/main.c606
1 files changed, 606 insertions, 0 deletions
diff --git a/src/cmd/godefs/main.c b/src/cmd/godefs/main.c
new file mode 100644
index 000000000..6a8630179
--- /dev/null
+++ b/src/cmd/godefs/main.c
@@ -0,0 +1,606 @@
+// 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"
+
+#ifdef _WIN32
+int
+spawn(char *prog, char **argv)
+{
+ return _spawnvp(P_NOWAIT, prog, (const char**)argv);
+}
+#undef waitfor
+void
+waitfor(int pid)
+{
+ _cwait(0, pid, 0);
+}
+#else
+int
+spawn(char *prog, char **argv)
+{
+ int pid = fork();
+ if(pid < 0)
+ sysfatal("fork: %r");
+ if(pid == 0) {
+ exec(argv[0], argv);
+ fprint(2, "exec gcc: %r\n");
+ exit(1);
+ }
+ return pid;
+}
+#endif
+
+void
+usage(void)
+{
+ fprint(2, "usage: godefs [-g package] [-c cc] [-f cc-arg] [defs.c ...]\n");
+ exit(1);
+}
+
+int gotypefmt(Fmt*);
+int ctypefmt(Fmt*);
+int prefixlen(Type*);
+int cutprefix(char*);
+
+Lang go =
+{
+ "const (\n",
+ "\t%s = %#llx;\n",
+ ")\n",
+
+ "type",
+ "\n",
+
+ "type %s struct {\n",
+ "type %s struct {\n",
+ "\tPad_godefs_%d [%d]byte;\n",
+ "}\n",
+
+ gotypefmt,
+};
+
+Lang c =
+{
+ "enum {\n",
+ "\t%s = %#llx,\n",
+ "};\n",
+
+ "typedef",
+ ";\n",
+
+ "typedef struct %s %s;\nstruct %s {\n",
+ "typedef union %s %s;\nunion %s {\n",
+ "\tbyte pad_godefs_%d[%d];\n",
+ "};\n",
+
+ ctypefmt,
+};
+
+char *pkg;
+
+int oargc;
+char **oargv;
+Lang *lang = &c;
+
+Const *con;
+int ncon;
+
+Type **typ;
+int ntyp;
+
+void
+waitforgcc(void)
+{
+ waitpid();
+}
+
+void
+main(int argc, char **argv)
+{
+ int p[2], pid, i, j, n, off, npad, prefix;
+ char **av, *q, *r, *tofree, *name;
+ char nambuf[100];
+ Biobuf *bin, *bout;
+ Type *t, *tt;
+ Field *f;
+ int orig_output_fd;
+
+ quotefmtinstall();
+
+ oargc = argc;
+ oargv = argv;
+ av = emalloc((30+argc)*sizeof av[0]);
+ atexit(waitforgcc);
+
+ n = 0;
+ av[n++] = "gcc";
+ av[n++] = "-fdollars-in-identifiers";
+ av[n++] = "-S"; // write assembly
+ av[n++] = "-gstabs+"; // include stabs info
+ av[n++] = "-o"; // to ...
+ av[n++] = "-"; // ... stdout
+ av[n++] = "-xc"; // read C
+
+ ARGBEGIN{
+ case 'g':
+ lang = &go;
+ pkg = EARGF(usage());
+ break;
+ case 'c':
+ av[0] = EARGF(usage());
+ break;
+ case 'f':
+ av[n++] = EARGF(usage());
+ break;
+ default:
+ usage();
+ }ARGEND
+
+ if(argc == 0)
+ av[n++] = "-";
+ else
+ av[n++] = argv[0];
+ av[n] = nil;
+
+ orig_output_fd = dup(1, -1);
+ for(i=0; i==0 || i < argc; i++) {
+ // Some versions of gcc do not accept -S with multiple files.
+ // Run gcc once for each file.
+ // Write assembly and stabs debugging to p[1].
+ if(pipe(p) < 0)
+ sysfatal("pipe: %r");
+ dup(p[1], 1);
+ close(p[1]);
+ if (argc)
+ av[n-1] = argv[i];
+ pid = spawn(av[0], av);
+ dup(orig_output_fd, 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);
+ waitfor(pid);
+ }
+ close(orig_output_fd);
+
+ // 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");
+
+ if(pkg)
+ Bprint(bout, "package %s\n\n", pkg);
+
+ // Constants.
+ Bprint(bout, "// Constants\n");
+ if(ncon > 0) {
+ Bprint(bout, lang->constbegin);
+ for(i=0; i<ncon; i++) {
+ // Go can handle negative constants,
+ // but C enums may not be able to.
+ if(lang == &go)
+ Bprint(bout, lang->constfmt, con[i].name, con[i].value);
+ else
+ Bprint(bout, lang->constfmt, con[i].name, con[i].value & 0xFFFFFFFF);
+ }
+ 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];
+ name = t->name;
+ while(t && t->kind == Typedef) {
+ if(name == nil && t->name != nil) {
+ name = t->name;
+ if(t->printed)
+ break;
+ }
+ t = t->type;
+ }
+ if(name == nil && t->name != nil) {
+ name = t->name;
+ if(t->printed)
+ continue;
+ t->printed = 1;
+ }
+ if(name == nil) {
+ fprint(2, "unknown name for %T", typ[i]);
+ continue;
+ }
+ 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%s", lang->typdef, name, t, lang->typdefend);
+ break;
+ case Union:
+ // In Go, print union as struct with only first element,
+ // padded the rest of the way.
+ 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];
+ if(f->type->kind == 0 && f->size <= 64 && (f->size&(f->size-1)) == 0) {
+ // unknown type but <= 64 bits and bit size is a power of two.
+ // could be enum - make Uint64 and then let it reduce
+ tt = emalloc(sizeof *tt);
+ *tt = *f->type;
+ f->type = tt;
+ tt->kind = Uint64;
+ while(tt->kind > Uint8 && kindsize[tt->kind] > f->size)
+ tt->kind -= 2;
+ }
+ // padding
+ if(t->kind == Struct || lang == &go) {
+ 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;
+ }
+ name = f->name;
+ if(cutprefix(name))
+ name += prefix;
+ if(strcmp(name, "") == 0) {
+ snprint(nambuf, sizeof nambuf, "Pad_godefs_%d", npad++);
+ name = nambuf;
+ }
+ Bprint(bout, "\t%#lT;\n", name, f->type);
+ if(t->kind == Union && lang == &go)
+ break;
+ }
+ // final padding
+ if(t->kind == Struct || lang == &go) {
+ 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';
+ if(name[0] == '_' && (f->flags & FmtSharp))
+ fmtprint(f, "X");
+ 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");
+ fmtprint(f, "STRUCT");
+ }
+ else if(s[0] != '$') {
+ fprint(2, "warning: missing name for struct %s\n", s);
+ fmtprint(f, "[%d]byte /* %s */", t->size, s);
+ } else
+ fmtprint(f, "%s", s+1);
+ 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;
+}
+
+// Is this the kind of name we should cut a prefix from?
+// The rule is that the name cannot begin with underscore
+// and must have an underscore eventually.
+int
+cutprefix(char *name)
+{
+ char *p;
+
+ // special case: orig_ in register struct
+ if(strncmp(name, "orig_", 5) == 0)
+ return 0;
+
+ for(p=name; *p; p++) {
+ if(*p == '_')
+ return p-name > 0;
+ }
+ 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];
+ if(!cutprefix(f->name))
+ continue;
+ 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;
+}