delve/pkg/proc/moduledata.go
Alessandro Arzilli 151de14d08 proc: support DW_AT_go_package_name (#1757)
Use the name specified by compile unit attribute DW_AT_go_package_name,
introduced in Go 1.13, to map package names to package paths, instead of
trying to deduce it from names of types.
Also use this mapping for resolving global variables and function
expressions.
2019-11-25 09:10:18 -08:00

206 lines
4.7 KiB
Go

package proc
import (
"go/constant"
"unsafe"
)
// delve counterpart to runtime.moduledata
type moduleData struct {
text, etext uintptr
types, etypes uintptr
typemapVar *Variable
}
func loadModuleData(bi *BinaryInfo, mem MemoryReadWriter) ([]moduleData, error) {
scope := globalScope(bi, bi.Images[0], mem)
var md *Variable
md, err := scope.findGlobal("runtime", "firstmoduledata")
if err != nil {
return nil, err
}
r := []moduleData{}
for md.Addr != 0 {
const (
typesField = "types"
etypesField = "etypes"
textField = "text"
etextField = "etext"
nextField = "next"
typemapField = "typemap"
)
vars := map[string]*Variable{}
for _, fieldName := range []string{typesField, etypesField, textField, etextField, nextField, typemapField} {
var err error
vars[fieldName], err = md.structMember(fieldName)
if err != nil {
return nil, err
}
}
var err error
touint := func(name string) (ret uintptr) {
if err == nil {
var n uint64
n, err = vars[name].asUint()
ret = uintptr(n)
}
return ret
}
r = append(r, moduleData{
types: touint(typesField), etypes: touint(etypesField),
text: touint(textField), etext: touint(etextField),
typemapVar: vars[typemapField],
})
if err != nil {
return nil, err
}
md = vars[nextField].maybeDereference()
if md.Unreadable != nil {
return nil, md.Unreadable
}
}
return r, nil
}
func findModuleDataForType(bi *BinaryInfo, mds []moduleData, typeAddr uintptr, mem MemoryReadWriter) *moduleData {
for i := range mds {
if typeAddr >= mds[i].types && typeAddr < mds[i].etypes {
return &mds[i]
}
}
return nil
}
func resolveTypeOff(bi *BinaryInfo, mds []moduleData, typeAddr uintptr, off uintptr, 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, uintptr(addr), rtyp, mem), nil
}
if t, _ := md.typemapVar.mapAccess(newConstant(constant.MakeUint64(uint64(off)), mem)); t != nil {
return t, nil
}
res := md.types + uintptr(off)
return newVariable("", res, rtyp, bi, mem), nil
}
func resolveNameOff(bi *BinaryInfo, mds []moduleData, typeAddr uintptr, off uintptr, 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 uintptr, mem MemoryReadWriter) (*Variable, error) {
scope := globalScope(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 uintptr, 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 += uintptr(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 += uintptr(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
}