mirror of https://github.com/golang/go.git
498 lines
9.2 KiB
C
498 lines
9.2 KiB
C
// 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;
|
|
}
|