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:
parent
b113f0d516
commit
a8b8f30d39
20
_fixtures/issue1601.go
Normal file
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")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user