diff options
author | Ondřej Surý <ondrej@sury.org> | 2011-09-13 13:13:40 +0200 |
---|---|---|
committer | Ondřej Surý <ondrej@sury.org> | 2011-09-13 13:13:40 +0200 |
commit | 5ff4c17907d5b19510a62e08fd8d3b11e62b431d (patch) | |
tree | c0650497e988f47be9c6f2324fa692a52dea82e1 /src/cmd/godefs/main.c | |
parent | 80f18fc933cf3f3e829c5455a1023d69f7b86e52 (diff) | |
download | golang-upstream/60.tar.gz |
Imported Upstream version 60upstream/60
Diffstat (limited to 'src/cmd/godefs/main.c')
-rw-r--r-- | src/cmd/godefs/main.c | 606 |
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; +} |