mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
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:
parent
a50ee009f7
commit
879a1c6a72
18 changed files with 513 additions and 1589 deletions
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue