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
This commit is contained in:
Russ Cox 2011-11-10 19:08:04 -05:00
parent a50ee009f7
commit 879a1c6a72
18 changed files with 513 additions and 1589 deletions

View file

@ -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"
n.Const = n.Define
// 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,7 +1237,9 @@ 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
t.Go = name
if !*godefs && !*cdefs {
t.Go = name
}
}
}
@ -1331,38 +1364,61 @@ func (c *typeConv) Struct(dt *dwarf.StructType) (expr *ast.StructType, csyntax s
ident[f.Name] = f.Name
used[f.Name] = true
}
for cid, goid := range ident {
if token.Lookup([]byte(goid)).IsKeyword() {
// Avoid keyword
goid = "_" + goid
// Also avoid existing fields
for _, exist := used[goid]; exist; _, exist = used[goid] {
if !*godefs && !*cdefs {
for cid, goid := range ident {
if token.Lookup([]byte(goid)).IsKeyword() {
// Avoid keyword
goid = "_" + goid
}
used[goid] = true
ident[cid] = goid
// Also avoid existing fields
for _, exist := used[goid]; exist; _, exist = used[goid] {
goid = "_" + goid
}
used[goid] = true
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
}