godwarf: support recursive types involving C qualifiers and typedefs (#1603)

Backports debug/dwarf commit: 535741a69a1300d1fe2800778b99c8a1b75d7fdd
CL: https://go-review.googlesource.com/18459

The x/debug/dwarf that we used for dwarf/godwarf/type.go was forked
from debug/dwarf long before this commit.

Original description:

    Currently readType simultaneously constructs a type graph and resolves
    the sizes of the types. However, these two operations are
    fundamentally at odds: the order we parse a cyclic structure in may be
    different than the order we need to resolve type sizes in. As a
    result, it's possible that when readType attempts to resolve the size
    of a typedef, it may dereference a nil Type field of another typedef
    retrieved from the type cache that's only partially constructed.

    To fix this, we delay resolving typedef sizes until the end of the
    readType recursion, when the full type graph is constructed.

Fixes #1601
This commit is contained in:
Alessandro Arzilli 2019-07-08 19:24:56 +02:00 committed by Derek Parker
parent b113f0d516
commit a8b8f30d39
3 changed files with 56 additions and 9 deletions

20
_fixtures/issue1601.go Normal file

@ -0,0 +1,20 @@
package main
import (
"runtime"
)
/*
typedef struct Qst Q1;
typedef const Q1 Q;
struct Qst {
Q *q;
};
const Q1 globalq;
*/
import "C"
func main() {
runtime.Breakpoint()
}

@ -483,7 +483,7 @@ func (t *ChanType) stringIntl(recCheck recCheck) string {
// Type reads the type at off in the DWARF ``info'' section. // Type reads the type at off in the DWARF ``info'' section.
func ReadType(d *dwarf.Data, index int, off dwarf.Offset, typeCache map[dwarf.Offset]Type) (Type, error) { func ReadType(d *dwarf.Data, index int, off dwarf.Offset, typeCache map[dwarf.Offset]Type) (Type, error) {
typ, err := readType(d, "info", d.Reader(), off, typeCache) typ, err := readType(d, "info", d.Reader(), off, typeCache, nil)
if typ != nil { if typ != nil {
typ.Common().Index = index typ.Common().Index = index
} }
@ -495,9 +495,14 @@ func getKind(e *dwarf.Entry) reflect.Kind {
return reflect.Kind(integer) return reflect.Kind(integer)
} }
type delayedSize struct {
ct *CommonType // type that needs its size computed from ut
ut Type // underlying type
}
// readType reads a type from r at off of name using and updating a // readType reads a type from r at off of name using and updating a
// type cache. // type cache, callers sohuld pass nil to delayedSize, it is used for recursion.
func readType(d *dwarf.Data, name string, r *dwarf.Reader, off dwarf.Offset, typeCache map[dwarf.Offset]Type) (Type, error) { func readType(d *dwarf.Data, name string, r *dwarf.Reader, off dwarf.Offset, typeCache map[dwarf.Offset]Type, delayedSizes *[]delayedSize) (Type, error) {
if t, ok := typeCache[off]; ok { if t, ok := typeCache[off]; ok {
return t, nil return t, nil
} }
@ -511,9 +516,23 @@ func readType(d *dwarf.Data, name string, r *dwarf.Reader, off dwarf.Offset, typ
return nil, dwarf.DecodeError{name, off, "no type at offset"} return nil, dwarf.DecodeError{name, off, "no type at offset"}
} }
// If this is the root of the recursion, prepare to resolve typedef sizes
// once the recursion is done. This must be done after the type graph is
// constructed because it may need to resolve cycles in a different order
// than readType encounters them.
if delayedSizes == nil {
var delayedSizeList []delayedSize
defer func() {
for _, ds := range delayedSizeList {
ds.ct.ByteSize = ds.ut.Size()
}
}()
delayedSizes = &delayedSizeList
}
// Parse type from dwarf.Entry. // Parse type from dwarf.Entry.
// Must always set typeCache[off] before calling // Must always set typeCache[off] before calling
// d.Type recursively, to handle circular types correctly. // d.readType recursively, to handle circular types correctly.
var typ Type var typ Type
nextDepth := 0 nextDepth := 0
@ -558,7 +577,7 @@ func readType(d *dwarf.Data, name string, r *dwarf.Reader, off dwarf.Offset, typ
var t Type var t Type
switch toff := tval.(type) { switch toff := tval.(type) {
case dwarf.Offset: case dwarf.Offset:
if t, err = readType(d, name, d.Reader(), toff, typeCache); err != nil { if t, err = readType(d, name, d.Reader(), toff, typeCache, delayedSizes); err != nil {
return nil return nil
} }
case uint64: case uint64:
@ -971,13 +990,13 @@ func readType(d *dwarf.Data, name string, r *dwarf.Reader, off dwarf.Offset, typ
b = -1 b = -1
switch t := typ.(type) { switch t := typ.(type) {
case *TypedefType: case *TypedefType:
b = t.Type.Size() *delayedSizes = append(*delayedSizes, delayedSize{typ.Common(), t.Type})
case *MapType: case *MapType:
b = t.Type.Size() *delayedSizes = append(*delayedSizes, delayedSize{typ.Common(), t.Type})
case *ChanType: case *ChanType:
b = t.Type.Size() *delayedSizes = append(*delayedSizes, delayedSize{typ.Common(), t.Type})
case *InterfaceType: case *InterfaceType:
b = t.Type.Size() *delayedSizes = append(*delayedSizes, delayedSize{typ.Common(), t.Type})
case *PtrType: case *PtrType:
b = int64(addressSize) b = int64(addressSize)
case *FuncType: case *FuncType:

@ -4410,3 +4410,11 @@ func TestPluginStepping(t *testing.T) {
{contNext, "plugin2.go:26"}, {contNext, "plugin2.go:26"},
{contNext, "plugintest2.go:42"}}) {contNext, "plugintest2.go:42"}})
} }
func TestIssue1601(t *testing.T) {
//Tests that recursive types involving C qualifiers and typedefs are parsed correctly
withTestProcess("issue1601", t, func(p proc.Process, fixture protest.Fixture) {
assertNoError(proc.Continue(p), t, "Continue")
evalVariable(p, t, "C.globalq")
})
}