proc: fix type of some struct global variables

Normally variables that have a named struct as a type will get a
typedef entry as their type, sometimes however the Go linker will
decide to use the DW_TAG_structure_type entry instead.

For consistency always wrap a struct type into a typedef when we are
creating a new variables (see comment in newVariable for exceptions).

This fixes a bug where it would be impossible to call methods on a
global variable.
This commit is contained in:
aarzilli
2018-08-17 08:17:22 +02:00
committed by Derek Parker
parent cab09a4bbb
commit 0461af8392
6 changed files with 54 additions and 25 deletions

View File

@ -65,6 +65,8 @@ func makeclos(pa *astruct) func(int) string {
} }
} }
var ga astruct
func main() { func main() {
one, two := 1, 2 one, two := 1, 2
intslice := []int{1, 2, 3} intslice := []int{1, 2, 3}
@ -86,5 +88,5 @@ func main() {
runtime.Breakpoint() runtime.Breakpoint()
call1(one, two) call1(one, two)
fn2clos(2) fn2clos(2)
fmt.Println(one, two, zero, callpanic, callstacktrace, stringsJoin, intslice, stringslice, comma, a.VRcvr, a.PRcvr, pa, vable_a, vable_pa, pable_pa, fn2clos, fn2glob, fn2valmeth, fn2ptrmeth, fn2nil) fmt.Println(one, two, zero, callpanic, callstacktrace, stringsJoin, intslice, stringslice, comma, a.VRcvr, a.PRcvr, pa, vable_a, vable_pa, pable_pa, fn2clos, fn2glob, fn2valmeth, fn2ptrmeth, fn2nil, ga)
} }

View File

@ -88,6 +88,8 @@ type compileUnit struct {
concreteInlinedFns []inlinedFn // list of concrete inlined functions within this compile unit concreteInlinedFns []inlinedFn // list of concrete inlined functions within this compile unit
optimized bool // this compile unit is optimized optimized bool // this compile unit is optimized
producer string // producer attribute producer string // producer attribute
startOffset, endOffset dwarf.Offset // interval of offsets contained in this compile unit
} }
type partialUnitConstant struct { type partialUnitConstant struct {
@ -495,6 +497,15 @@ func (bi *BinaryInfo) findCompileUnit(pc uint64) *compileUnit {
return nil return nil
} }
func (bi *BinaryInfo) findCompileUnitForOffset(off dwarf.Offset) *compileUnit {
for _, cu := range bi.compileUnits {
if off >= cu.startOffset && off < cu.endOffset {
return cu
}
}
return nil
}
func (bi *BinaryInfo) Producer() string { func (bi *BinaryInfo) Producer() string {
for _, cu := range bi.compileUnits { for _, cu := range bi.compileUnits {
if cu.isgo && cu.producer != "" { if cu.isgo && cu.producer != "" {

View File

@ -1266,7 +1266,7 @@ func (v *Variable) mapAccess(idx *Variable) (*Variable, error) {
} }
if first { if first {
first = false first = false
if err := idx.isType(key.DwarfType, key.Kind); err != nil { if err := idx.isType(key.RealType, key.Kind); err != nil {
return nil, err return nil, err
} }
} }

View File

@ -197,19 +197,26 @@ func (bi *BinaryInfo) loadDebugInfoMaps(debugLineBytes []byte, wg *sync.WaitGrou
var pu *partialUnit = nil var pu *partialUnit = nil
var partialUnits = make(map[dwarf.Offset]*partialUnit) var partialUnits = make(map[dwarf.Offset]*partialUnit)
abstractOriginNameTable := make(map[dwarf.Offset]string) abstractOriginNameTable := make(map[dwarf.Offset]string)
var lastOffset dwarf.Offset
outer: outer:
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() { for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
if err != nil { if err != nil {
break break
} }
lastOffset = entry.Offset
switch entry.Tag { switch entry.Tag {
case dwarf.TagCompileUnit: case dwarf.TagCompileUnit:
if pu != nil { if pu != nil {
partialUnits[pu.entry.Offset] = pu partialUnits[pu.entry.Offset] = pu
pu = nil pu = nil
} }
if cu != nil {
cu.endOffset = entry.Offset
}
cu = &compileUnit{} cu = &compileUnit{}
cu.entry = entry cu.entry = entry
cu.startOffset = entry.Offset
if lang, _ := entry.Val(dwarf.AttrLanguage).(int64); lang == dwarfGoLanguage { if lang, _ := entry.Val(dwarf.AttrLanguage).(int64); lang == dwarfGoLanguage {
cu.isgo = true cu.isgo = true
} }
@ -439,6 +446,11 @@ outer:
} }
} }
} }
if cu != nil {
cu.endOffset = lastOffset + 1
}
sort.Sort(compileUnitsByLowpc(bi.compileUnits)) sort.Sort(compileUnitsByLowpc(bi.compileUnits))
sort.Sort(functionsDebugInfoByEntry(bi.Functions)) sort.Sort(functionsDebugInfoByEntry(bi.Functions))
sort.Sort(packageVarsByAddr(bi.packageVars)) sort.Sort(packageVarsByAddr(bi.packageVars))
@ -520,29 +532,6 @@ func (bi *BinaryInfo) registerRuntimeTypeToDIE(entry *dwarf.Entry, ardr *reader.
if _, ok := bi.runtimeTypeToDIE[off]; !ok { if _, ok := bi.runtimeTypeToDIE[off]; !ok {
bi.runtimeTypeToDIE[off] = runtimeTypeDIE{entry.Offset, -1} bi.runtimeTypeToDIE[off] = runtimeTypeDIE{entry.Offset, -1}
} }
return
}
if entry.Tag != dwarf.TagTypedef {
return
}
// For named structs the compiler will emit a TagStructType entry and a
// TagTypedef entry. The AttrGoRuntimeType is set on the TagStructType
// entry but we prefer to use the typedef instead, to make interface
// values consistent with other variables.
rtypOff, ok := entry.Val(dwarf.AttrType).(dwarf.Offset)
if !ok {
return
}
ardr.Seek(rtypOff)
rentry, _ := ardr.Next()
if rentry == nil {
return
}
if off, ok := rentry.Val(godwarf.AttrGoRuntimeType).(uint64); ok {
bi.runtimeTypeToDIE[off] = runtimeTypeDIE{entry.Offset, -1}
} }
} }

View File

@ -202,6 +202,31 @@ func (v *Variable) newVariable(name string, addr uintptr, dwarfType godwarf.Type
} }
func newVariable(name string, addr uintptr, dwarfType godwarf.Type, bi *BinaryInfo, mem MemoryReadWriter) *Variable { func newVariable(name string, addr uintptr, dwarfType godwarf.Type, bi *BinaryInfo, mem MemoryReadWriter) *Variable {
if styp, isstruct := dwarfType.(*godwarf.StructType); isstruct && !strings.Contains(styp.Name, "<") && !strings.Contains(styp.Name, "{") {
// For named structs the compiler will emit a DW_TAG_structure_type entry
// and a DW_TAG_typedef entry.
//
// Normally variables refer to the typedef entry but sometimes global
// variables will refer to the struct entry incorrectly.
// Also the runtime type offset resolution (runtimeTypeToDIE) will return
// the struct entry directly.
//
// In both cases we prefer to have a typedef type for consistency's sake.
//
// So we wrap all struct types into a fake typedef type except for:
// a. types not defined by go
// b. anonymous struct types (they contain the '{' character)
// c. Go internal struct types used to describe maps (they contain the '<'
// character).
cu := bi.findCompileUnitForOffset(dwarfType.Common().Offset)
if cu != nil && cu.isgo {
dwarfType = &godwarf.TypedefType{
CommonType: *(dwarfType.Common()),
Type: dwarfType,
}
}
}
v := &Variable{ v := &Variable{
Name: name, Name: name,
Addr: addr, Addr: addr,

View File

@ -1112,6 +1112,8 @@ func TestCallFunction(t *testing.T) {
{`fn2ptrmeth(14)`, []string{`:string:"14 - 6 = 8"`}, nil}, // indirect call of func value / set to pointer method {`fn2ptrmeth(14)`, []string{`:string:"14 - 6 = 8"`}, nil}, // indirect call of func value / set to pointer method
{"fn2nil()", nil, errors.New("nil pointer dereference")}, {"fn2nil()", nil, errors.New("nil pointer dereference")},
{"ga.PRcvr(2)", []string{`:string:"2 - 0 = 2"`}, nil},
} }
withTestProcess("fncall", t, func(p proc.Process, fixture protest.Fixture) { withTestProcess("fncall", t, func(p proc.Process, fixture protest.Fixture) {