mirror of
https://github.com/go-delve/delve.git
synced 2025-10-29 09:46:56 +08:00
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:
@ -65,6 +65,8 @@ func makeclos(pa *astruct) func(int) string {
|
||||
}
|
||||
}
|
||||
|
||||
var ga astruct
|
||||
|
||||
func main() {
|
||||
one, two := 1, 2
|
||||
intslice := []int{1, 2, 3}
|
||||
@ -86,5 +88,5 @@ func main() {
|
||||
runtime.Breakpoint()
|
||||
call1(one, two)
|
||||
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)
|
||||
}
|
||||
|
||||
@ -88,6 +88,8 @@ type compileUnit struct {
|
||||
concreteInlinedFns []inlinedFn // list of concrete inlined functions within this compile unit
|
||||
optimized bool // this compile unit is optimized
|
||||
producer string // producer attribute
|
||||
|
||||
startOffset, endOffset dwarf.Offset // interval of offsets contained in this compile unit
|
||||
}
|
||||
|
||||
type partialUnitConstant struct {
|
||||
@ -495,6 +497,15 @@ func (bi *BinaryInfo) findCompileUnit(pc uint64) *compileUnit {
|
||||
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 {
|
||||
for _, cu := range bi.compileUnits {
|
||||
if cu.isgo && cu.producer != "" {
|
||||
|
||||
@ -1266,7 +1266,7 @@ func (v *Variable) mapAccess(idx *Variable) (*Variable, error) {
|
||||
}
|
||||
if first {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@ -197,19 +197,26 @@ func (bi *BinaryInfo) loadDebugInfoMaps(debugLineBytes []byte, wg *sync.WaitGrou
|
||||
var pu *partialUnit = nil
|
||||
var partialUnits = make(map[dwarf.Offset]*partialUnit)
|
||||
abstractOriginNameTable := make(map[dwarf.Offset]string)
|
||||
var lastOffset dwarf.Offset
|
||||
|
||||
outer:
|
||||
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
lastOffset = entry.Offset
|
||||
switch entry.Tag {
|
||||
case dwarf.TagCompileUnit:
|
||||
if pu != nil {
|
||||
partialUnits[pu.entry.Offset] = pu
|
||||
pu = nil
|
||||
}
|
||||
if cu != nil {
|
||||
cu.endOffset = entry.Offset
|
||||
}
|
||||
cu = &compileUnit{}
|
||||
cu.entry = entry
|
||||
cu.startOffset = entry.Offset
|
||||
if lang, _ := entry.Val(dwarf.AttrLanguage).(int64); lang == dwarfGoLanguage {
|
||||
cu.isgo = true
|
||||
}
|
||||
@ -439,6 +446,11 @@ outer:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if cu != nil {
|
||||
cu.endOffset = lastOffset + 1
|
||||
}
|
||||
|
||||
sort.Sort(compileUnitsByLowpc(bi.compileUnits))
|
||||
sort.Sort(functionsDebugInfoByEntry(bi.Functions))
|
||||
sort.Sort(packageVarsByAddr(bi.packageVars))
|
||||
@ -520,29 +532,6 @@ func (bi *BinaryInfo) registerRuntimeTypeToDIE(entry *dwarf.Entry, ardr *reader.
|
||||
if _, ok := bi.runtimeTypeToDIE[off]; !ok {
|
||||
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}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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 {
|
||||
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{
|
||||
Name: name,
|
||||
Addr: addr,
|
||||
|
||||
@ -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
|
||||
|
||||
{"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) {
|
||||
|
||||
Reference in New Issue
Block a user