| // 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; | |
| USED(prog); | |
| 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; | |
| } |