From 2e6a64af11fe8b317ac5c4a3516991649e1f933f Mon Sep 17 00:00:00 2001 From: aarzilli Date: Sat, 13 May 2023 18:11:47 +0200 Subject: [PATCH] proc: fix runtime type handling for Go 1.21 Go 1.21 renamed runtime._type to internal/abi.Type and changed the name of its fields. Update Delve so that it uses the new names for loading interfaces and generic type parameters. --- _scripts/rtype-out.txt | 4 ++-- pkg/proc/bininfo.go | 12 ++++++++-- pkg/proc/fncall.go | 6 +++-- pkg/proc/proc_test.go | 3 ++- pkg/proc/types.go | 11 ++++++--- pkg/proc/variables.go | 48 ++++++++++++++++++++++---------------- pkg/proc/variables_test.go | 3 ++- 7 files changed, 56 insertions(+), 31 deletions(-) diff --git a/_scripts/rtype-out.txt b/_scripts/rtype-out.txt index 8799bfa837..7783cf447c 100644 --- a/_scripts/rtype-out.txt +++ b/_scripts/rtype-out.txt @@ -15,7 +15,7 @@ type bmap struct { } type eface struct { - _type *_type + _type *_type|*internal/abi.Type data unsafe.Pointer } @@ -50,7 +50,7 @@ type iface struct { } type itab struct { - _type *_type + _type *_type|*internal/abi.Type } type moduledata struct { diff --git a/pkg/proc/bininfo.go b/pkg/proc/bininfo.go index a12aac8a8c..4cd41dc4d8 100644 --- a/pkg/proc/bininfo.go +++ b/pkg/proc/bininfo.go @@ -900,6 +900,13 @@ func (bi *BinaryInfo) typeToImage(typ godwarf.Type) *Image { return bi.Images[typ.Common().Index] } +func (bi *BinaryInfo) runtimeTypeTypename() string { + if goversion.ProducerAfterOrEqual(bi.Producer(), 1, 21) { + return "internal/abi.Type" + } + return "runtime._type" +} + var errBinaryInfoClose = errors.New("multiple errors closing executable files") // Close closes all internal readers. @@ -2130,10 +2137,11 @@ func (bi *BinaryInfo) loadDebugInfoMaps(image *Image, debugInfoBytes, debugLineB if fn != nil && fn.cu.image == image { tree, err := image.getDwarfTree(fn.offset) if err == nil { - tree.Children, err = regabiMallocgcWorkaround(bi) + children, err := regabiMallocgcWorkaround(bi) if err != nil { - bi.logger.Errorf("could not patch runtime.mallogc: %v", err) + bi.logger.Errorf("could not patch runtime.mallocgc: %v", err) } else { + tree.Children = children image.runtimeMallocgcTree = tree } } diff --git a/pkg/proc/fncall.go b/pkg/proc/fncall.go index f5dd79a952..bd0df51534 100644 --- a/pkg/proc/fncall.go +++ b/pkg/proc/fncall.go @@ -1263,6 +1263,8 @@ func (e fakeEntry) AttrField(attr dwarf.Attr) *dwarf.Field { } func regabiMallocgcWorkaround(bi *BinaryInfo) ([]*godwarf.Tree, error) { + ptrToRuntimeType := "*" + bi.runtimeTypeTypename() + var err1 error t := func(name string) godwarf.Type { @@ -1298,7 +1300,7 @@ func regabiMallocgcWorkaround(bi *BinaryInfo) ([]*godwarf.Tree, error) { case "amd64": r := []*godwarf.Tree{ m("size", t("uintptr"), regnum.AMD64_Rax, false), - m("typ", t("*runtime._type"), regnum.AMD64_Rbx, false), + m("typ", t(ptrToRuntimeType), regnum.AMD64_Rbx, false), m("needzero", t("bool"), regnum.AMD64_Rcx, false), m("~r1", t("unsafe.Pointer"), regnum.AMD64_Rax, true), } @@ -1306,7 +1308,7 @@ func regabiMallocgcWorkaround(bi *BinaryInfo) ([]*godwarf.Tree, error) { case "arm64": r := []*godwarf.Tree{ m("size", t("uintptr"), regnum.ARM64_X0, false), - m("typ", t("*runtime._type"), regnum.ARM64_X0+1, false), + m("typ", t(ptrToRuntimeType), regnum.ARM64_X0+1, false), m("needzero", t("bool"), regnum.ARM64_X0+2, false), m("~r1", t("unsafe.Pointer"), regnum.ARM64_X0, true), } diff --git a/pkg/proc/proc_test.go b/pkg/proc/proc_test.go index 9706ace1c5..a48111b33b 100644 --- a/pkg/proc/proc_test.go +++ b/pkg/proc/proc_test.go @@ -4647,7 +4647,7 @@ func TestCgoStacktrace2(t *testing.T) { // If a panic happens during cgo execution the stacktrace should show the C // function that caused the problem. withTestProcess("cgosigsegvstack", t, func(p *proc.Target, grp *proc.TargetGroup, fixture protest.Fixture) { - grp.Continue() + assertNoError(grp.Continue(), t, "Continue") frames, err := proc.ThreadStacktrace(p.CurrentThread(), 100) assertNoError(err, t, "Stacktrace()") logStacktrace(t, p, frames) @@ -5099,6 +5099,7 @@ func TestStepOutPreservesGoroutine(t *testing.T) { candg := []*proc.G{} bestg := []*proc.G{} for _, g := range gs { + t.Logf("stacktracing goroutine %d (%v)\n", g.ID, g.CurrentLoc) frames, err := g.Stacktrace(20, 0) assertNoError(err, t, "Stacktrace") for _, frame := range frames { diff --git a/pkg/proc/types.go b/pkg/proc/types.go index 1940b26a5a..125bff123d 100644 --- a/pkg/proc/types.go +++ b/pkg/proc/types.go @@ -122,6 +122,8 @@ func runtimeTypeToDIE(_type *Variable, dataAddr uint64) (typ godwarf.Type, kind if rtdie.kind == -1 { if kindField := _type.loadFieldNamed("kind"); kindField != nil && kindField.Value != nil { rtdie.kind, _ = constant.Int64Val(kindField.Value) + } else if kindField := _type.loadFieldNamed("Kind_"); kindField != nil && kindField.Value != nil { + rtdie.kind, _ = constant.Int64Val(kindField.Value) } } return typ, rtdie.kind, nil @@ -146,7 +148,7 @@ func resolveParametricType(bi *BinaryInfo, mem MemoryReadWriter, t godwarf.Type, if err != nil { return ptyp.TypedefType.Type, err } - runtimeType, err := bi.findType("runtime._type") + runtimeType, err := bi.findType(bi.runtimeTypeTypename()) if err != nil { return ptyp.TypedefType.Type, err } @@ -189,13 +191,16 @@ func dwarfToRuntimeType(bi *BinaryInfo, mem MemoryReadWriter, typ godwarf.Type) typeAddr = uint64(md.types) + off - rtyp, err := bi.findType("runtime._type") + rtyp, err := bi.findType(bi.runtimeTypeTypename()) if err != nil { return 0, 0, false, err } _type := newVariable("", typeAddr, rtyp, bi, mem) kindv := _type.loadFieldNamed("kind") - if kindv.Unreadable != nil || kindv.Kind != reflect.Uint { + if kindv == nil || kindv.Unreadable != nil || kindv.Kind != reflect.Uint { + kindv = _type.loadFieldNamed("Kind_") + } + if kindv == nil || kindv.Unreadable != nil || kindv.Kind != reflect.Uint { return 0, 0, false, fmt.Errorf("unreadable interface type: %v", kindv.Unreadable) } typeKind, _ = constant.Uint64Val(kindv.Value) diff --git a/pkg/proc/variables.go b/pkg/proc/variables.go index e77515ff90..e04e16fa8b 100644 --- a/pkg/proc/variables.go +++ b/pkg/proc/variables.go @@ -671,7 +671,7 @@ func newVariable(name string, addr uint64, dwarfType godwarf.Type, bi *BinaryInf v.stride = 1 v.fieldType = &godwarf.UintType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: 1, Name: "byte", ReflectKind: reflect.Uint8}, BitSize: 8, BitOffset: 0}} if v.Addr != 0 { - v.Base, v.Len, v.Unreadable = readStringInfo(v.mem, v.bi.Arch, v.Addr) + v.Base, v.Len, v.Unreadable = readStringInfo(v.mem, v.bi.Arch, v.Addr, t) } case *godwarf.SliceType: v.Kind = reflect.Slice @@ -1455,30 +1455,38 @@ func convertToEface(srcv, dstv *Variable) error { return dstv.writeEmptyInterface(typeAddr, srcv) } -func readStringInfo(mem MemoryReadWriter, arch *Arch, addr uint64) (uint64, int64, error) { +func readStringInfo(mem MemoryReadWriter, arch *Arch, addr uint64, typ *godwarf.StringType) (uint64, int64, error) { // string data structure is always two ptrs in size. Addr, followed by len // http://research.swtch.com/godata mem = cacheMemory(mem, addr, arch.PtrSize()*2) - // read len - strlen, err := readIntRaw(mem, addr+uint64(arch.PtrSize()), int64(arch.PtrSize())) - if err != nil { - return 0, 0, fmt.Errorf("could not read string len %s", err) - } - if strlen < 0 { - return 0, 0, fmt.Errorf("invalid length: %d", strlen) - } + var strlen int64 + var outaddr uint64 + var err error - // read addr - addr, err = readUintRaw(mem, addr, int64(arch.PtrSize())) - if err != nil { - return 0, 0, fmt.Errorf("could not read string pointer %s", err) - } - if addr == 0 { - return 0, 0, nil + for _, field := range typ.StructType.Field { + switch field.Name { + case "len": + strlen, err = readIntRaw(mem, addr+uint64(field.ByteOffset), int64(arch.PtrSize())) + if err != nil { + return 0, 0, fmt.Errorf("could not read string len %s", err) + } + if strlen < 0 { + return 0, 0, fmt.Errorf("invalid length: %d", strlen) + } + case "str": + outaddr, err = readUintRaw(mem, addr+uint64(field.ByteOffset), int64(arch.PtrSize())) + if err != nil { + return 0, 0, fmt.Errorf("could not read string pointer %s", err) + } + if addr == 0 { + return 0, 0, nil + } + } } - return addr, strlen, nil + + return outaddr, strlen, nil } func readStringValue(mem MemoryReadWriter, addr uint64, strlen int64, cfg LoadConfig) (string, error) { @@ -2248,7 +2256,7 @@ func (v *Variable) readInterface() (_type, data *Variable, isnil bool) { // +rtype -field iface.tab *itab // +rtype -field iface.data unsafe.Pointer - // +rtype -field eface._type *_type + // +rtype -field eface._type *_type|*internal/abi.Type // +rtype -field eface.data unsafe.Pointer for _, f := range ityp.Field { @@ -2259,7 +2267,7 @@ func (v *Variable) readInterface() (_type, data *Variable, isnil bool) { isnil = tab.Addr == 0 if !isnil { var err error - _type, err = tab.structMember("_type") // +rtype *_type + _type, err = tab.structMember("_type") // +rtype *_type|*internal/abi.Type if err != nil { v.Unreadable = fmt.Errorf("invalid interface type: %v", err) return diff --git a/pkg/proc/variables_test.go b/pkg/proc/variables_test.go index 00f7f7b084..82c155e4f3 100644 --- a/pkg/proc/variables_test.go +++ b/pkg/proc/variables_test.go @@ -212,7 +212,7 @@ func TestSetVariable(t *testing.T) { assertNoError(err, t, "EvalVariable()") assertVariable(t, variable, varTest{tc.name, true, tc.startVal, "", tc.typ, nil}) - assertNoError(setVariable(p, tc.name, tc.expr), t, "SetVariable()") + assertNoError(setVariable(p, tc.name, tc.expr), t, fmt.Sprintf("SetVariable(%q, %q)", tc.name, tc.expr)) variable, err = evalVariableWithCfg(p, tc.name, pnormalLoadConfig) assertNoError(err, t, "EvalVariable()") @@ -856,6 +856,7 @@ func TestEvalExpression(t *testing.T) { assertNoError(grp.Continue(), t, "Continue() returned an error") for i, tc := range testcases { t.Run(strconv.Itoa(i), func(t *testing.T) { + t.Logf("%q", tc.name) variable, err := evalVariableWithCfg(p, tc.name, pnormalLoadConfig) if err != nil && err.Error() == "evaluating methods not supported on this version of Go" { // this type of eval is unsupported with the current version of Go.