proc: fix runtime type handling for Go 1.21 (#3370)

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.
This commit is contained in:
Alessandro Arzilli 2023-05-15 19:21:52 +02:00 committed by GitHub
parent e95ae9c21b
commit 674bd63996
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 59 additions and 31 deletions

@ -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 {

@ -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
}
}

@ -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),
}

@ -4647,7 +4647,10 @@ 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()
err := grp.Continue()
if _, exited := err.(proc.ErrProcessExited); exited {
t.Fatal("process exited")
}
frames, err := proc.ThreadStacktrace(p.CurrentThread(), 100)
assertNoError(err, t, "Stacktrace()")
logStacktrace(t, p, frames)
@ -5099,6 +5102,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 {

@ -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)

@ -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
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
}
}
}
// 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
}
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

@ -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.