mirror of
https://github.com/go-delve/delve.git
synced 2025-10-29 17:56:45 +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() {
|
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)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 != "" {
|
||||||
|
|||||||
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user