Skip to content

Commit

Permalink
proc: fix runtime type handling for Go 1.21
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
aarzilli committed May 14, 2023
1 parent e95ae9c commit b0a1be3
Show file tree
Hide file tree
Showing 7 changed files with 59 additions and 31 deletions.
4 changes: 2 additions & 2 deletions _scripts/rtype-out.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ type bmap struct {
}

type eface struct {
_type *_type
_type *_type|*internal/abi.Type
data unsafe.Pointer
}

Expand Down Expand Up @@ -50,7 +50,7 @@ type iface struct {
}

type itab struct {
_type *_type
_type *_type|*internal/abi.Type
}

type moduledata struct {
Expand Down
12 changes: 10 additions & 2 deletions pkg/proc/bininfo.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
}
}
Expand Down
6 changes: 4 additions & 2 deletions pkg/proc/fncall.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -1298,15 +1300,15 @@ 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),
}
return r, err1
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),
}
Expand Down
6 changes: 5 additions & 1 deletion pkg/proc/proc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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 {
Expand Down
11 changes: 8 additions & 3 deletions pkg/proc/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
}
Expand Down Expand Up @@ -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)
Expand Down
48 changes: 28 additions & 20 deletions pkg/proc/variables.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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 {
Expand All @@ -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
Expand Down
3 changes: 2 additions & 1 deletion pkg/proc/variables_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()")
Expand Down Expand Up @@ -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.
Expand Down

0 comments on commit b0a1be3

Please sign in to comment.