Commit 99686ec7 authored by Gustavo Niemeyer's avatar Gustavo Niemeyer Committed by Russ Cox

cgo: fix dwarf type parsing

The recursive algorithm used to parse types in cgo
has a bug related to building the C type representation.

As an example, when the recursion starts at a type *T,
the C type representation won't be known until type T
itself is parsed.  But then, it is possible that type T
references the type **T internally.  The latter
representation is built based on the one of *T, which
started the recursion, so it won't attempt to parse it
again, and will instead use the current representation
value for *T, which is still empty at this point.

This problem was fixed by introducing a simple TypeRepr
type which builds the string representation lazily,
analogous to how the Go type information is built within
the same algorithm.  This way, even if a type
representation is still unknown at some level in the
recursion, representations dependant on it can still
be created correctly.

R=rsc
CC=golang-dev
https://golang.org/cl/4244052
parent 04ca4f82
...@@ -776,6 +776,32 @@ var dwarfToName = map[string]string{ ...@@ -776,6 +776,32 @@ var dwarfToName = map[string]string{
const signedDelta = 64 const signedDelta = 64
// String returns the current type representation. Format arguments
// are assembled within this method so that any changes in mutable
// values are taken into account.
func (tr *TypeRepr) String() string {
if len(tr.Repr) == 0 {
return ""
}
if len(tr.FormatArgs) == 0 {
return tr.Repr
}
return fmt.Sprintf(tr.Repr, tr.FormatArgs...)
}
// Empty returns true if the result of String would be "".
func (tr *TypeRepr) Empty() bool {
return len(tr.Repr) == 0
}
// Set modifies the type representation.
// If fargs are provided, repr is used as a format for fmt.Sprintf.
// Otherwise, repr is used unprocessed as the type representation.
func (tr *TypeRepr) Set(repr string, fargs ...interface{}) {
tr.Repr = repr
tr.FormatArgs = fargs
}
// Type returns a *Type with the same memory layout as // Type returns a *Type with the same memory layout as
// dtype when used as the type of a variable or a struct field. // dtype when used as the type of a variable or a struct field.
func (c *typeConv) Type(dtype dwarf.Type) *Type { func (c *typeConv) Type(dtype dwarf.Type) *Type {
...@@ -789,16 +815,15 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { ...@@ -789,16 +815,15 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type {
t := new(Type) t := new(Type)
t.Size = dtype.Size() t.Size = dtype.Size()
t.Align = -1 t.Align = -1
t.C = dtype.Common().Name t.C = &TypeRepr{Repr: dtype.Common().Name}
t.EnumValues = nil
c.m[dtype] = t c.m[dtype] = t
if t.Size < 0 { if t.Size < 0 {
// Unsized types are [0]byte // Unsized types are [0]byte
t.Size = 0 t.Size = 0
t.Go = c.Opaque(0) t.Go = c.Opaque(0)
if t.C == "" { if t.C.Empty() {
t.C = "void" t.C.Set("void")
} }
return t return t
} }
...@@ -827,7 +852,7 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { ...@@ -827,7 +852,7 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type {
sub := c.Type(dt.Type) sub := c.Type(dt.Type)
t.Align = sub.Align t.Align = sub.Align
gt.Elt = sub.Go gt.Elt = sub.Go
t.C = fmt.Sprintf("typeof(%s[%d])", sub.C, dt.Count) t.C.Set("typeof(%s[%d])", sub.C, dt.Count)
case *dwarf.BoolType: case *dwarf.BoolType:
t.Go = c.bool t.Go = c.bool
...@@ -844,7 +869,7 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { ...@@ -844,7 +869,7 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type {
if t.Align = t.Size; t.Align >= c.ptrSize { if t.Align = t.Size; t.Align >= c.ptrSize {
t.Align = c.ptrSize t.Align = c.ptrSize
} }
t.C = "enum " + dt.EnumName t.C.Set("enum " + dt.EnumName)
signed := 0 signed := 0
t.EnumValues = make(map[string]int64) t.EnumValues = make(map[string]int64)
for _, ev := range dt.Val { for _, ev := range dt.Val {
...@@ -932,7 +957,7 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { ...@@ -932,7 +957,7 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type {
// Translate void* as unsafe.Pointer // Translate void* as unsafe.Pointer
if _, ok := base(dt.Type).(*dwarf.VoidType); ok { if _, ok := base(dt.Type).(*dwarf.VoidType); ok {
t.Go = c.unsafePointer t.Go = c.unsafePointer
t.C = "void*" t.C.Set("void*")
break break
} }
...@@ -940,7 +965,7 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { ...@@ -940,7 +965,7 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type {
t.Go = gt // publish before recursive call t.Go = gt // publish before recursive call
sub := c.Type(dt.Type) sub := c.Type(dt.Type)
gt.X = sub.Go gt.X = sub.Go
t.C = sub.C + "*" t.C.Set("%s*", sub.C)
case *dwarf.QualType: case *dwarf.QualType:
// Ignore qualifier. // Ignore qualifier.
...@@ -955,21 +980,21 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { ...@@ -955,21 +980,21 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type {
if tag == "" { if tag == "" {
tag = "__" + strconv.Itoa(tagGen) tag = "__" + strconv.Itoa(tagGen)
tagGen++ tagGen++
} else if t.C == "" { } else if t.C.Empty() {
t.C = dt.Kind + " " + tag t.C.Set(dt.Kind + " " + tag)
} }
name := c.Ident("_Ctype_" + dt.Kind + "_" + tag) name := c.Ident("_Ctype_" + dt.Kind + "_" + tag)
t.Go = name // publish before recursive calls t.Go = name // publish before recursive calls
switch dt.Kind { switch dt.Kind {
case "union", "class": case "union", "class":
typedef[name.Name] = c.Opaque(t.Size) typedef[name.Name] = c.Opaque(t.Size)
if t.C == "" { if t.C.Empty() {
t.C = fmt.Sprintf("typeof(unsigned char[%d])", t.Size) t.C.Set("typeof(unsigned char[%d])", t.Size)
} }
case "struct": case "struct":
g, csyntax, align := c.Struct(dt) g, csyntax, align := c.Struct(dt)
if t.C == "" { if t.C.Empty() {
t.C = csyntax t.C.Set(csyntax)
} }
t.Align = align t.Align = align
typedef[name.Name] = g typedef[name.Name] = g
...@@ -1024,7 +1049,7 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { ...@@ -1024,7 +1049,7 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type {
case *dwarf.VoidType: case *dwarf.VoidType:
t.Go = c.void t.Go = c.void
t.C = "void" t.C.Set("void")
} }
switch dtype.(type) { switch dtype.(type) {
...@@ -1041,7 +1066,7 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type { ...@@ -1041,7 +1066,7 @@ func (c *typeConv) Type(dtype dwarf.Type) *Type {
} }
} }
if t.C == "" { if t.C.Empty() {
fatal("internal error: did not create C name for %s", dtype) fatal("internal error: did not create C name for %s", dtype)
} }
...@@ -1056,11 +1081,13 @@ func (c *typeConv) FuncArg(dtype dwarf.Type) *Type { ...@@ -1056,11 +1081,13 @@ func (c *typeConv) FuncArg(dtype dwarf.Type) *Type {
case *dwarf.ArrayType: case *dwarf.ArrayType:
// Arrays are passed implicitly as pointers in C. // Arrays are passed implicitly as pointers in C.
// In Go, we must be explicit. // In Go, we must be explicit.
tr := &TypeRepr{}
tr.Set("%s*", t.C)
return &Type{ return &Type{
Size: c.ptrSize, Size: c.ptrSize,
Align: c.ptrSize, Align: c.ptrSize,
Go: &ast.StarExpr{X: t.Go}, Go: &ast.StarExpr{X: t.Go},
C: t.C + "*", C: tr,
} }
case *dwarf.TypedefType: case *dwarf.TypedefType:
// C has much more relaxed rules than Go for // C has much more relaxed rules than Go for
...@@ -1189,7 +1216,7 @@ func (c *typeConv) Struct(dt *dwarf.StructType) (expr *ast.StructType, csyntax s ...@@ -1189,7 +1216,7 @@ func (c *typeConv) Struct(dt *dwarf.StructType) (expr *ast.StructType, csyntax s
fld[n] = &ast.Field{Names: []*ast.Ident{c.Ident(ident[f.Name])}, Type: t.Go} fld[n] = &ast.Field{Names: []*ast.Ident{c.Ident(ident[f.Name])}, Type: t.Go}
off += t.Size off += t.Size
buf.WriteString(t.C) buf.WriteString(t.C.String())
buf.WriteString(" ") buf.WriteString(" ")
buf.WriteString(f.Name) buf.WriteString(f.Name)
buf.WriteString("; ") buf.WriteString("; ")
......
...@@ -82,11 +82,17 @@ type ExpFunc struct { ...@@ -82,11 +82,17 @@ type ExpFunc struct {
ExpName string // name to use from C ExpName string // name to use from C
} }
// A TypeRepr contains the string representation of a type.
type TypeRepr struct {
Repr string
FormatArgs []interface{}
}
// A Type collects information about a type in both the C and Go worlds. // A Type collects information about a type in both the C and Go worlds.
type Type struct { type Type struct {
Size int64 Size int64
Align int64 Align int64
C string C *TypeRepr
Go ast.Expr Go ast.Expr
EnumValues map[string]int64 EnumValues map[string]int64
} }
......
...@@ -163,7 +163,7 @@ func (p *Package) structType(n *Name) (string, int64) { ...@@ -163,7 +163,7 @@ func (p *Package) structType(n *Name) (string, int64) {
off += pad off += pad
} }
qual := "" qual := ""
if t.C[len(t.C)-1] == '*' { if c := t.C.String(); c[len(c)-1] == '*' {
qual = "const " qual = "const "
} }
fmt.Fprintf(&buf, "\t\t%s%s r;\n", qual, t.C) fmt.Fprintf(&buf, "\t\t%s%s r;\n", qual, t.C)
...@@ -403,7 +403,7 @@ func (p *Package) writeExports(fgo2, fc, fm *os.File) { ...@@ -403,7 +403,7 @@ func (p *Package) writeExports(fgo2, fc, fm *os.File) {
if fntype.Results == nil || len(fntype.Results.List) == 0 { if fntype.Results == nil || len(fntype.Results.List) == 0 {
gccResult = "void" gccResult = "void"
} else if len(fntype.Results.List) == 1 && len(fntype.Results.List[0].Names) <= 1 { } else if len(fntype.Results.List) == 1 && len(fntype.Results.List[0].Names) <= 1 {
gccResult = p.cgoType(fntype.Results.List[0].Type).C gccResult = p.cgoType(fntype.Results.List[0].Type).C.String()
} else { } else {
fmt.Fprintf(fgcch, "\n/* Return type for %s */\n", exp.ExpName) fmt.Fprintf(fgcch, "\n/* Return type for %s */\n", exp.ExpName)
fmt.Fprintf(fgcch, "struct %s_return {\n", exp.ExpName) fmt.Fprintf(fgcch, "struct %s_return {\n", exp.ExpName)
...@@ -418,7 +418,7 @@ func (p *Package) writeExports(fgo2, fc, fm *os.File) { ...@@ -418,7 +418,7 @@ func (p *Package) writeExports(fgo2, fc, fm *os.File) {
// Build the wrapper function compiled by gcc. // Build the wrapper function compiled by gcc.
s := fmt.Sprintf("%s %s(", gccResult, exp.ExpName) s := fmt.Sprintf("%s %s(", gccResult, exp.ExpName)
if fn.Recv != nil { if fn.Recv != nil {
s += p.cgoType(fn.Recv.List[0].Type).C s += p.cgoType(fn.Recv.List[0].Type).C.String()
s += " recv" s += " recv"
} }
forFieldList(fntype.Params, forFieldList(fntype.Params,
...@@ -534,24 +534,28 @@ func forFieldList(fl *ast.FieldList, fn func(int, ast.Expr)) { ...@@ -534,24 +534,28 @@ func forFieldList(fl *ast.FieldList, fn func(int, ast.Expr)) {
} }
} }
func c(repr string, args ...interface{}) *TypeRepr {
return &TypeRepr{repr, args}
}
// Map predeclared Go types to Type. // Map predeclared Go types to Type.
var goTypes = map[string]*Type{ var goTypes = map[string]*Type{
"int": &Type{Size: 4, Align: 4, C: "int"}, "int": &Type{Size: 4, Align: 4, C: c("int")},
"uint": &Type{Size: 4, Align: 4, C: "uint"}, "uint": &Type{Size: 4, Align: 4, C: c("uint")},
"int8": &Type{Size: 1, Align: 1, C: "schar"}, "int8": &Type{Size: 1, Align: 1, C: c("schar")},
"uint8": &Type{Size: 1, Align: 1, C: "uchar"}, "uint8": &Type{Size: 1, Align: 1, C: c("uchar")},
"int16": &Type{Size: 2, Align: 2, C: "short"}, "int16": &Type{Size: 2, Align: 2, C: c("short")},
"uint16": &Type{Size: 2, Align: 2, C: "ushort"}, "uint16": &Type{Size: 2, Align: 2, C: c("ushort")},
"int32": &Type{Size: 4, Align: 4, C: "int"}, "int32": &Type{Size: 4, Align: 4, C: c("int")},
"uint32": &Type{Size: 4, Align: 4, C: "uint"}, "uint32": &Type{Size: 4, Align: 4, C: c("uint")},
"int64": &Type{Size: 8, Align: 8, C: "int64"}, "int64": &Type{Size: 8, Align: 8, C: c("int64")},
"uint64": &Type{Size: 8, Align: 8, C: "uint64"}, "uint64": &Type{Size: 8, Align: 8, C: c("uint64")},
"float": &Type{Size: 4, Align: 4, C: "float"}, "float": &Type{Size: 4, Align: 4, C: c("float")},
"float32": &Type{Size: 4, Align: 4, C: "float"}, "float32": &Type{Size: 4, Align: 4, C: c("float")},
"float64": &Type{Size: 8, Align: 8, C: "double"}, "float64": &Type{Size: 8, Align: 8, C: c("double")},
"complex": &Type{Size: 8, Align: 8, C: "__complex float"}, "complex": &Type{Size: 8, Align: 8, C: c("__complex float")},
"complex64": &Type{Size: 8, Align: 8, C: "__complex float"}, "complex64": &Type{Size: 8, Align: 8, C: c("__complex float")},
"complex128": &Type{Size: 16, Align: 16, C: "__complex double"}, "complex128": &Type{Size: 16, Align: 16, C: c("__complex double")},
} }
// Map an ast type to a Type. // Map an ast type to a Type.
...@@ -559,21 +563,21 @@ func (p *Package) cgoType(e ast.Expr) *Type { ...@@ -559,21 +563,21 @@ func (p *Package) cgoType(e ast.Expr) *Type {
switch t := e.(type) { switch t := e.(type) {
case *ast.StarExpr: case *ast.StarExpr:
x := p.cgoType(t.X) x := p.cgoType(t.X)
return &Type{Size: p.PtrSize, Align: p.PtrSize, C: x.C + "*"} return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("%s*", x.C)}
case *ast.ArrayType: case *ast.ArrayType:
if t.Len == nil { if t.Len == nil {
return &Type{Size: p.PtrSize + 8, Align: p.PtrSize, C: "GoSlice"} return &Type{Size: p.PtrSize + 8, Align: p.PtrSize, C: c("GoSlice")}
} }
case *ast.StructType: case *ast.StructType:
// TODO // TODO
case *ast.FuncType: case *ast.FuncType:
return &Type{Size: p.PtrSize, Align: p.PtrSize, C: "void*"} return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("void*")}
case *ast.InterfaceType: case *ast.InterfaceType:
return &Type{Size: 3 * p.PtrSize, Align: p.PtrSize, C: "GoInterface"} return &Type{Size: 3 * p.PtrSize, Align: p.PtrSize, C: c("GoInterface")}
case *ast.MapType: case *ast.MapType:
return &Type{Size: p.PtrSize, Align: p.PtrSize, C: "GoMap"} return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("GoMap")}
case *ast.ChanType: case *ast.ChanType:
return &Type{Size: p.PtrSize, Align: p.PtrSize, C: "GoChan"} return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("GoChan")}
case *ast.Ident: case *ast.Ident:
// Look up the type in the top level declarations. // Look up the type in the top level declarations.
// TODO: Handle types defined within a function. // TODO: Handle types defined within a function.
...@@ -598,10 +602,10 @@ func (p *Package) cgoType(e ast.Expr) *Type { ...@@ -598,10 +602,10 @@ func (p *Package) cgoType(e ast.Expr) *Type {
} }
} }
if t.Name == "uintptr" { if t.Name == "uintptr" {
return &Type{Size: p.PtrSize, Align: p.PtrSize, C: "uintptr"} return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("uintptr")}
} }
if t.Name == "string" { if t.Name == "string" {
return &Type{Size: p.PtrSize + 4, Align: p.PtrSize, C: "GoString"} return &Type{Size: p.PtrSize + 4, Align: p.PtrSize, C: c("GoString")}
} }
if r, ok := goTypes[t.Name]; ok { if r, ok := goTypes[t.Name]; ok {
if r.Align > p.PtrSize { if r.Align > p.PtrSize {
...@@ -612,11 +616,11 @@ func (p *Package) cgoType(e ast.Expr) *Type { ...@@ -612,11 +616,11 @@ func (p *Package) cgoType(e ast.Expr) *Type {
case *ast.SelectorExpr: case *ast.SelectorExpr:
id, ok := t.X.(*ast.Ident) id, ok := t.X.(*ast.Ident)
if ok && id.Name == "unsafe" && t.Sel.Name == "Pointer" { if ok && id.Name == "unsafe" && t.Sel.Name == "Pointer" {
return &Type{Size: p.PtrSize, Align: p.PtrSize, C: "void*"} return &Type{Size: p.PtrSize, Align: p.PtrSize, C: c("void*")}
} }
} }
error(e.Pos(), "unrecognized Go type %T", e) error(e.Pos(), "unrecognized Go type %T", e)
return &Type{Size: 4, Align: 4, C: "int"} return &Type{Size: 4, Align: 4, C: c("int")}
} }
const gccProlog = ` const gccProlog = `
......
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