proc: remove old method to resolve the type of an interface to a DIE (#3150)
Before 1.11 we used to read a bunch of runtime structures to determine the runtime type of an interface variable. This had significant dependencies on private structs of the Go runtime and the code is broken for versions of Go after 1.17. Remove all this code, since it is no longer used and doesn't work with newer versions of Go anyway.
This commit is contained in:
parent
ec5fcc07fe
commit
02d46b059e
@ -96,11 +96,6 @@ type BinaryInfo struct {
|
|||||||
|
|
||||||
gStructOffset uint64
|
gStructOffset uint64
|
||||||
|
|
||||||
// nameOfRuntimeType maps an address of a runtime._type struct to its
|
|
||||||
// decoded name. Used with versions of Go <= 1.10 to figure out the DIE of
|
|
||||||
// the concrete type of interfaces.
|
|
||||||
nameOfRuntimeType map[uint64]nameOfRuntimeTypeEntry
|
|
||||||
|
|
||||||
// consts[off] lists all the constants with the type defined at offset off.
|
// consts[off] lists all the constants with the type defined at offset off.
|
||||||
consts constantsMap
|
consts constantsMap
|
||||||
|
|
||||||
@ -639,7 +634,7 @@ type ElfDynamicSection struct {
|
|||||||
|
|
||||||
// NewBinaryInfo returns an initialized but unloaded BinaryInfo struct.
|
// NewBinaryInfo returns an initialized but unloaded BinaryInfo struct.
|
||||||
func NewBinaryInfo(goos, goarch string) *BinaryInfo {
|
func NewBinaryInfo(goos, goarch string) *BinaryInfo {
|
||||||
r := &BinaryInfo{GOOS: goos, nameOfRuntimeType: make(map[uint64]nameOfRuntimeTypeEntry), logger: logflags.DebuggerLogger()}
|
r := &BinaryInfo{GOOS: goos, logger: logflags.DebuggerLogger()}
|
||||||
|
|
||||||
// TODO: find better way to determine proc arch (perhaps use executable file info).
|
// TODO: find better way to determine proc arch (perhaps use executable file info).
|
||||||
switch goarch {
|
switch goarch {
|
||||||
|
|||||||
@ -1,10 +1,5 @@
|
|||||||
package proc
|
package proc
|
||||||
|
|
||||||
import (
|
|
||||||
"go/constant"
|
|
||||||
"unsafe"
|
|
||||||
)
|
|
||||||
|
|
||||||
// delve counterpart to runtime.moduledata
|
// delve counterpart to runtime.moduledata
|
||||||
type moduleData struct {
|
type moduleData struct {
|
||||||
text, etext uint64
|
text, etext uint64
|
||||||
@ -83,127 +78,3 @@ func findModuleDataForType(bi *BinaryInfo, mds []moduleData, typeAddr uint64, me
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveTypeOff(bi *BinaryInfo, mds []moduleData, typeAddr, off uint64, mem MemoryReadWriter) (*Variable, error) {
|
|
||||||
// See runtime.(*_type).typeOff in $GOROOT/src/runtime/type.go
|
|
||||||
md := findModuleDataForType(bi, mds, typeAddr, mem)
|
|
||||||
|
|
||||||
rtyp, err := bi.findType("runtime._type")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if md == nil {
|
|
||||||
v, err := reflectOffsMapAccess(bi, off, mem)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
v.loadValue(LoadConfig{false, 1, 0, 0, -1, 0})
|
|
||||||
addr, _ := constant.Int64Val(v.Value)
|
|
||||||
return v.newVariable(v.Name, uint64(addr), rtyp, mem), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if t, _ := md.typemapVar.mapAccess(newConstant(constant.MakeUint64(uint64(off)), mem)); t != nil {
|
|
||||||
return t, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
res := md.types + off
|
|
||||||
|
|
||||||
return newVariable("", uint64(res), rtyp, bi, mem), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func resolveNameOff(bi *BinaryInfo, mds []moduleData, typeAddr, off uint64, mem MemoryReadWriter) (name, tag string, pkgpathoff int32, err error) {
|
|
||||||
// See runtime.resolveNameOff in $GOROOT/src/runtime/type.go
|
|
||||||
for _, md := range mds {
|
|
||||||
if typeAddr >= md.types && typeAddr < md.etypes {
|
|
||||||
return loadName(bi, md.types+off, mem)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
v, err := reflectOffsMapAccess(bi, off, mem)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
resv := v.maybeDereference()
|
|
||||||
if resv.Unreadable != nil {
|
|
||||||
return "", "", 0, resv.Unreadable
|
|
||||||
}
|
|
||||||
|
|
||||||
return loadName(bi, resv.Addr, mem)
|
|
||||||
}
|
|
||||||
|
|
||||||
func reflectOffsMapAccess(bi *BinaryInfo, off uint64, mem MemoryReadWriter) (*Variable, error) {
|
|
||||||
scope := globalScope(nil, bi, bi.Images[0], mem)
|
|
||||||
reflectOffs, err := scope.findGlobal("runtime", "reflectOffs")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
reflectOffsm, err := reflectOffs.structMember("m")
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return reflectOffsm.mapAccess(newConstant(constant.MakeUint64(uint64(off)), mem))
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
// flags for the name struct (see 'type name struct' in $GOROOT/src/reflect/type.go)
|
|
||||||
nameflagExported = 1 << 0
|
|
||||||
nameflagHasTag = 1 << 1
|
|
||||||
nameflagHasPkg = 1 << 2
|
|
||||||
)
|
|
||||||
|
|
||||||
func loadName(bi *BinaryInfo, addr uint64, mem MemoryReadWriter) (name, tag string, pkgpathoff int32, err error) {
|
|
||||||
off := addr
|
|
||||||
namedata := make([]byte, 3)
|
|
||||||
_, err = mem.ReadMemory(namedata, off)
|
|
||||||
off += 3
|
|
||||||
if err != nil {
|
|
||||||
return "", "", 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
namelen := uint16(namedata[1])<<8 | uint16(namedata[2])
|
|
||||||
|
|
||||||
rawstr := make([]byte, int(namelen))
|
|
||||||
_, err = mem.ReadMemory(rawstr, off)
|
|
||||||
off += uint64(namelen)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
name = string(rawstr)
|
|
||||||
|
|
||||||
if namedata[0]&nameflagHasTag != 0 {
|
|
||||||
taglendata := make([]byte, 2)
|
|
||||||
_, err = mem.ReadMemory(taglendata, off)
|
|
||||||
off += 2
|
|
||||||
if err != nil {
|
|
||||||
return "", "", 0, err
|
|
||||||
}
|
|
||||||
taglen := uint16(taglendata[0])<<8 | uint16(taglendata[1])
|
|
||||||
|
|
||||||
rawstr := make([]byte, int(taglen))
|
|
||||||
_, err = mem.ReadMemory(rawstr, off)
|
|
||||||
off += uint64(taglen)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
tag = string(rawstr)
|
|
||||||
}
|
|
||||||
|
|
||||||
if namedata[0]&nameflagHasPkg != 0 {
|
|
||||||
pkgdata := make([]byte, 4)
|
|
||||||
_, err = mem.ReadMemory(pkgdata, off)
|
|
||||||
if err != nil {
|
|
||||||
return "", "", 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// see func pkgPath in $GOROOT/src/reflect/type.go
|
|
||||||
copy((*[4]byte)(unsafe.Pointer(&pkgpathoff))[:], pkgdata)
|
|
||||||
}
|
|
||||||
|
|
||||||
return name, tag, pkgpathoff, nil
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,17 +1,14 @@
|
|||||||
package proc
|
package proc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"debug/dwarf"
|
"debug/dwarf"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/constant"
|
"go/constant"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/go-delve/delve/pkg/dwarf/godwarf"
|
"github.com/go-delve/delve/pkg/dwarf/godwarf"
|
||||||
"github.com/go-delve/delve/pkg/dwarf/reader"
|
"github.com/go-delve/delve/pkg/dwarf/reader"
|
||||||
"github.com/go-delve/delve/pkg/goversion"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// The kind field in runtime._type is a reflect.Kind value plus
|
// The kind field in runtime._type is a reflect.Kind value plus
|
||||||
@ -24,24 +21,6 @@ const (
|
|||||||
kindMask = (1 << 5) - 1 // +rtype kindMask
|
kindMask = (1 << 5) - 1 // +rtype kindMask
|
||||||
)
|
)
|
||||||
|
|
||||||
// Value of tflag field in runtime._type.
|
|
||||||
// See $GOROOT/reflect/type.go for a description of these flags.
|
|
||||||
const (
|
|
||||||
tflagUncommon = 1 << 0
|
|
||||||
tflagExtraStar = 1 << 1
|
|
||||||
tflagNamed = 1 << 2
|
|
||||||
)
|
|
||||||
|
|
||||||
// These constants contain the names of the fields of runtime.interfacetype
|
|
||||||
// and runtime.imethod.
|
|
||||||
// runtime.interfacetype.mhdr is a slice of runtime.imethod describing the
|
|
||||||
// methods of the interface.
|
|
||||||
const (
|
|
||||||
imethodFieldName = "name"
|
|
||||||
imethodFieldItyp = "ityp"
|
|
||||||
interfacetypeFieldMhdr = "mhdr"
|
|
||||||
)
|
|
||||||
|
|
||||||
type runtimeTypeDIE struct {
|
type runtimeTypeDIE struct {
|
||||||
offset dwarf.Offset
|
offset dwarf.Offset
|
||||||
kind int64
|
kind int64
|
||||||
@ -150,28 +129,7 @@ func runtimeTypeToDIE(_type *Variable, dataAddr uint64) (typ godwarf.Type, kind
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// go1.7 to go1.10 implementation: convert runtime._type structs to type names
|
return nil, 0, fmt.Errorf("could not resolve interface type")
|
||||||
|
|
||||||
if goversion.ProducerAfterOrEqual(_type.bi.Producer(), 1, 17) {
|
|
||||||
// Go 1.17 changed the encoding of names in runtime._type breaking the
|
|
||||||
// code below, but the codepath above, using runtimeTypeToDIE should be
|
|
||||||
// enough.
|
|
||||||
// The change happened in commit 287025925f66f90ad9b30aea2e533928026a8376
|
|
||||||
// reviewed in https://go-review.googlesource.com/c/go/+/318249
|
|
||||||
return nil, 0, fmt.Errorf("could not resolve interface type")
|
|
||||||
}
|
|
||||||
|
|
||||||
typename, kind, err := nameOfRuntimeType(mds, _type)
|
|
||||||
if err != nil {
|
|
||||||
return nil, 0, fmt.Errorf("invalid interface type: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
typ, err = bi.findType(typename)
|
|
||||||
if err != nil {
|
|
||||||
return nil, 0, fmt.Errorf("interface type %q not found for %#x: %v", typename, dataAddr, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return typ, kind, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolveParametricType returns the real type of t if t is a parametric
|
// resolveParametricType returns the real type of t if t is a parametric
|
||||||
@ -202,449 +160,6 @@ func resolveParametricType(tgt *Target, bi *BinaryInfo, mem MemoryReadWriter, t
|
|||||||
return typ, nil
|
return typ, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type nameOfRuntimeTypeEntry struct {
|
|
||||||
typename string
|
|
||||||
kind int64
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the type name of the type described in _type.
|
|
||||||
// _type is a non-loaded Variable pointing to runtime._type struct in the target.
|
|
||||||
// The returned string is in the format that's used in DWARF data
|
|
||||||
func nameOfRuntimeType(mds []moduleData, _type *Variable) (typename string, kind int64, err error) {
|
|
||||||
if e, ok := _type.bi.nameOfRuntimeType[_type.Addr]; ok {
|
|
||||||
return e.typename, e.kind, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var tflag int64
|
|
||||||
|
|
||||||
if tflagField := _type.loadFieldNamed("tflag"); tflagField != nil && tflagField.Value != nil {
|
|
||||||
tflag, _ = constant.Int64Val(tflagField.Value)
|
|
||||||
}
|
|
||||||
if kindField := _type.loadFieldNamed("kind"); kindField != nil && kindField.Value != nil {
|
|
||||||
kind, _ = constant.Int64Val(kindField.Value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Named types are defined by a 'type' expression, everything else
|
|
||||||
// (for example pointers to named types) are not considered named.
|
|
||||||
if tflag&tflagNamed != 0 {
|
|
||||||
typename, err = nameOfNamedRuntimeType(mds, _type, kind, tflag)
|
|
||||||
if err == nil {
|
|
||||||
_type.bi.nameOfRuntimeType[_type.Addr] = nameOfRuntimeTypeEntry{typename: typename, kind: kind}
|
|
||||||
}
|
|
||||||
return typename, kind, err
|
|
||||||
}
|
|
||||||
|
|
||||||
typename, err = nameOfUnnamedRuntimeType(mds, _type, kind, tflag)
|
|
||||||
if err == nil {
|
|
||||||
_type.bi.nameOfRuntimeType[_type.Addr] = nameOfRuntimeTypeEntry{typename: typename, kind: kind}
|
|
||||||
}
|
|
||||||
return typename, kind, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// The layout of a runtime._type struct is as follows:
|
|
||||||
//
|
|
||||||
// <runtime._type><kind specific struct fields><runtime.uncommontype>
|
|
||||||
//
|
|
||||||
// with the 'uncommon type struct' being optional
|
|
||||||
//
|
|
||||||
// For named types first we extract the type name from the 'str'
|
|
||||||
// field in the runtime._type struct.
|
|
||||||
// Then we prepend the package path from the runtime.uncommontype
|
|
||||||
// struct, when it exists.
|
|
||||||
//
|
|
||||||
// To find out the memory address of the runtime.uncommontype struct
|
|
||||||
// we first cast the Variable pointing to the runtime._type struct
|
|
||||||
// to a struct specific to the type's kind (for example, if the type
|
|
||||||
// being described is a slice type the variable will be specialized
|
|
||||||
// to a runtime.slicetype).
|
|
||||||
func nameOfNamedRuntimeType(mds []moduleData, _type *Variable, kind, tflag int64) (typename string, err error) {
|
|
||||||
var strOff int64
|
|
||||||
if strField := _type.loadFieldNamed("str"); strField != nil && strField.Value != nil {
|
|
||||||
strOff, _ = constant.Int64Val(strField.Value)
|
|
||||||
} else {
|
|
||||||
return "", errors.New("could not find str field")
|
|
||||||
}
|
|
||||||
|
|
||||||
// The following code is adapted from reflect.(*rtype).Name.
|
|
||||||
// For a description of how memory is organized for type names read
|
|
||||||
// the comment to 'type name struct' in $GOROOT/src/reflect/type.go
|
|
||||||
|
|
||||||
typename, _, _, err = resolveNameOff(_type.bi, mds, _type.Addr, uint64(strOff), _type.mem)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if tflag&tflagExtraStar != 0 {
|
|
||||||
typename = typename[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
if i := strings.Index(typename, "."); i >= 0 {
|
|
||||||
typename = typename[i+1:]
|
|
||||||
} else {
|
|
||||||
return typename, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// The following code is adapted from reflect.(*rtype).PkgPath in
|
|
||||||
// $GOROOT/src/reflect/type.go
|
|
||||||
|
|
||||||
_type, err = specificRuntimeType(_type, kind)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
if ut := uncommon(_type, tflag); ut != nil {
|
|
||||||
if pkgPathField := ut.loadFieldNamed("pkgpath"); pkgPathField != nil && pkgPathField.Value != nil {
|
|
||||||
pkgPathOff, _ := constant.Int64Val(pkgPathField.Value)
|
|
||||||
pkgPath, _, _, err := resolveNameOff(_type.bi, mds, _type.Addr, uint64(pkgPathOff), _type.mem)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
if slash := strings.LastIndex(pkgPath, "/"); slash >= 0 {
|
|
||||||
fixedName := strings.Replace(pkgPath[slash+1:], ".", "%2e", -1)
|
|
||||||
if fixedName != pkgPath[slash+1:] {
|
|
||||||
pkgPath = pkgPath[:slash+1] + fixedName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
typename = pkgPath + "." + typename
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return typename, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func nameOfUnnamedRuntimeType(mds []moduleData, _type *Variable, kind, tflag int64) (string, error) {
|
|
||||||
_type, err := specificRuntimeType(_type, kind)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
// The types referred to here are defined in $GOROOT/src/runtime/type.go
|
|
||||||
switch reflect.Kind(kind & kindMask) {
|
|
||||||
case reflect.Array:
|
|
||||||
var len int64
|
|
||||||
if lenField := _type.loadFieldNamed("len"); lenField != nil && lenField.Value != nil {
|
|
||||||
len, _ = constant.Int64Val(lenField.Value)
|
|
||||||
}
|
|
||||||
elemname, err := fieldToType(mds, _type, "elem")
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("[%d]%s", len, elemname), nil
|
|
||||||
case reflect.Chan:
|
|
||||||
elemname, err := fieldToType(mds, _type, "elem")
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return "chan " + elemname, nil
|
|
||||||
case reflect.Func:
|
|
||||||
return nameOfFuncRuntimeType(mds, _type, tflag, true)
|
|
||||||
case reflect.Interface:
|
|
||||||
return nameOfInterfaceRuntimeType(mds, _type, kind, tflag)
|
|
||||||
case reflect.Map:
|
|
||||||
keyname, err := fieldToType(mds, _type, "key")
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
elemname, err := fieldToType(mds, _type, "elem")
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return "map[" + keyname + "]" + elemname, nil
|
|
||||||
case reflect.Ptr:
|
|
||||||
elemname, err := fieldToType(mds, _type, "elem")
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return "*" + elemname, nil
|
|
||||||
case reflect.Slice:
|
|
||||||
elemname, err := fieldToType(mds, _type, "elem")
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return "[]" + elemname, nil
|
|
||||||
case reflect.Struct:
|
|
||||||
return nameOfStructRuntimeType(mds, _type, kind, tflag)
|
|
||||||
default:
|
|
||||||
return nameOfNamedRuntimeType(mds, _type, kind, tflag)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the expression describing an anonymous function type.
|
|
||||||
// A runtime.functype is followed by a runtime.uncommontype
|
|
||||||
// (optional) and then by an array of pointers to runtime._type,
|
|
||||||
// one for each input and output argument.
|
|
||||||
func nameOfFuncRuntimeType(mds []moduleData, _type *Variable, tflag int64, anonymous bool) (string, error) {
|
|
||||||
rtyp, err := _type.bi.findType("runtime._type")
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
prtyp := pointerTo(rtyp, _type.bi.Arch)
|
|
||||||
|
|
||||||
uadd := _type.RealType.Common().ByteSize
|
|
||||||
if ut := uncommon(_type, tflag); ut != nil {
|
|
||||||
uadd += ut.RealType.Common().ByteSize
|
|
||||||
}
|
|
||||||
|
|
||||||
var inCount, outCount int64
|
|
||||||
if inCountField := _type.loadFieldNamed("inCount"); inCountField != nil && inCountField.Value != nil {
|
|
||||||
inCount, _ = constant.Int64Val(inCountField.Value)
|
|
||||||
}
|
|
||||||
if outCountField := _type.loadFieldNamed("outCount"); outCountField != nil && outCountField.Value != nil {
|
|
||||||
outCount, _ = constant.Int64Val(outCountField.Value)
|
|
||||||
// only the lowest 15 bits of outCount are used, rest are flags
|
|
||||||
outCount = outCount & (1<<15 - 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
cursortyp := _type.newVariable("", _type.Addr+uint64(uadd), prtyp, _type.mem)
|
|
||||||
var buf bytes.Buffer
|
|
||||||
if anonymous {
|
|
||||||
buf.WriteString("func(")
|
|
||||||
} else {
|
|
||||||
buf.WriteString("(")
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := int64(0); i < inCount; i++ {
|
|
||||||
argtype := cursortyp.maybeDereference()
|
|
||||||
cursortyp.Addr += uint64(_type.bi.Arch.PtrSize())
|
|
||||||
argtypename, _, err := nameOfRuntimeType(mds, argtype)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
buf.WriteString(argtypename)
|
|
||||||
if i != inCount-1 {
|
|
||||||
buf.WriteString(", ")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buf.WriteString(")")
|
|
||||||
|
|
||||||
switch outCount {
|
|
||||||
case 0:
|
|
||||||
// nothing to do
|
|
||||||
case 1:
|
|
||||||
buf.WriteString(" ")
|
|
||||||
argtype := cursortyp.maybeDereference()
|
|
||||||
argtypename, _, err := nameOfRuntimeType(mds, argtype)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
buf.WriteString(argtypename)
|
|
||||||
default:
|
|
||||||
buf.WriteString(" (")
|
|
||||||
for i := int64(0); i < outCount; i++ {
|
|
||||||
argtype := cursortyp.maybeDereference()
|
|
||||||
cursortyp.Addr += uint64(_type.bi.Arch.PtrSize())
|
|
||||||
argtypename, _, err := nameOfRuntimeType(mds, argtype)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
buf.WriteString(argtypename)
|
|
||||||
if i != inCount-1 {
|
|
||||||
buf.WriteString(", ")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
buf.WriteString(")")
|
|
||||||
}
|
|
||||||
return buf.String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func nameOfInterfaceRuntimeType(mds []moduleData, _type *Variable, kind, tflag int64) (string, error) {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
buf.WriteString("interface {")
|
|
||||||
|
|
||||||
methods, _ := _type.structMember(interfacetypeFieldMhdr)
|
|
||||||
methods.loadArrayValues(0, LoadConfig{false, 1, 0, 4096, -1, 0})
|
|
||||||
if methods.Unreadable != nil {
|
|
||||||
return "", nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(methods.Children) == 0 {
|
|
||||||
buf.WriteString("}")
|
|
||||||
return buf.String(), nil
|
|
||||||
}
|
|
||||||
buf.WriteString(" ")
|
|
||||||
|
|
||||||
for i, im := range methods.Children {
|
|
||||||
var methodname, methodtype string
|
|
||||||
for i := range im.Children {
|
|
||||||
switch im.Children[i].Name {
|
|
||||||
case imethodFieldName:
|
|
||||||
nameoff, _ := constant.Int64Val(im.Children[i].Value)
|
|
||||||
var err error
|
|
||||||
methodname, _, _, err = resolveNameOff(_type.bi, mds, _type.Addr, uint64(nameoff), _type.mem)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
case imethodFieldItyp:
|
|
||||||
typeoff, _ := constant.Int64Val(im.Children[i].Value)
|
|
||||||
typ, err := resolveTypeOff(_type.bi, mds, _type.Addr, uint64(typeoff), _type.mem)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
typ, err = specificRuntimeType(typ, int64(reflect.Func))
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
var tflag int64
|
|
||||||
if tflagField := typ.loadFieldNamed("tflag"); tflagField != nil && tflagField.Value != nil {
|
|
||||||
tflag, _ = constant.Int64Val(tflagField.Value)
|
|
||||||
}
|
|
||||||
methodtype, err = nameOfFuncRuntimeType(mds, typ, tflag, false)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
buf.WriteString(methodname)
|
|
||||||
buf.WriteString(methodtype)
|
|
||||||
|
|
||||||
if i != len(methods.Children)-1 {
|
|
||||||
buf.WriteString("; ")
|
|
||||||
} else {
|
|
||||||
buf.WriteString(" }")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return buf.String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func nameOfStructRuntimeType(mds []moduleData, _type *Variable, kind, tflag int64) (string, error) {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
buf.WriteString("struct {")
|
|
||||||
|
|
||||||
fields, _ := _type.structMember("fields")
|
|
||||||
fields.loadArrayValues(0, LoadConfig{false, 2, 0, 4096, -1, 0})
|
|
||||||
if fields.Unreadable != nil {
|
|
||||||
return "", fields.Unreadable
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(fields.Children) == 0 {
|
|
||||||
buf.WriteString("}")
|
|
||||||
return buf.String(), nil
|
|
||||||
}
|
|
||||||
buf.WriteString(" ")
|
|
||||||
|
|
||||||
for i, field := range fields.Children {
|
|
||||||
var fieldname, fieldtypename string
|
|
||||||
var typeField *Variable
|
|
||||||
isembed := false
|
|
||||||
for i := range field.Children {
|
|
||||||
switch field.Children[i].Name {
|
|
||||||
case "name":
|
|
||||||
var nameoff int64
|
|
||||||
switch field.Children[i].Kind {
|
|
||||||
case reflect.Struct:
|
|
||||||
nameoff = int64(field.Children[i].fieldVariable("bytes").Children[0].Addr)
|
|
||||||
default:
|
|
||||||
nameoff, _ = constant.Int64Val(field.Children[i].Value)
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
fieldname, _, _, err = loadName(_type.bi, uint64(nameoff), _type.mem)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
case "typ":
|
|
||||||
typeField = field.Children[i].maybeDereference()
|
|
||||||
var err error
|
|
||||||
fieldtypename, _, err = nameOfRuntimeType(mds, typeField)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
case "offsetAnon":
|
|
||||||
// The offsetAnon field of runtime.structfield combines the offset of
|
|
||||||
// the struct field from the base address of the struct with a flag
|
|
||||||
// determining whether the field is anonymous (i.e. an embedded struct).
|
|
||||||
//
|
|
||||||
// offsetAnon = (offset<<1) | (anonFlag)
|
|
||||||
//
|
|
||||||
// Here we are only interested in the anonymous flag.
|
|
||||||
offsetAnon, _ := constant.Int64Val(field.Children[i].Value)
|
|
||||||
isembed = offsetAnon%2 != 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// fieldname will be the empty string for anonymous fields
|
|
||||||
if fieldname != "" && !isembed {
|
|
||||||
buf.WriteString(fieldname)
|
|
||||||
buf.WriteString(" ")
|
|
||||||
}
|
|
||||||
buf.WriteString(fieldtypename)
|
|
||||||
if i != len(fields.Children)-1 {
|
|
||||||
buf.WriteString("; ")
|
|
||||||
} else {
|
|
||||||
buf.WriteString(" }")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf.String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func fieldToType(mds []moduleData, _type *Variable, fieldName string) (string, error) {
|
|
||||||
typeField, err := _type.structMember(fieldName)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
typeField = typeField.maybeDereference()
|
|
||||||
typename, _, err := nameOfRuntimeType(mds, typeField)
|
|
||||||
return typename, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func specificRuntimeType(_type *Variable, kind int64) (*Variable, error) {
|
|
||||||
typ, err := typeForKind(kind, _type.bi)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if typ == nil {
|
|
||||||
return _type, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return _type.newVariable(_type.Name, _type.Addr, typ, _type.mem), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// See reflect.(*rtype).uncommon in $GOROOT/src/reflect/type.go
|
|
||||||
func uncommon(_type *Variable, tflag int64) *Variable {
|
|
||||||
if tflag&tflagUncommon == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
typ, err := _type.bi.findType("runtime.uncommontype")
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return _type.newVariable(_type.Name, _type.Addr+uint64(_type.RealType.Size()), typ, _type.mem)
|
|
||||||
}
|
|
||||||
|
|
||||||
var kindToRuntimeTypeName = map[reflect.Kind]string{
|
|
||||||
reflect.Array: "runtime.arraytype",
|
|
||||||
reflect.Chan: "runtime.chantype",
|
|
||||||
reflect.Func: "runtime.functype",
|
|
||||||
reflect.Interface: "runtime.interfacetype",
|
|
||||||
reflect.Map: "runtime.maptype",
|
|
||||||
reflect.Ptr: "runtime.ptrtype",
|
|
||||||
reflect.Slice: "runtime.slicetype",
|
|
||||||
reflect.Struct: "runtime.structtype",
|
|
||||||
}
|
|
||||||
|
|
||||||
// typeForKind returns a *dwarf.StructType describing the specialization of
|
|
||||||
// runtime._type for the specified type kind. For example if kind is
|
|
||||||
// reflect.ArrayType it will return runtime.arraytype
|
|
||||||
func typeForKind(kind int64, bi *BinaryInfo) (*godwarf.StructType, error) {
|
|
||||||
typename, ok := kindToRuntimeTypeName[reflect.Kind(kind&kindMask)]
|
|
||||||
if !ok {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
typ, err := bi.findType(typename)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
typ = resolveTypedef(typ)
|
|
||||||
return typ.(*godwarf.StructType), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func dwarfToRuntimeType(bi *BinaryInfo, mem MemoryReadWriter, typ godwarf.Type) (typeAddr uint64, typeKind uint64, found bool, err error) {
|
func dwarfToRuntimeType(bi *BinaryInfo, mem MemoryReadWriter, typ godwarf.Type) (typeAddr uint64, typeKind uint64, found bool, err error) {
|
||||||
so := bi.typeToImage(typ)
|
so := bi.typeToImage(typ)
|
||||||
rdr := so.DwarfReader()
|
rdr := so.DwarfReader()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user