Commit 879a1c6a authored by Russ Cox's avatar Russ Cox

godefs: delete, replaced by cgo -godefs

Godefs was a C program that ran gcc and then parsed the
stabs debugging information in the resulting object file to
generate C or Go code for bootstrapping as part of
package runtime or package syscall.

Cgo does the same work, but using the dwarf debugging
information.  Add -godefs and -cdefs options to cgo that
mimic godefs's output, albeit with different input
(a Go program, not a C program).

This has been a "nice to have" for a while but was forced
by Apple removing stabs debugging output from their
latest compilers.

Fixes #835.
Fixes #2338.

R=golang-dev, bradfitz, r, dave, iant
CC=golang-dev
https://golang.org/cl/5367043
parent a50ee009
......@@ -16,7 +16,6 @@ DIRS=\
cc\
cov\
gc\
godefs\
gopack\
nm\
prof\
......
......@@ -8,6 +8,7 @@ TARG=cgo
GOFILES=\
ast.go\
gcc.go\
godefs.go\
main.go\
out.go\
util.go\
......
......@@ -128,6 +128,7 @@ func (f *File) ReadGo(name string) {
f.walk(ast1, "prog", (*File).saveExport)
f.walk(ast2, "prog", (*File).saveExport2)
f.Comments = ast1.Comments
f.AST = ast2
}
......
......@@ -24,6 +24,7 @@ import (
"strconv"
"strings"
"unicode"
"unicode/utf8"
)
var debugDefine = flag.Bool("debug-define", false, "print relevant #defines")
......@@ -59,6 +60,9 @@ func cname(s string) string {
if strings.HasPrefix(s, "enum_") {
return "enum " + s[len("enum_"):]
}
if strings.HasPrefix(s, "sizeof_") {
return "sizeof(" + cname(s[len("sizeof_"):]) + ")"
}
return s
}
......@@ -347,7 +351,16 @@ func (p *Package) guessKinds(f *File) []*Name {
}
if ok {
n.Kind = "const"
// Turn decimal into hex, just for consistency
// with enum-derived constants. Otherwise
// in the cgo -godefs output half the constants
// are in hex and half are in whatever the #define used.
i, err := strconv.Btoi64(n.Define, 0)
if err == nil {
n.Const = fmt.Sprintf("%#x", i)
} else {
n.Const = n.Define
}
continue
}
......@@ -589,12 +602,12 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
if enums[i] != 0 && n.Type.EnumValues != nil {
k := fmt.Sprintf("__cgo_enum__%d", i)
n.Kind = "const"
n.Const = strconv.Itoa64(n.Type.EnumValues[k])
n.Const = fmt.Sprintf("%#x", n.Type.EnumValues[k])
// Remove injected enum to ensure the value will deep-compare
// equally in future loads of the same constant.
delete(n.Type.EnumValues, k)
} else if n.Kind == "const" && i < len(enumVal) {
n.Const = strconv.Itoa64(enumVal[i])
n.Const = fmt.Sprintf("%#x", enumVal[i])
}
}
}
......@@ -603,7 +616,8 @@ func (p *Package) loadDWARF(f *File, names []*Name) {
// rewriteRef rewrites all the C.xxx references in f.AST to refer to the
// Go equivalents, now that we have figured out the meaning of all
// the xxx.
// the xxx. In *godefs or *cdefs mode, rewriteRef replaces the names
// with full definitions instead of mangled names.
func (p *Package) rewriteRef(f *File) {
// Assign mangled names.
for _, n := range f.Name {
......@@ -679,6 +693,17 @@ func (p *Package) rewriteRef(f *File) {
error_(r.Pos(), "must call C.%s", r.Name.Go)
}
}
if *godefs || *cdefs {
// Substitute definition for mangled type name.
if id, ok := expr.(*ast.Ident); ok {
if t := typedef[id.Name]; t != nil {
expr = t
}
if id.Name == r.Name.Mangle && r.Name.Const != "" {
expr = ast.NewIdent(r.Name.Const)
}
}
}
*r.Expr = expr
}
}
......@@ -856,6 +881,7 @@ type typeConv struct {
var tagGen int
var typedef = make(map[string]ast.Expr)
var goIdent = make(map[string]*ast.Ident)
func (c *typeConv) Init(ptrSize int64) {
c.ptrSize = ptrSize
......@@ -1121,6 +1147,7 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type {
}
name := c.Ident("_Ctype_" + dt.Kind + "_" + tag)
t.Go = name // publish before recursive calls
goIdent[name.Name] = name
switch dt.Kind {
case "union", "class":
typedef[name.Name] = c.Opaque(t.Size)
......@@ -1155,7 +1182,8 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type {
t.Align = c.ptrSize
break
}
name := c.Ident("_Ctypedef_" + dt.Name)
name := c.Ident("_Ctype_" + dt.Name)
goIdent[name.Name] = name
t.Go = name // publish before recursive call
sub := c.Type(dt.Type)
t.Size = sub.Size
......@@ -1163,6 +1191,9 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type {
if _, ok := typedef[name.Name]; !ok {
typedef[name.Name] = sub.Go
}
if *godefs || *cdefs {
t.Go = sub.Go
}
case *dwarf.UcharType:
if t.Size != 1 {
......@@ -1206,9 +1237,11 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type {
s = strings.Join(strings.Split(s, " "), "") // strip spaces
name := c.Ident("_Ctype_" + s)
typedef[name.Name] = t.Go
if !*godefs && !*cdefs {
t.Go = name
}
}
}
if t.C.Empty() {
fatalf("internal error: did not create C name for %s", dtype)
......@@ -1331,6 +1364,8 @@ func (c *typeConv) Struct(dt *dwarf.StructType) (expr *ast.StructType, csyntax s
ident[f.Name] = f.Name
used[f.Name] = true
}
if !*godefs && !*cdefs {
for cid, goid := range ident {
if token.Lookup([]byte(goid)).IsKeyword() {
// Avoid keyword
......@@ -1345,24 +1380,45 @@ func (c *typeConv) Struct(dt *dwarf.StructType) (expr *ast.StructType, csyntax s
ident[cid] = goid
}
}
}
anon := 0
for _, f := range dt.Field {
if f.BitSize > 0 && f.BitSize != f.ByteSize*8 {
continue
}
if f.ByteOffset > off {
fld = c.pad(fld, f.ByteOffset-off)
off = f.ByteOffset
}
t := c.Type(f.Type)
tgo := t.Go
size := t.Size
if f.BitSize > 0 {
if f.BitSize%8 != 0 {
continue
}
size = f.BitSize / 8
name := tgo.(*ast.Ident).String()
if strings.HasPrefix(name, "int") {
name = "int"
} else {
name = "uint"
}
tgo = ast.NewIdent(name + fmt.Sprint(f.BitSize))
}
n := len(fld)
fld = fld[0 : n+1]
fld[n] = &ast.Field{Names: []*ast.Ident{c.Ident(ident[f.Name])}, Type: t.Go}
off += t.Size
name := f.Name
if name == "" {
name = fmt.Sprintf("anon%d", anon)
anon++
ident[name] = name
}
fld[n] = &ast.Field{Names: []*ast.Ident{c.Ident(ident[name])}, Type: tgo}
off += size
buf.WriteString(t.C.String())
buf.WriteString(" ")
buf.WriteString(f.Name)
buf.WriteString(name)
buf.WriteString("; ")
if t.Align > align {
align = t.Align
......@@ -1377,6 +1433,96 @@ func (c *typeConv) Struct(dt *dwarf.StructType) (expr *ast.StructType, csyntax s
}
buf.WriteString("}")
csyntax = buf.String()
if *godefs || *cdefs {
godefsFields(fld)
}
expr = &ast.StructType{Fields: &ast.FieldList{List: fld}}
return
}
func upper(s string) string {
if s == "" {
return ""
}
r, size := utf8.DecodeRuneInString(s)
if r == '_' {
return "X" + s
}
return string(unicode.ToUpper(r)) + s[size:]
}
// godefsFields rewrites field names for use in Go or C definitions.
// It strips leading common prefixes (like tv_ in tv_sec, tv_usec)
// converts names to upper case, and rewrites _ into Pad_godefs_n,
// so that all fields are exported.
func godefsFields(fld []*ast.Field) {
prefix := fieldPrefix(fld)
npad := 0
for _, f := range fld {
for _, n := range f.Names {
if strings.HasPrefix(n.Name, prefix) && n.Name != prefix {
n.Name = n.Name[len(prefix):]
}
if n.Name == "_" {
// Use exported name instead.
n.Name = "Pad_cgo_" + strconv.Itoa(npad)
npad++
}
if !*cdefs {
n.Name = upper(n.Name)
}
}
p := &f.Type
t := *p
if star, ok := t.(*ast.StarExpr); ok {
star = &ast.StarExpr{X: star.X}
*p = star
p = &star.X
t = *p
}
if id, ok := t.(*ast.Ident); ok {
if id.Name == "unsafe.Pointer" {
*p = ast.NewIdent("*byte")
}
}
}
}
// fieldPrefix returns the prefix that should be removed from all the
// field names when generating the C or Go code. For generated
// C, we leave the names as is (tv_sec, tv_usec), since that's what
// people are used to seeing in C. For generated Go code, such as
// package syscall's data structures, we drop a common prefix
// (so sec, usec, which will get turned into Sec, Usec for exporting).
func fieldPrefix(fld []*ast.Field) string {
if *cdefs {
return ""
}
prefix := ""
for _, f := range fld {
for _, n := range f.Names {
// Ignore field names that don't have the prefix we're
// looking for. It is common in C headers to have fields
// named, say, _pad in an otherwise prefixed header.
// If the struct has 3 fields tv_sec, tv_usec, _pad1, then we
// still want to remove the tv_ prefix.
// The check for "orig_" here handles orig_eax in the
// x86 ptrace register sets, which otherwise have all fields
// with reg_ prefixes.
if strings.HasPrefix(n.Name, "orig_") || strings.HasPrefix(n.Name, "_") {
continue
}
i := strings.Index(n.Name, "_")
if i < 0 {
continue
}
if prefix == "" {
prefix = n.Name[:i+1]
} else if prefix != n.Name[:i+1] {
return ""
}
}
}
return prefix
}
// Copyright 2011 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.
package main
import (
"bytes"
"fmt"
"go/ast"
"go/printer"
"go/token"
"os"
"strings"
)
// godefs returns the output for -godefs mode.
func (p *Package) godefs(f *File, srcfile string) string {
var buf bytes.Buffer
fmt.Fprintf(&buf, "// Created by cgo -godefs - DO NOT EDIT\n")
fmt.Fprintf(&buf, "// %s\n", strings.Join(os.Args, " "))
fmt.Fprintf(&buf, "\n")
override := make(map[string]string)
// Allow source file to specify override mappings.
// For example, the socket data structures refer
// to in_addr and in_addr6 structs but we want to be
// able to treat them as byte arrays, so the godefs
// inputs in package syscall say
//
// // +godefs map struct_in_addr [4]byte
// // +godefs map struct_in_addr6 [16]byte
//
for _, g := range f.Comments {
for _, c := range g.List {
i := strings.Index(c.Text, "+godefs map")
if i < 0 {
continue
}
s := strings.TrimSpace(c.Text[i+len("+godefs map"):])
i = strings.Index(s, " ")
if i < 0 {
fmt.Fprintf(os.Stderr, "invalid +godefs map comment: %s\n", c.Text)
continue
}
override["_Ctype_"+strings.TrimSpace(s[:i])] = strings.TrimSpace(s[i:])
}
}
for _, n := range f.Name {
if s := override[n.Go]; s != "" {
override[n.Mangle] = s
}
}
// Otherwise, if the source file says type T C.whatever,
// use "T" as the mangling of C.whatever,
// except in the definition (handled at end of function).
refName := make(map[*ast.Expr]*Name)
for _, r := range f.Ref {
refName[r.Expr] = r.Name
}
for _, d := range f.AST.Decls {
d, ok := d.(*ast.GenDecl)
if !ok || d.Tok != token.TYPE {
continue
}
for _, s := range d.Specs {
s := s.(*ast.TypeSpec)
n := refName[&s.Type]
if n != nil && n.Mangle != "" {
override[n.Mangle] = s.Name.Name
}
}
}
// Extend overrides using typedefs:
// If we know that C.xxx should format as T
// and xxx is a typedef for yyy, make C.yyy format as T.
for typ, def := range typedef {
if new := override[typ]; new != "" {
if id, ok := def.(*ast.Ident); ok {
override[id.Name] = new
}
}
}
// Apply overrides.
for old, new := range override {
if id := goIdent[old]; id != nil {
id.Name = new
}
}
// Any names still using the _C syntax are not going to compile,
// although in general we don't know whether they all made it
// into the file, so we can't warn here.
//
// The most common case is union types, which begin with
// _Ctype_union and for which typedef[name] is a Go byte
// array of the appropriate size (such as [4]byte).
// Substitute those union types with byte arrays.
for name, id := range goIdent {
if id.Name == name && strings.Contains(name, "_Ctype_union") {
if def := typedef[name]; def != nil {
id.Name = gofmt(def)
}
}
}
printer.Fprint(&buf, fset, f.AST)
return buf.String()
}
// cdefs returns the output for -cdefs mode.
// The easiest way to do this is to translate the godefs Go to C.
func (p *Package) cdefs(f *File, srcfile string) string {
godefsOutput := p.godefs(f, srcfile)
lines := strings.Split(godefsOutput, "\n")
lines[0] = "// Created by cgo -cdefs - DO NOT EDIT"
for i, line := range lines {
lines[i] = strings.TrimSpace(line)
}
var out bytes.Buffer
printf := func(format string, args ...interface{}) { fmt.Fprintf(&out, format, args...) }
didTypedef := false
for i := 0; i < len(lines); i++ {
line := lines[i]
// Delete
// package x
if strings.HasPrefix(line, "package ") {
continue
}
// Convert
// const (
// A = 1
// B = 2
// )
//
// to
//
// enum {
// A = 1,
// B = 2,
// };
if line == "const (" {
printf("enum {\n")
for i++; i < len(lines) && lines[i] != ")"; i++ {
line = lines[i]
if line != "" {
printf("\t%s,", line)
}
printf("\n")
}
printf("};\n")
continue
}
// Convert
// const A = 1
// to
// enum { A = 1 };
if strings.HasPrefix(line, "const ") {
printf("enum { %s };\n", line[len("const "):])
continue
}
// On first type definition, typedef all the structs
// in case there are dependencies between them.
if !didTypedef && strings.HasPrefix(line, "type ") {
didTypedef = true
for _, line := range lines {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "type ") && strings.HasSuffix(line, " struct {") {
s := line[len("type ") : len(line)-len(" struct {")]
printf("typedef struct %s %s;\n", s, s)
}
}
printf("\n")
printf("#pragma pack on\n")
printf("\n")
}
// Convert
// type T struct {
// X int64
// Y *int32
// Z [4]byte
// }
//
// to
//
// struct T {
// int64 X;
// int32 *Y;
// byte Z[4];
// }
if strings.HasPrefix(line, "type ") && strings.HasSuffix(line, " struct {") {
s := line[len("type ") : len(line)-len(" struct {")]
printf("struct %s {\n", s)
for i++; i < len(lines) && lines[i] != "}"; i++ {
line := lines[i]
if line != "" {
f := strings.Fields(line)
if len(f) != 2 {
fmt.Fprintf(os.Stderr, "cgo: cannot parse struct field: %s\n", line)
nerrors++
continue
}
printf("\t%s;", cdecl(f[0], f[1]))
}
printf("\n")
}
printf("};\n")
continue
}
// Convert
// type T int
// to
// typedef int T;
if strings.HasPrefix(line, "type ") {
f := strings.Fields(line[len("type "):])
if len(f) != 2 {
fmt.Fprintf(os.Stderr, "cgo: cannot parse type definition: %s\n", line)
nerrors++
continue
}
printf("typedef\t%s;\n", cdecl(f[0], f[1]))
continue
}
printf("%s\n", line)
}
if didTypedef {
printf("\n")
printf("#pragma pack off\n")
}
return out.String()
}
// cdecl returns the C declaration for the given Go name and type.
// It only handles the specific cases necessary for converting godefs output.
func cdecl(name, typ string) string {
// X *[0]byte -> X *void
if strings.HasPrefix(typ, "*[0]") {
typ = "*void"
}
// X *byte -> *X byte
if strings.HasPrefix(typ, "*") {
name = "*" + name
typ = typ[1:]
}
// X [4]byte -> X[4] byte
if strings.HasPrefix(typ, "[") {
i := strings.Index(typ, "]") + 1
name = name + typ[:i]
typ = typ[i:]
}
// X T -> T X
return typ + "\t" + name
}
var gofmtBuf bytes.Buffer
// gofmt returns the gofmt-formatted string for an AST node.
func gofmt(n interface{}) string {
gofmtBuf.Reset()
err := printer.Fprint(&gofmtBuf, fset, n)
if err != nil {
return "<" + err.Error() + ">"
}
return gofmtBuf.String()
}
......@@ -43,6 +43,7 @@ type Package struct {
// A File collects information about a single Go input file.
type File struct {
AST *ast.File // parsed AST
Comments []*ast.CommentGroup // comments from file
Package string // Package name
Preamble string // C preamble (doc comment on import "C")
Ref []*Ref // all references to C.xxx in AST
......@@ -123,6 +124,12 @@ var fset = token.NewFileSet()
var dynobj = flag.String("dynimport", "", "if non-empty, print dynamic import data for that file")
// These flags are for bootstrapping a new Go implementation,
// to generate Go and C headers that match the data layout and
// constant values used in the host's C libraries and system calls.
var godefs = flag.Bool("godefs", false, "for bootstrap: write Go definitions for C file to standard output")
var cdefs = flag.Bool("cdefs", false, "for bootstrap: write C definitions for C file to standard output")
var goarch, goos string
func main() {
......@@ -142,6 +149,11 @@ func main() {
return
}
if *godefs && *cdefs {
fmt.Fprintf(os.Stderr, "cgo: cannot use -cdefs and -godefs together\n")
os.Exit(2)
}
args := flag.Args()
if len(args) < 1 {
usage()
......@@ -159,36 +171,9 @@ func main() {
usage()
}
// Copy it to a new slice so it can grow.
gccOptions := make([]string, i)
copy(gccOptions, args[0:i])
goFiles := args[i:]
goarch = runtime.GOARCH
if s := os.Getenv("GOARCH"); s != "" {
goarch = s
}
goos = runtime.GOOS
if s := os.Getenv("GOOS"); s != "" {
goos = s
}
ptrSize := ptrSizeMap[goarch]
if ptrSize == 0 {
fatalf("unknown $GOARCH %q", goarch)
}
// Clear locale variables so gcc emits English errors [sic].
os.Setenv("LANG", "en_US.UTF-8")
os.Setenv("LC_ALL", "C")
os.Setenv("LC_CTYPE", "C")
p := &Package{
PtrSize: ptrSize,
GccOptions: gccOptions,
CgoFlags: make(map[string]string),
Written: make(map[string]bool),
}
p := newPackage(args[:i])
// Need a unique prefix for the global C symbols that
// we use to coordinate between gcc and ourselves.
......@@ -239,17 +224,58 @@ func main() {
pkg = filepath.Join(dir, pkg)
}
p.PackagePath = pkg
p.writeOutput(f, input)
p.Record(f)
if *godefs {
os.Stdout.WriteString(p.godefs(f, input))
} else if *cdefs {
os.Stdout.WriteString(p.cdefs(f, input))
} else {
p.writeOutput(f, input)
}
}
if !*godefs && !*cdefs {
p.writeDefs()
}
if nerrors > 0 {
os.Exit(2)
}
}
// newPackage returns a new Package that will invoke
// gcc with the additional arguments specified in args.
func newPackage(args []string) *Package {
// Copy the gcc options to a new slice so the list
// can grow without overwriting the slice that args is in.
gccOptions := make([]string, len(args))
copy(gccOptions, args)
goarch = runtime.GOARCH
if s := os.Getenv("GOARCH"); s != "" {
goarch = s
}
goos = runtime.GOOS
if s := os.Getenv("GOOS"); s != "" {
goos = s
}
ptrSize := ptrSizeMap[goarch]
if ptrSize == 0 {
fatalf("unknown $GOARCH %q", goarch)
}
// Reset locale variables so gcc emits English errors [sic].
os.Setenv("LANG", "en_US.UTF-8")
os.Setenv("LC_ALL", "C")
p := &Package{
PtrSize: ptrSize,
GccOptions: gccOptions,
CgoFlags: make(map[string]string),
Written: make(map[string]bool),
}
return p
}
// Record what needs to be recorded about f.
func (p *Package) Record(f *File) {
if p.PackageName == "" {
......
......@@ -53,7 +53,7 @@ func (p *Package) writeDefs() {
for name, def := range typedef {
fmt.Fprintf(fgo2, "type %s ", name)
printer.Fprint(fgo2, fset, def)
fmt.Fprintf(fgo2, "\n")
fmt.Fprintf(fgo2, "\n\n")
}
fmt.Fprintf(fgo2, "type _Ctype_void [0]byte\n")
......
# 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.inc
O:=$(HOST_O)
TARG=godefs
OFILES=\
main.$O\
stabs.$O\
util.$O\
HFILES=a.h
include ../../Make.ccmd
test: $(TARG)
./test.sh
// 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 <u.h>
#include <libc.h>
#include <bio.h>
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
int printed; // has the definition been printed yet?
};
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;
extern int kindsize[];
// Language output
typedef struct Lang Lang;
struct Lang
{
char *constbegin;
char *constfmt;
char *constend;
char *typdef;
char *typdefend;
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*);
// 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 is a bootstrapping tool for porting the Go runtime to new systems.
It translates C type declarations into C or Go type declarations
with the same memory layout.
Usage: godefs [-g package] [-c cc] [-f cc-arg]... [defs.c ...]
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 to godefs is a C input file that can be compiled by
the host system's standard C compiler (typically gcc).
This file is expected to define new types and enumerated constants
whose names begin with $ (a legal identifier character in gcc).
Godefs compile the given input file with the host compiler and
then parses the debug info embedded in the assembly output.
This is far easier than reading system headers on most machines.
The output from godefs is either C output intended for the
Plan 9 C compiler tool chain (6c, 8c, or 5c) or Go output.
The options are:
-g package
generate Go output using the given package name.
In the Go output, struct fields have leading xx_ prefixes
removed and the first character capitalized (exported).
-c cc
set the name of the host system's C compiler (default "gcc")
-f cc-arg
add cc-arg to the command line when invoking the system C compiler
(for example, -f -m64 to invoke gcc -m64).
Repeating this option adds multiple flags to the command line.
For example, if this is x.c:
#include <sys/stat.h>
typedef struct timespec $Timespec;
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 {
int64 tv_sec;
int64 tv_nsec;
};
#pragma pack off
and "godefs -g MyPackage x.c" generates:
// godefs -g MyPackage x.c
// MACHINE GENERATED - DO NOT EDIT.
package MyPackage
// Constants
const (
S_IFMT = 0xf000;
S_IFIFO = 0x1000;
S_IFCHR = 0x2000;
)
// Types
type Timespec struct {
Sec int64;
Nsec int64;
}
*/
package documentation
This diff is collapsed.
// 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;
}
// Written to parse max/min of vlong correctly.
static vlong
parseoctal(char **pp)
{
char *p;
vlong n;
p = *pp;
if(*p++ != '0')
return 0;
n = 0;
while(*p >= '0' && *p <= '9')
n = n << 3 | *p++ - '0';
*pp = p;
return n;
}
// 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
{
vlong lo;
vlong hi;
int kind;
};
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,
1LL << 63, ~(1LL << 63), Int64,
0, -1, Uint64,
4, 0, Float32,
8, 0, Float64,
16, 0, Void,
};
int kindsize[] = {
0,
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;
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:
fprint(2, "unknown type char %c in %s\n", *p, p);
*pp = "";
return t;
case '@': // type attribute
while (*++p != ';');
*pp = ++p;
return parsedef(pp, nil);
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 'B': // volatile
case 'k': // const
++*pp;
return parsedef(pp, nil);
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 == '0')
lo = parseoctal(&p);
else
lo = strtoll(p, &p, 10);
if(*p != ';' || *++p == ';') {
if(stabsdebug)
fprint(2, "range expected number: %s\n", p);
return nil;
}
if(*p == '0')
hi = parseoctal(&p);
else
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; i<nelem(intranges); i++) {
r = &intranges[i];
if(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;
}
while(f->type->kind == Typedef)
f->type = f->type->type;
// 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) {
tt = emalloc(sizeof *tt);
*tt = *f->type;
f->type = tt;
f->type->kind -= 2;
}
p++;
t->nf++;
}
break;
case 'x':
// reference to struct, union not yet defined.
p++;
switch(*p) {
case 's':
t->kind = Struct;
break;
case 'u':
t->kind = Union;
break;
default:
fprint(2, "unknown x type char x%c", *p);
*pp = "";
return t;
}
if(parsename(&p, &t->name) < 0)
return nil;
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;
}
#!/usr/bin/env bash
# Copyright 2011 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.
eval $(gomake --no-print-directory -f ../../Make.inc go-env)
TMP="testdata_tmp.go"
TEST="testdata.c"
GOLDEN="testdata_${GOOS}_${GOARCH}.golden"
case ${GOARCH} in
"amd64") CCARG="-f-m64";;
"386") CCARG="-f-m32";;
*) CCARG="";;
esac
cleanup() {
rm ${TMP}
}
error() {
cleanup
echo $1
exit 1
}
if [ ! -e ${GOLDEN} ]; then
echo "skipping - no golden defined for this platform"
exit
fi
./godefs -g test ${CCARG} ${TEST} > ${TMP}
if [ $? != 0 ]; then
error "Error: Could not run godefs for ${TEST}"
fi
diff ${TMP} ${GOLDEN}
if [ $? != 0 ]; then
error "FAIL: godefs for ${TEST} did not match ${GOLDEN}"
fi
cleanup
echo "PASS"
// Copyright 2011 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 <stdint.h>
// Issue 432 - enum fields in struct can cause misaligned struct fields
typedef enum {
a
} T1;
struct T2 {
uint8_t a;
T1 b;
T1 c;
uint16_t d;
};
typedef struct T2 T2;
typedef T2 $T2;
// Issue 1162 - structs with fields named Pad[0-9]+ conflict with field
// names used by godefs for padding
struct T3 {
uint8_t a;
int Pad0;
};
typedef struct T3 $T3;
// Issue 1466 - forward references to types in stabs debug info were
// always treated as enums
struct T4 {};
struct T5 {
struct T4 *a;
};
typedef struct T5 T5;
typedef struct T4 $T4;
typedef T5 $T5;
// Test constants and enumerations are printed correctly. clang/2.9 with
// -O2 and above causes Bprint to print %#llx values incorrectly.
enum {
$sizeofPtr = sizeof(void*),
$sizeofShort = sizeof(short),
$sizeofInt = sizeof(int),
$sizeofLong = sizeof(long),
$sizeofLongLong = sizeof(long long),
};
// ./godefs -g test -f-m32 testdata.c
// MACHINE GENERATED - DO NOT EDIT.
package test
// Constants
const (
sizeofPtr = 0x4;
sizeofShort = 0x2;
sizeofInt = 0x4;
sizeofLong = 0x4;
sizeofLongLong = 0x8;
)
// Types
type T2 struct {
A uint8;
Pad_godefs_0 [3]byte;
B uint32;
C uint32;
D uint16;
Pad_godefs_1 [2]byte;
}
type T3 struct {
A uint8;
Pad_godefs_0 [3]byte;
Pad0 int32;
}
type T4 struct {
}
type T5 struct {
A *T4;
}
// ./godefs -g test -f-m64 testdata.c
// MACHINE GENERATED - DO NOT EDIT.
package test
// Constants
const (
sizeofPtr = 0x8;
sizeofShort = 0x2;
sizeofInt = 0x4;
sizeofLong = 0x8;
sizeofLongLong = 0x8;
)
// Types
type T2 struct {
A uint8;
Pad_godefs_0 [3]byte;
B uint32;
C uint32;
D uint16;
Pad_godefs_1 [2]byte;
}
type T3 struct {
A uint8;
Pad_godefs_0 [3]byte;
Pad0 int32;
}
type T4 struct {
}
type T5 struct {
A *T4;
}
// ./godefs -g test testdata.c
// MACHINE GENERATED - DO NOT EDIT.
package test
// Constants
const (
sizeofPtr = 0x4;
sizeofShort = 0x2;
sizeofInt = 0x4;
sizeofLong = 0x4;
sizeofLongLong = 0x8;
)
// Types
type T2 struct {
A uint8;
Pad_godefs_0 [3]byte;
B uint32;
C uint32;
D uint16;
Pad_godefs_1 [2]byte;
}
type T3 struct {
A uint8;
Pad_godefs_0 [3]byte;
Pad0 int32;
}
type T4 struct {
}
type T5 struct {
A *T4;
}
// 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;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment