proc: support debugging plugins (#1414)
This change splits the BinaryInfo object into a slice of Image objects containing information about the base executable and each loaded shared library (note: go plugins are shared libraries). Delve backens are supposed to call BinaryInfo.AddImage whenever they detect that a new shared library has been loaded. Member fields of BinaryInfo that are used to speed up access to dwarf (Functions, packageVars, consts, etc...) remain part of BinaryInfo and are updated to reference the correct image object. This simplifies this change. This approach has a few shortcomings: 1. Multiple shared libraries can define functions or globals with the same name and we have no way to disambiguate between them. 2. We don't have a way to handle library unloading. Both of those affect C shared libraries much more than they affect go plugins. Go plugins can't be unloaded at all and a lot of name collisions are prevented by import paths. There's only one problem that is concerning: if two plugins both import the same package they will end up with multiple definition for the same function. For example if two plugins use fmt.Printf the final in-memory image (and therefore our BinaryInfo object) will end up with two copies of fmt.Printf at different memory addresses. If a user types break fmt.Printf a breakpoint should be created at *both* locations. Allowing this is a relatively complex change that should be done in a different PR than this. For this reason I consider this approach an acceptable and sustainable stopgap. Updates #865
This commit is contained in:
parent
71a7fe04d9
commit
f3b149bda7
@ -75,3 +75,12 @@ func (fdes FrameDescriptionEntries) FDEForPC(pc uint64) (*FrameDescriptionEntry,
|
|||||||
}
|
}
|
||||||
return fdes[idx], nil
|
return fdes[idx], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Append appends otherFDEs to fdes and returns the result.
|
||||||
|
func (fdes FrameDescriptionEntries) Append(otherFDEs FrameDescriptionEntries) FrameDescriptionEntries {
|
||||||
|
r := append(fdes, otherFDEs...)
|
||||||
|
sort.Slice(r, func(i, j int) bool {
|
||||||
|
return r[i].Begin() < r[j].Begin()
|
||||||
|
})
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
@ -71,6 +71,7 @@ type Type interface {
|
|||||||
// If a field is not known or not applicable for a given type,
|
// If a field is not known or not applicable for a given type,
|
||||||
// the zero value is used.
|
// the zero value is used.
|
||||||
type CommonType struct {
|
type CommonType struct {
|
||||||
|
Index int // index supplied by caller of ReadType
|
||||||
ByteSize int64 // size of value of this type, in bytes
|
ByteSize int64 // size of value of this type, in bytes
|
||||||
Name string // name that can be used to refer to type
|
Name string // name that can be used to refer to type
|
||||||
ReflectKind reflect.Kind // the reflect kind of the type.
|
ReflectKind reflect.Kind // the reflect kind of the type.
|
||||||
@ -478,8 +479,12 @@ func (t *ChanType) stringIntl(recCheck recCheck) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Type reads the type at off in the DWARF ``info'' section.
|
// Type reads the type at off in the DWARF ``info'' section.
|
||||||
func ReadType(d *dwarf.Data, off dwarf.Offset, typeCache map[dwarf.Offset]Type) (Type, error) {
|
func ReadType(d *dwarf.Data, index int, off dwarf.Offset, typeCache map[dwarf.Offset]Type) (Type, error) {
|
||||||
return readType(d, "info", d.Reader(), off, typeCache)
|
typ, err := readType(d, "info", d.Reader(), off, typeCache)
|
||||||
|
if typ != nil {
|
||||||
|
typ.Common().Index = index
|
||||||
|
}
|
||||||
|
return typ, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func getKind(e *dwarf.Entry) reflect.Kind {
|
func getKind(e *dwarf.Entry) reflect.Kind {
|
||||||
|
@ -17,7 +17,7 @@ type Arch interface {
|
|||||||
DerefTLS() bool
|
DerefTLS() bool
|
||||||
FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *BinaryInfo) *frame.FrameContext
|
FixFrameUnwindContext(fctxt *frame.FrameContext, pc uint64, bi *BinaryInfo) *frame.FrameContext
|
||||||
RegSize(uint64) int
|
RegSize(uint64) int
|
||||||
RegistersToDwarfRegisters(regs Registers, staticBase uint64) op.DwarfRegisters
|
RegistersToDwarfRegisters(bi *BinaryInfo, regs Registers) op.DwarfRegisters
|
||||||
GoroutineToDwarfRegisters(*G) op.DwarfRegisters
|
GoroutineToDwarfRegisters(*G) op.DwarfRegisters
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,7 +270,7 @@ func maxAmd64DwarfRegister() int {
|
|||||||
|
|
||||||
// RegistersToDwarfRegisters converts hardware registers to the format used
|
// RegistersToDwarfRegisters converts hardware registers to the format used
|
||||||
// by the DWARF expression interpreter.
|
// by the DWARF expression interpreter.
|
||||||
func (a *AMD64) RegistersToDwarfRegisters(regs Registers, staticBase uint64) op.DwarfRegisters {
|
func (a *AMD64) RegistersToDwarfRegisters(bi *BinaryInfo, regs Registers) op.DwarfRegisters {
|
||||||
dregs := make([]*op.DwarfRegister, maxAmd64DwarfRegister()+1)
|
dregs := make([]*op.DwarfRegister, maxAmd64DwarfRegister()+1)
|
||||||
|
|
||||||
dregs[amd64DwarfIPRegNum] = op.DwarfRegisterFromUint64(regs.PC())
|
dregs[amd64DwarfIPRegNum] = op.DwarfRegisterFromUint64(regs.PC())
|
||||||
@ -292,7 +292,9 @@ func (a *AMD64) RegistersToDwarfRegisters(regs Registers, staticBase uint64) op.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return op.DwarfRegisters{StaticBase: staticBase, Regs: dregs, ByteOrder: binary.LittleEndian, PCRegNum: amd64DwarfIPRegNum, SPRegNum: amd64DwarfSPRegNum, BPRegNum: amd64DwarfBPRegNum}
|
so := bi.pcToImage(regs.PC())
|
||||||
|
|
||||||
|
return op.DwarfRegisters{StaticBase: so.StaticBase, Regs: dregs, ByteOrder: binary.LittleEndian, PCRegNum: amd64DwarfIPRegNum, SPRegNum: amd64DwarfSPRegNum, BPRegNum: amd64DwarfBPRegNum}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GoroutineToDwarfRegisters extract the saved DWARF registers from a parked
|
// GoroutineToDwarfRegisters extract the saved DWARF registers from a parked
|
||||||
@ -302,5 +304,8 @@ func (a *AMD64) GoroutineToDwarfRegisters(g *G) op.DwarfRegisters {
|
|||||||
dregs[amd64DwarfIPRegNum] = op.DwarfRegisterFromUint64(g.PC)
|
dregs[amd64DwarfIPRegNum] = op.DwarfRegisterFromUint64(g.PC)
|
||||||
dregs[amd64DwarfSPRegNum] = op.DwarfRegisterFromUint64(g.SP)
|
dregs[amd64DwarfSPRegNum] = op.DwarfRegisterFromUint64(g.SP)
|
||||||
dregs[amd64DwarfBPRegNum] = op.DwarfRegisterFromUint64(g.BP)
|
dregs[amd64DwarfBPRegNum] = op.DwarfRegisterFromUint64(g.BP)
|
||||||
return op.DwarfRegisters{StaticBase: g.variable.bi.staticBase, Regs: dregs, ByteOrder: binary.LittleEndian, PCRegNum: amd64DwarfIPRegNum, SPRegNum: amd64DwarfSPRegNum, BPRegNum: amd64DwarfBPRegNum}
|
|
||||||
|
so := g.variable.bi.pcToImage(g.PC)
|
||||||
|
|
||||||
|
return op.DwarfRegisters{StaticBase: so.StaticBase, Regs: dregs, ByteOrder: binary.LittleEndian, PCRegNum: amd64DwarfIPRegNum, SPRegNum: amd64DwarfSPRegNum, BPRegNum: amd64DwarfBPRegNum}
|
||||||
}
|
}
|
||||||
|
@ -29,14 +29,14 @@ import (
|
|||||||
// BinaryInfo holds information on the binaries being executed (this
|
// BinaryInfo holds information on the binaries being executed (this
|
||||||
// includes both the executable and also any loaded libraries).
|
// includes both the executable and also any loaded libraries).
|
||||||
type BinaryInfo struct {
|
type BinaryInfo struct {
|
||||||
// Path on disk of the binary being executed.
|
|
||||||
Path string
|
|
||||||
// Architecture of this binary.
|
// Architecture of this binary.
|
||||||
Arch Arch
|
Arch Arch
|
||||||
|
|
||||||
// GOOS operating system this binary is executing on.
|
// GOOS operating system this binary is executing on.
|
||||||
GOOS string
|
GOOS string
|
||||||
|
|
||||||
|
debugInfoDirectories []string
|
||||||
|
|
||||||
// Functions is a list of all DW_TAG_subprogram entries in debug_info, sorted by entry point
|
// Functions is a list of all DW_TAG_subprogram entries in debug_info, sorted by entry point
|
||||||
Functions []Function
|
Functions []Function
|
||||||
// Sources is a list of all source files found in debug_line.
|
// Sources is a list of all source files found in debug_line.
|
||||||
@ -60,32 +60,25 @@ type BinaryInfo struct {
|
|||||||
// Maps package names to package paths, needed to lookup types inside DWARF info
|
// Maps package names to package paths, needed to lookup types inside DWARF info
|
||||||
packageMap map[string]string
|
packageMap map[string]string
|
||||||
|
|
||||||
dwarf *dwarf.Data
|
|
||||||
dwarfReader *dwarf.Reader
|
|
||||||
frameEntries frame.FrameDescriptionEntries
|
frameEntries frame.FrameDescriptionEntries
|
||||||
loclist loclistReader
|
|
||||||
compileUnits []*compileUnit
|
compileUnits []*compileUnit
|
||||||
types map[string]dwarf.Offset
|
types map[string]dwarfRef
|
||||||
packageVars []packageVar // packageVars is a list of all global/package variables in debug_info, sorted by address
|
packageVars []packageVar // packageVars is a list of all global/package variables in debug_info, sorted by address
|
||||||
typeCache map[dwarf.Offset]godwarf.Type
|
|
||||||
|
|
||||||
gStructOffset uint64
|
gStructOffset uint64
|
||||||
|
|
||||||
loadModuleDataOnce sync.Once
|
// nameOfRuntimeType maps an address of a runtime._type struct to its
|
||||||
moduleData []moduleData
|
// decoded name. Used with versions of Go <= 1.10 to figure out the DIE of
|
||||||
nameOfRuntimeType map[uintptr]nameOfRuntimeTypeEntry
|
// the concrete type of interfaces.
|
||||||
|
nameOfRuntimeType map[uintptr]nameOfRuntimeTypeEntry
|
||||||
// runtimeTypeToDIE maps between the offset of a runtime._type in
|
|
||||||
// runtime.moduledata.types and the offset of the DIE in debug_info. This
|
|
||||||
// map is filled by using the extended attribute godwarf.AttrGoRuntimeType
|
|
||||||
// which was added in go 1.11.
|
|
||||||
runtimeTypeToDIE map[uint64]runtimeTypeDIE
|
|
||||||
|
|
||||||
// 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
|
||||||
|
|
||||||
loadErrMu sync.Mutex
|
loadErrMu sync.Mutex
|
||||||
loadErr error
|
loadErr error
|
||||||
|
|
||||||
|
initialized bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrUnsupportedLinuxArch is returned when attempting to debug a binary compiled for an unsupported architecture.
|
// ErrUnsupportedLinuxArch is returned when attempting to debug a binary compiled for an unsupported architecture.
|
||||||
@ -120,6 +113,14 @@ type compileUnit struct {
|
|||||||
producer string // producer attribute
|
producer string // producer attribute
|
||||||
|
|
||||||
startOffset, endOffset dwarf.Offset // interval of offsets contained in this compile unit
|
startOffset, endOffset dwarf.Offset // interval of offsets contained in this compile unit
|
||||||
|
|
||||||
|
image *Image // parent image of this compilation unit.
|
||||||
|
}
|
||||||
|
|
||||||
|
// dwarfRef is a reference to a Debug Info Entry inside a shared object.
|
||||||
|
type dwarfRef struct {
|
||||||
|
imageIndex int
|
||||||
|
offset dwarf.Offset
|
||||||
}
|
}
|
||||||
|
|
||||||
type partialUnitConstant struct {
|
type partialUnitConstant struct {
|
||||||
@ -203,7 +204,7 @@ func (fn *Function) Optimized() bool {
|
|||||||
return fn.cu.optimized
|
return fn.cu.optimized
|
||||||
}
|
}
|
||||||
|
|
||||||
type constantsMap map[dwarf.Offset]*constantType
|
type constantsMap map[dwarfRef]*constantType
|
||||||
|
|
||||||
type constantType struct {
|
type constantType struct {
|
||||||
initialized bool
|
initialized bool
|
||||||
@ -222,6 +223,7 @@ type constantValue struct {
|
|||||||
// a register, or non-contiguously) addr will be 0.
|
// a register, or non-contiguously) addr will be 0.
|
||||||
type packageVar struct {
|
type packageVar struct {
|
||||||
name string
|
name string
|
||||||
|
cu *compileUnit
|
||||||
offset dwarf.Offset
|
offset dwarf.Offset
|
||||||
addr uint64
|
addr uint64
|
||||||
}
|
}
|
||||||
@ -304,7 +306,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[uintptr]nameOfRuntimeTypeEntry), typeCache: make(map[dwarf.Offset]godwarf.Type)}
|
r := &BinaryInfo{GOOS: goos, nameOfRuntimeType: make(map[uintptr]nameOfRuntimeTypeEntry)}
|
||||||
|
|
||||||
// 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 {
|
||||||
@ -316,24 +318,28 @@ func NewBinaryInfo(goos, goarch string) *BinaryInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// LoadBinaryInfo will load and store the information from the binary at 'path'.
|
// LoadBinaryInfo will load and store the information from the binary at 'path'.
|
||||||
// It is expected this will be called in parallel with other initialization steps
|
|
||||||
// so a sync.WaitGroup must be provided.
|
|
||||||
func (bi *BinaryInfo) LoadBinaryInfo(path string, entryPoint uint64, debugInfoDirs []string) error {
|
func (bi *BinaryInfo) LoadBinaryInfo(path string, entryPoint uint64, debugInfoDirs []string) error {
|
||||||
fi, err := os.Stat(path)
|
fi, err := os.Stat(path)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
bi.lastModified = fi.ModTime()
|
bi.lastModified = fi.ModTime()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bi.debugInfoDirectories = debugInfoDirs
|
||||||
|
|
||||||
|
return bi.AddImage(path, entryPoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadBinaryInfo(bi *BinaryInfo, image *Image, path string, entryPoint uint64) error {
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
defer wg.Wait()
|
defer wg.Wait()
|
||||||
bi.Path = path
|
|
||||||
switch bi.GOOS {
|
switch bi.GOOS {
|
||||||
case "linux":
|
case "linux":
|
||||||
return bi.LoadBinaryInfoElf(path, entryPoint, debugInfoDirs, &wg)
|
return loadBinaryInfoElf(bi, image, path, entryPoint, &wg)
|
||||||
case "windows":
|
case "windows":
|
||||||
return bi.LoadBinaryInfoPE(path, entryPoint, &wg)
|
return loadBinaryInfoPE(bi, image, path, entryPoint, &wg)
|
||||||
case "darwin":
|
case "darwin":
|
||||||
return bi.LoadBinaryInfoMacho(path, entryPoint, &wg)
|
return loadBinaryInfoMacho(bi, image, path, entryPoint, &wg)
|
||||||
}
|
}
|
||||||
return errors.New("unsupported operating system")
|
return errors.New("unsupported operating system")
|
||||||
}
|
}
|
||||||
@ -350,8 +356,8 @@ func (bi *BinaryInfo) LastModified() time.Time {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DwarfReader returns a reader for the dwarf data
|
// DwarfReader returns a reader for the dwarf data
|
||||||
func (bi *BinaryInfo) DwarfReader() *reader.Reader {
|
func (so *Image) DwarfReader() *reader.Reader {
|
||||||
return reader.New(bi.dwarf)
|
return reader.New(so.dwarf)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Types returns list of types present in the debugged program.
|
// Types returns list of types present in the debugged program.
|
||||||
@ -425,71 +431,168 @@ func (bi *BinaryInfo) PCToFunc(pc uint64) *Function {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Image represents a loaded library file (shared object on linux, DLL on windows).
|
// pcToImage returns the image containing the given PC address.
|
||||||
type Image struct {
|
func (bi *BinaryInfo) pcToImage(pc uint64) *Image {
|
||||||
Path string
|
fn := bi.PCToFunc(pc)
|
||||||
addr uint64
|
return bi.funcToImage(fn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddImage adds the specified image to bi.
|
// Image represents a loaded library file (shared object on linux, DLL on windows).
|
||||||
func (bi *BinaryInfo) AddImage(path string, addr uint64) {
|
type Image struct {
|
||||||
if !strings.HasPrefix(path, "/") {
|
Path string
|
||||||
return
|
StaticBase uint64
|
||||||
|
addr uint64
|
||||||
|
|
||||||
|
index int // index of this object in BinaryInfo.SharedObjects
|
||||||
|
|
||||||
|
closer io.Closer
|
||||||
|
sepDebugCloser io.Closer
|
||||||
|
|
||||||
|
dwarf *dwarf.Data
|
||||||
|
dwarfReader *dwarf.Reader
|
||||||
|
loclist loclistReader
|
||||||
|
|
||||||
|
typeCache map[dwarf.Offset]godwarf.Type
|
||||||
|
|
||||||
|
// runtimeTypeToDIE maps between the offset of a runtime._type in
|
||||||
|
// runtime.moduledata.types and the offset of the DIE in debug_info. This
|
||||||
|
// map is filled by using the extended attribute godwarf.AttrGoRuntimeType
|
||||||
|
// which was added in go 1.11.
|
||||||
|
runtimeTypeToDIE map[uint64]runtimeTypeDIE
|
||||||
|
|
||||||
|
loadErrMu sync.Mutex
|
||||||
|
loadErr error
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddImage adds the specified image to bi, loading data asynchronously.
|
||||||
|
// Addr is the relocated entry point for the executable and staticBase (i.e.
|
||||||
|
// the relocation offset) for all other images.
|
||||||
|
// The first image added must be the executable file.
|
||||||
|
func (bi *BinaryInfo) AddImage(path string, addr uint64) error {
|
||||||
|
// Check if the image is already present.
|
||||||
|
if len(bi.Images) > 0 && !strings.HasPrefix(path, "/") {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
for _, image := range bi.Images {
|
for _, image := range bi.Images {
|
||||||
if image.Path == path && image.addr == addr {
|
if image.Path == path && image.addr == addr {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//TODO(aarzilli): actually load informations about the image here
|
|
||||||
bi.Images = append(bi.Images, &Image{Path: path, addr: addr})
|
// Actually add the image.
|
||||||
|
image := &Image{Path: path, addr: addr, typeCache: make(map[dwarf.Offset]godwarf.Type)}
|
||||||
|
// add Image regardless of error so that we don't attempt to re-add it every time we stop
|
||||||
|
image.index = len(bi.Images)
|
||||||
|
bi.Images = append(bi.Images, image)
|
||||||
|
err := loadBinaryInfo(bi, image, path, addr)
|
||||||
|
if err != nil {
|
||||||
|
bi.Images[len(bi.Images)-1].loadErr = err
|
||||||
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes all internal readers.
|
// moduleDataToImage finds the image corresponding to the given module data object.
|
||||||
func (bi *BinaryInfo) Close() error {
|
func (bi *BinaryInfo) moduleDataToImage(md *moduleData) *Image {
|
||||||
if bi.sepDebugCloser != nil {
|
return bi.funcToImage(bi.PCToFunc(uint64(md.text)))
|
||||||
bi.sepDebugCloser.Close()
|
}
|
||||||
}
|
|
||||||
if bi.closer != nil {
|
// imageToModuleData finds the module data in mds corresponding to the given image.
|
||||||
return bi.closer.Close()
|
func (bi *BinaryInfo) imageToModuleData(image *Image, mds []moduleData) *moduleData {
|
||||||
|
for _, md := range mds {
|
||||||
|
im2 := bi.moduleDataToImage(&md)
|
||||||
|
if im2.index == image.index {
|
||||||
|
return &md
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bi *BinaryInfo) setLoadError(fmtstr string, args ...interface{}) {
|
// typeToImage returns the image containing the give type.
|
||||||
bi.loadErrMu.Lock()
|
func (bi *BinaryInfo) typeToImage(typ godwarf.Type) *Image {
|
||||||
bi.loadErr = fmt.Errorf(fmtstr, args...)
|
return bi.Images[typ.Common().Index]
|
||||||
bi.loadErrMu.Unlock()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadError returns any internal load error.
|
var errBinaryInfoClose = errors.New("multiple errors closing executable files")
|
||||||
func (bi *BinaryInfo) LoadError() error {
|
|
||||||
return bi.loadErr
|
// Close closes all internal readers.
|
||||||
|
func (bi *BinaryInfo) Close() error {
|
||||||
|
var errs []error
|
||||||
|
for _, image := range bi.Images {
|
||||||
|
if err := image.Close(); err != nil {
|
||||||
|
errs = append(errs, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch len(errs) {
|
||||||
|
case 0:
|
||||||
|
return nil
|
||||||
|
case 1:
|
||||||
|
return errs[0]
|
||||||
|
default:
|
||||||
|
return errBinaryInfoClose
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (image *Image) Close() error {
|
||||||
|
var err1, err2 error
|
||||||
|
if image.sepDebugCloser != nil {
|
||||||
|
err := image.sepDebugCloser.Close()
|
||||||
|
if err != nil {
|
||||||
|
err1 = fmt.Errorf("closing shared object %q (split dwarf): %v", image.Path, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if image.closer != nil {
|
||||||
|
err := image.closer.Close()
|
||||||
|
if err != nil {
|
||||||
|
err2 = fmt.Errorf("closing shared object %q: %v", image.Path, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err1 != nil && err2 != nil {
|
||||||
|
return errBinaryInfoClose
|
||||||
|
}
|
||||||
|
if err1 != nil {
|
||||||
|
return err1
|
||||||
|
}
|
||||||
|
return err2
|
||||||
|
}
|
||||||
|
|
||||||
|
func (image *Image) setLoadError(fmtstr string, args ...interface{}) {
|
||||||
|
image.loadErrMu.Lock()
|
||||||
|
image.loadErr = fmt.Errorf(fmtstr, args...)
|
||||||
|
image.loadErrMu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadError returns any error incurred while loading this image.
|
||||||
|
func (image *Image) LoadError() error {
|
||||||
|
return image.loadErr
|
||||||
}
|
}
|
||||||
|
|
||||||
type nilCloser struct{}
|
type nilCloser struct{}
|
||||||
|
|
||||||
func (c *nilCloser) Close() error { return nil }
|
func (c *nilCloser) Close() error { return nil }
|
||||||
|
|
||||||
// LoadFromData creates a new BinaryInfo object using the specified data.
|
// LoadImageFromData creates a new Image, using the specified data, and adds it to bi.
|
||||||
// This is used for debugging BinaryInfo, you should use LoadBinary instead.
|
// This is used for debugging BinaryInfo, you should use LoadBinary instead.
|
||||||
func (bi *BinaryInfo) LoadFromData(dwdata *dwarf.Data, debugFrameBytes, debugLineBytes, debugLocBytes []byte) {
|
func (bi *BinaryInfo) LoadImageFromData(dwdata *dwarf.Data, debugFrameBytes, debugLineBytes, debugLocBytes []byte) {
|
||||||
bi.closer = (*nilCloser)(nil)
|
image := &Image{}
|
||||||
bi.sepDebugCloser = (*nilCloser)(nil)
|
image.closer = (*nilCloser)(nil)
|
||||||
bi.dwarf = dwdata
|
image.sepDebugCloser = (*nilCloser)(nil)
|
||||||
|
image.dwarf = dwdata
|
||||||
|
image.typeCache = make(map[dwarf.Offset]godwarf.Type)
|
||||||
|
|
||||||
if debugFrameBytes != nil {
|
if debugFrameBytes != nil {
|
||||||
bi.frameEntries = frame.Parse(debugFrameBytes, frame.DwarfEndian(debugFrameBytes), bi.staticBase)
|
bi.frameEntries = frame.Parse(debugFrameBytes, frame.DwarfEndian(debugFrameBytes), 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
bi.loclistInit(debugLocBytes)
|
image.loclistInit(debugLocBytes, bi.Arch.PtrSize())
|
||||||
|
|
||||||
bi.loadDebugInfoMaps(debugLineBytes, nil, nil)
|
bi.loadDebugInfoMaps(image, debugLineBytes, nil, nil)
|
||||||
|
|
||||||
|
bi.Images = append(bi.Images, image)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bi *BinaryInfo) loclistInit(data []byte) {
|
func (image *Image) loclistInit(data []byte, ptrSz int) {
|
||||||
bi.loclist.data = data
|
image.loclist.data = data
|
||||||
bi.loclist.ptrSz = bi.Arch.PtrSize()
|
image.loclist.ptrSz = ptrSz
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bi *BinaryInfo) locationExpr(entry reader.Entry, attr dwarf.Attr, pc uint64) ([]byte, string, error) {
|
func (bi *BinaryInfo) locationExpr(entry reader.Entry, attr dwarf.Attr, pc uint64) ([]byte, string, error) {
|
||||||
@ -507,9 +610,6 @@ func (bi *BinaryInfo) locationExpr(entry reader.Entry, attr dwarf.Attr, pc uint6
|
|||||||
if !ok {
|
if !ok {
|
||||||
return nil, "", fmt.Errorf("could not interpret location attribute %s", attr)
|
return nil, "", fmt.Errorf("could not interpret location attribute %s", attr)
|
||||||
}
|
}
|
||||||
if bi.loclist.data == nil {
|
|
||||||
return nil, "", fmt.Errorf("could not find loclist entry at %#x for address %#x (no debug_loc section found)", off, pc)
|
|
||||||
}
|
|
||||||
instr := bi.loclistEntry(off, pc)
|
instr := bi.loclistEntry(off, pc)
|
||||||
if instr == nil {
|
if instr == nil {
|
||||||
return nil, "", fmt.Errorf("could not find loclist entry at %#x for address %#x", off, pc)
|
return nil, "", fmt.Errorf("could not find loclist entry at %#x for address %#x", off, pc)
|
||||||
@ -537,13 +637,18 @@ func (bi *BinaryInfo) Location(entry reader.Entry, attr dwarf.Attr, pc uint64, r
|
|||||||
// for address pc.
|
// for address pc.
|
||||||
func (bi *BinaryInfo) loclistEntry(off int64, pc uint64) []byte {
|
func (bi *BinaryInfo) loclistEntry(off int64, pc uint64) []byte {
|
||||||
var base uint64
|
var base uint64
|
||||||
|
image := bi.Images[0]
|
||||||
if cu := bi.findCompileUnit(pc); cu != nil {
|
if cu := bi.findCompileUnit(pc); cu != nil {
|
||||||
base = cu.lowPC
|
base = cu.lowPC
|
||||||
|
image = cu.image
|
||||||
|
}
|
||||||
|
if image == nil || image.loclist.data == nil {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
bi.loclist.Seek(int(off))
|
image.loclist.Seek(int(off))
|
||||||
var e loclistEntry
|
var e loclistEntry
|
||||||
for bi.loclist.Next(&e) {
|
for image.loclist.Next(&e) {
|
||||||
if e.BaseAddressSelection() {
|
if e.BaseAddressSelection() {
|
||||||
base = e.highpc
|
base = e.highpc
|
||||||
continue
|
continue
|
||||||
@ -588,8 +693,17 @@ func (bi *BinaryInfo) Producer() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Type returns the Dwarf type entry at `offset`.
|
// Type returns the Dwarf type entry at `offset`.
|
||||||
func (bi *BinaryInfo) Type(offset dwarf.Offset) (godwarf.Type, error) {
|
func (image *Image) Type(offset dwarf.Offset) (godwarf.Type, error) {
|
||||||
return godwarf.ReadType(bi.dwarf, offset, bi.typeCache)
|
return godwarf.ReadType(image.dwarf, image.index, offset, image.typeCache)
|
||||||
|
}
|
||||||
|
|
||||||
|
// funcToImage returns the Image containing function fn, or the
|
||||||
|
// executable file as a fallback.
|
||||||
|
func (bi *BinaryInfo) funcToImage(fn *Function) *Image {
|
||||||
|
if fn == nil {
|
||||||
|
return bi.Images[0]
|
||||||
|
}
|
||||||
|
return fn.cu.image
|
||||||
}
|
}
|
||||||
|
|
||||||
// ELF ///////////////////////////////////////////////////////////////
|
// ELF ///////////////////////////////////////////////////////////////
|
||||||
@ -611,7 +725,7 @@ func (e *ErrNoBuildIDNote) Error() string {
|
|||||||
//
|
//
|
||||||
// Alternatively, if the debug file cannot be found be the build-id, Delve
|
// Alternatively, if the debug file cannot be found be the build-id, Delve
|
||||||
// will look in directories specified by the debug-info-directories config value.
|
// will look in directories specified by the debug-info-directories config value.
|
||||||
func (bi *BinaryInfo) openSeparateDebugInfo(exe *elf.File, debugInfoDirectories []string) (*os.File, *elf.File, error) {
|
func (bi *BinaryInfo) openSeparateDebugInfo(image *Image, exe *elf.File, debugInfoDirectories []string) (*os.File, *elf.File, error) {
|
||||||
var debugFilePath string
|
var debugFilePath string
|
||||||
for _, dir := range debugInfoDirectories {
|
for _, dir := range debugInfoDirectories {
|
||||||
var potentialDebugFilePath string
|
var potentialDebugFilePath string
|
||||||
@ -622,7 +736,7 @@ func (bi *BinaryInfo) openSeparateDebugInfo(exe *elf.File, debugInfoDirectories
|
|||||||
}
|
}
|
||||||
potentialDebugFilePath = fmt.Sprintf("%s/%s/%s.debug", dir, desc1, desc2)
|
potentialDebugFilePath = fmt.Sprintf("%s/%s/%s.debug", dir, desc1, desc2)
|
||||||
} else {
|
} else {
|
||||||
potentialDebugFilePath = fmt.Sprintf("%s/%s.debug", dir, filepath.Base(bi.Path))
|
potentialDebugFilePath = fmt.Sprintf("%s/%s.debug", dir, filepath.Base(image.Path))
|
||||||
}
|
}
|
||||||
_, err := os.Stat(potentialDebugFilePath)
|
_, err := os.Stat(potentialDebugFilePath)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -681,13 +795,13 @@ func parseBuildID(exe *elf.File) (string, string, error) {
|
|||||||
return desc[:2], desc[2:], nil
|
return desc[:2], desc[2:], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadBinaryInfoElf specifically loads information from an ELF binary.
|
// loadBinaryInfoElf specifically loads information from an ELF binary.
|
||||||
func (bi *BinaryInfo) LoadBinaryInfoElf(path string, entryPoint uint64, debugInfoDirectories []string, wg *sync.WaitGroup) error {
|
func loadBinaryInfoElf(bi *BinaryInfo, image *Image, path string, addr uint64, wg *sync.WaitGroup) error {
|
||||||
exe, err := os.OpenFile(path, 0, os.ModePerm)
|
exe, err := os.OpenFile(path, 0, os.ModePerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
bi.closer = exe
|
image.closer = exe
|
||||||
elfFile, err := elf.NewFile(exe)
|
elfFile, err := elf.NewFile(exe)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -696,70 +810,80 @@ func (bi *BinaryInfo) LoadBinaryInfoElf(path string, entryPoint uint64, debugInf
|
|||||||
return ErrUnsupportedLinuxArch
|
return ErrUnsupportedLinuxArch
|
||||||
}
|
}
|
||||||
|
|
||||||
if entryPoint != 0 {
|
if image.index == 0 {
|
||||||
bi.staticBase = entryPoint - elfFile.Entry
|
// adding executable file:
|
||||||
} else {
|
// - addr is entryPoint therefore staticBase needs to be calculated by
|
||||||
if elfFile.Type == elf.ET_DYN {
|
// subtracting the entry point specified in the executable file from addr.
|
||||||
|
// - memory address of the .dynamic section needs to be recorded in
|
||||||
|
// BinaryInfo so that we can find loaded libraries.
|
||||||
|
if addr != 0 {
|
||||||
|
image.StaticBase = addr - elfFile.Entry
|
||||||
|
} else if elfFile.Type == elf.ET_DYN {
|
||||||
return ErrCouldNotDetermineRelocation
|
return ErrCouldNotDetermineRelocation
|
||||||
}
|
}
|
||||||
}
|
if dynsec := elfFile.Section(".dynamic"); dynsec != nil {
|
||||||
|
bi.ElfDynamicSection.Addr = dynsec.Addr + image.StaticBase
|
||||||
if dynsec := elfFile.Section(".dynamic"); dynsec != nil {
|
bi.ElfDynamicSection.Size = dynsec.Size
|
||||||
bi.ElfDynamicSection.Addr = dynsec.Addr + bi.staticBase
|
}
|
||||||
bi.ElfDynamicSection.Size = dynsec.Size
|
} else {
|
||||||
|
image.StaticBase = addr
|
||||||
}
|
}
|
||||||
|
|
||||||
dwarfFile := elfFile
|
dwarfFile := elfFile
|
||||||
|
|
||||||
bi.dwarf, err = elfFile.DWARF()
|
image.dwarf, err = elfFile.DWARF()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
var sepFile *os.File
|
var sepFile *os.File
|
||||||
var serr error
|
var serr error
|
||||||
sepFile, dwarfFile, serr = bi.openSeparateDebugInfo(elfFile, debugInfoDirectories)
|
sepFile, dwarfFile, serr = bi.openSeparateDebugInfo(image, elfFile, bi.debugInfoDirectories)
|
||||||
if serr != nil {
|
if serr != nil {
|
||||||
return serr
|
return serr
|
||||||
}
|
}
|
||||||
bi.sepDebugCloser = sepFile
|
image.sepDebugCloser = sepFile
|
||||||
bi.dwarf, err = dwarfFile.DWARF()
|
image.dwarf, err = dwarfFile.DWARF()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bi.dwarfReader = bi.dwarf.Reader()
|
image.dwarfReader = image.dwarf.Reader()
|
||||||
|
|
||||||
debugLineBytes, err := godwarf.GetDebugSectionElf(dwarfFile, "line")
|
debugLineBytes, err := godwarf.GetDebugSectionElf(dwarfFile, "line")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
debugLocBytes, _ := godwarf.GetDebugSectionElf(dwarfFile, "loc")
|
debugLocBytes, _ := godwarf.GetDebugSectionElf(dwarfFile, "loc")
|
||||||
bi.loclistInit(debugLocBytes)
|
image.loclistInit(debugLocBytes, bi.Arch.PtrSize())
|
||||||
|
|
||||||
wg.Add(3)
|
wg.Add(2)
|
||||||
go bi.parseDebugFrameElf(dwarfFile, wg)
|
go bi.parseDebugFrameElf(image, dwarfFile, wg)
|
||||||
go bi.loadDebugInfoMaps(debugLineBytes, wg, nil)
|
go bi.loadDebugInfoMaps(image, debugLineBytes, wg, nil)
|
||||||
go bi.setGStructOffsetElf(dwarfFile, wg)
|
if image.index == 0 {
|
||||||
|
// determine g struct offset only when loading the executable file
|
||||||
|
wg.Add(1)
|
||||||
|
go bi.setGStructOffsetElf(image, dwarfFile, wg)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bi *BinaryInfo) parseDebugFrameElf(exe *elf.File, wg *sync.WaitGroup) {
|
func (bi *BinaryInfo) parseDebugFrameElf(image *Image, exe *elf.File, wg *sync.WaitGroup) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
debugFrameData, err := godwarf.GetDebugSectionElf(exe, "frame")
|
debugFrameData, err := godwarf.GetDebugSectionElf(exe, "frame")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
bi.setLoadError("could not get .debug_frame section: %v", err)
|
image.setLoadError("could not get .debug_frame section: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
debugInfoData, err := godwarf.GetDebugSectionElf(exe, "info")
|
debugInfoData, err := godwarf.GetDebugSectionElf(exe, "info")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
bi.setLoadError("could not get .debug_info section: %v", err)
|
image.setLoadError("could not get .debug_info section: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
bi.frameEntries = frame.Parse(debugFrameData, frame.DwarfEndian(debugInfoData), bi.staticBase)
|
bi.frameEntries = bi.frameEntries.Append(frame.Parse(debugFrameData, frame.DwarfEndian(debugInfoData), image.StaticBase))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bi *BinaryInfo) setGStructOffsetElf(exe *elf.File, wg *sync.WaitGroup) {
|
func (bi *BinaryInfo) setGStructOffsetElf(image *Image, exe *elf.File, wg *sync.WaitGroup) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
// This is a bit arcane. Essentially:
|
// This is a bit arcane. Essentially:
|
||||||
@ -770,7 +894,7 @@ func (bi *BinaryInfo) setGStructOffsetElf(exe *elf.File, wg *sync.WaitGroup) {
|
|||||||
// offset in libc's TLS block.
|
// offset in libc's TLS block.
|
||||||
symbols, err := exe.Symbols()
|
symbols, err := exe.Symbols()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
bi.setLoadError("could not parse ELF symbols: %v", err)
|
image.setLoadError("could not parse ELF symbols: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
var tlsg *elf.Symbol
|
var tlsg *elf.Symbol
|
||||||
@ -808,17 +932,17 @@ func (bi *BinaryInfo) setGStructOffsetElf(exe *elf.File, wg *sync.WaitGroup) {
|
|||||||
|
|
||||||
const _IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE = 0x0040
|
const _IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE = 0x0040
|
||||||
|
|
||||||
// LoadBinaryInfoPE specifically loads information from a PE binary.
|
// loadBinaryInfoPE specifically loads information from a PE binary.
|
||||||
func (bi *BinaryInfo) LoadBinaryInfoPE(path string, entryPoint uint64, wg *sync.WaitGroup) error {
|
func loadBinaryInfoPE(bi *BinaryInfo, image *Image, path string, entryPoint uint64, wg *sync.WaitGroup) error {
|
||||||
peFile, closer, err := openExecutablePathPE(path)
|
peFile, closer, err := openExecutablePathPE(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
bi.closer = closer
|
image.closer = closer
|
||||||
if peFile.Machine != pe.IMAGE_FILE_MACHINE_AMD64 {
|
if peFile.Machine != pe.IMAGE_FILE_MACHINE_AMD64 {
|
||||||
return ErrUnsupportedWindowsArch
|
return ErrUnsupportedWindowsArch
|
||||||
}
|
}
|
||||||
bi.dwarf, err = peFile.DWARF()
|
image.dwarf, err = peFile.DWARF()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -826,25 +950,25 @@ func (bi *BinaryInfo) LoadBinaryInfoPE(path string, entryPoint uint64, wg *sync.
|
|||||||
//TODO(aarzilli): actually test this when Go supports PIE buildmode on Windows.
|
//TODO(aarzilli): actually test this when Go supports PIE buildmode on Windows.
|
||||||
opth := peFile.OptionalHeader.(*pe.OptionalHeader64)
|
opth := peFile.OptionalHeader.(*pe.OptionalHeader64)
|
||||||
if entryPoint != 0 {
|
if entryPoint != 0 {
|
||||||
bi.staticBase = entryPoint - opth.ImageBase
|
image.StaticBase = entryPoint - opth.ImageBase
|
||||||
} else {
|
} else {
|
||||||
if opth.DllCharacteristics&_IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE != 0 {
|
if opth.DllCharacteristics&_IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE != 0 {
|
||||||
return ErrCouldNotDetermineRelocation
|
return ErrCouldNotDetermineRelocation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bi.dwarfReader = bi.dwarf.Reader()
|
image.dwarfReader = image.dwarf.Reader()
|
||||||
|
|
||||||
debugLineBytes, err := godwarf.GetDebugSectionPE(peFile, "line")
|
debugLineBytes, err := godwarf.GetDebugSectionPE(peFile, "line")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
debugLocBytes, _ := godwarf.GetDebugSectionPE(peFile, "loc")
|
debugLocBytes, _ := godwarf.GetDebugSectionPE(peFile, "loc")
|
||||||
bi.loclistInit(debugLocBytes)
|
image.loclistInit(debugLocBytes, bi.Arch.PtrSize())
|
||||||
|
|
||||||
wg.Add(2)
|
wg.Add(2)
|
||||||
go bi.parseDebugFramePE(peFile, wg)
|
go bi.parseDebugFramePE(image, peFile, wg)
|
||||||
go bi.loadDebugInfoMaps(debugLineBytes, wg, nil)
|
go bi.loadDebugInfoMaps(image, debugLineBytes, wg, nil)
|
||||||
|
|
||||||
// Use ArbitraryUserPointer (0x28) as pointer to pointer
|
// Use ArbitraryUserPointer (0x28) as pointer to pointer
|
||||||
// to G struct per:
|
// to G struct per:
|
||||||
@ -867,21 +991,21 @@ func openExecutablePathPE(path string) (*pe.File, io.Closer, error) {
|
|||||||
return peFile, f, nil
|
return peFile, f, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bi *BinaryInfo) parseDebugFramePE(exe *pe.File, wg *sync.WaitGroup) {
|
func (bi *BinaryInfo) parseDebugFramePE(image *Image, exe *pe.File, wg *sync.WaitGroup) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
debugFrameBytes, err := godwarf.GetDebugSectionPE(exe, "frame")
|
debugFrameBytes, err := godwarf.GetDebugSectionPE(exe, "frame")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
bi.setLoadError("could not get .debug_frame section: %v", err)
|
image.setLoadError("could not get .debug_frame section: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
debugInfoBytes, err := godwarf.GetDebugSectionPE(exe, "info")
|
debugInfoBytes, err := godwarf.GetDebugSectionPE(exe, "info")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
bi.setLoadError("could not get .debug_info section: %v", err)
|
image.setLoadError("could not get .debug_info section: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
bi.frameEntries = frame.Parse(debugFrameBytes, frame.DwarfEndian(debugInfoBytes), bi.staticBase)
|
bi.frameEntries = bi.frameEntries.Append(frame.Parse(debugFrameBytes, frame.DwarfEndian(debugInfoBytes), image.StaticBase))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Borrowed from https://golang.org/src/cmd/internal/objfile/pe.go
|
// Borrowed from https://golang.org/src/cmd/internal/objfile/pe.go
|
||||||
@ -903,33 +1027,33 @@ func findPESymbol(f *pe.File, name string) (*pe.Symbol, error) {
|
|||||||
|
|
||||||
// MACH-O ////////////////////////////////////////////////////////////
|
// MACH-O ////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// LoadBinaryInfoMacho specifically loads information from a Mach-O binary.
|
// loadBinaryInfoMacho specifically loads information from a Mach-O binary.
|
||||||
func (bi *BinaryInfo) LoadBinaryInfoMacho(path string, entryPoint uint64, wg *sync.WaitGroup) error {
|
func loadBinaryInfoMacho(bi *BinaryInfo, image *Image, path string, entryPoint uint64, wg *sync.WaitGroup) error {
|
||||||
exe, err := macho.Open(path)
|
exe, err := macho.Open(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
bi.closer = exe
|
image.closer = exe
|
||||||
if exe.Cpu != macho.CpuAmd64 {
|
if exe.Cpu != macho.CpuAmd64 {
|
||||||
return ErrUnsupportedDarwinArch
|
return ErrUnsupportedDarwinArch
|
||||||
}
|
}
|
||||||
bi.dwarf, err = exe.DWARF()
|
image.dwarf, err = exe.DWARF()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
bi.dwarfReader = bi.dwarf.Reader()
|
image.dwarfReader = image.dwarf.Reader()
|
||||||
|
|
||||||
debugLineBytes, err := godwarf.GetDebugSectionMacho(exe, "line")
|
debugLineBytes, err := godwarf.GetDebugSectionMacho(exe, "line")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
debugLocBytes, _ := godwarf.GetDebugSectionMacho(exe, "loc")
|
debugLocBytes, _ := godwarf.GetDebugSectionMacho(exe, "loc")
|
||||||
bi.loclistInit(debugLocBytes)
|
image.loclistInit(debugLocBytes, bi.Arch.PtrSize())
|
||||||
|
|
||||||
wg.Add(2)
|
wg.Add(2)
|
||||||
go bi.parseDebugFrameMacho(exe, wg)
|
go bi.parseDebugFrameMacho(image, exe, wg)
|
||||||
go bi.loadDebugInfoMaps(debugLineBytes, wg, bi.setGStructOffsetMacho)
|
go bi.loadDebugInfoMaps(image, debugLineBytes, wg, bi.setGStructOffsetMacho)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -945,19 +1069,19 @@ func (bi *BinaryInfo) setGStructOffsetMacho() {
|
|||||||
bi.gStructOffset = 0x8a0
|
bi.gStructOffset = 0x8a0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bi *BinaryInfo) parseDebugFrameMacho(exe *macho.File, wg *sync.WaitGroup) {
|
func (bi *BinaryInfo) parseDebugFrameMacho(image *Image, exe *macho.File, wg *sync.WaitGroup) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
debugFrameBytes, err := godwarf.GetDebugSectionMacho(exe, "frame")
|
debugFrameBytes, err := godwarf.GetDebugSectionMacho(exe, "frame")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
bi.setLoadError("could not get __debug_frame section: %v", err)
|
image.setLoadError("could not get __debug_frame section: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
debugInfoBytes, err := godwarf.GetDebugSectionMacho(exe, "info")
|
debugInfoBytes, err := godwarf.GetDebugSectionMacho(exe, "info")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
bi.setLoadError("could not get .debug_info section: %v", err)
|
image.setLoadError("could not get .debug_info section: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
bi.frameEntries = frame.Parse(debugFrameBytes, frame.DwarfEndian(debugInfoBytes), bi.staticBase)
|
bi.frameEntries = bi.frameEntries.Append(frame.Parse(debugFrameBytes, frame.DwarfEndian(debugInfoBytes), image.StaticBase))
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ func fakeBinaryInfo(t *testing.T, dwb *dwarfbuilder.Builder) *proc.BinaryInfo {
|
|||||||
assertNoError(err, t, "creating dwarf")
|
assertNoError(err, t, "creating dwarf")
|
||||||
|
|
||||||
bi := proc.NewBinaryInfo("linux", "amd64")
|
bi := proc.NewBinaryInfo("linux", "amd64")
|
||||||
bi.LoadFromData(dwdata, frame, line, loc)
|
bi.LoadImageFromData(dwdata, frame, line, loc)
|
||||||
|
|
||||||
return bi
|
return bi
|
||||||
}
|
}
|
||||||
@ -88,9 +88,9 @@ func dwarfExprCheck(t *testing.T, mem proc.MemoryReadWriter, regs op.DwarfRegist
|
|||||||
return scope
|
return scope
|
||||||
}
|
}
|
||||||
|
|
||||||
func dwarfRegisters(regs *linutil.AMD64Registers) op.DwarfRegisters {
|
func dwarfRegisters(bi *proc.BinaryInfo, regs *linutil.AMD64Registers) op.DwarfRegisters {
|
||||||
a := proc.AMD64Arch("linux")
|
a := proc.AMD64Arch("linux")
|
||||||
dwarfRegs := a.RegistersToDwarfRegisters(regs, 0)
|
dwarfRegs := a.RegistersToDwarfRegisters(bi, regs)
|
||||||
dwarfRegs.CFA = defaultCFA
|
dwarfRegs.CFA = defaultCFA
|
||||||
dwarfRegs.FrameBase = defaultCFA
|
dwarfRegs.FrameBase = defaultCFA
|
||||||
return dwarfRegs
|
return dwarfRegs
|
||||||
@ -123,7 +123,7 @@ func TestDwarfExprRegisters(t *testing.T) {
|
|||||||
regs.Regs.Rax = uint64(testCases["a"])
|
regs.Regs.Rax = uint64(testCases["a"])
|
||||||
regs.Regs.Rdx = uint64(testCases["c"])
|
regs.Regs.Rdx = uint64(testCases["c"])
|
||||||
|
|
||||||
dwarfExprCheck(t, mem, dwarfRegisters(®s), bi, testCases, mainfn)
|
dwarfExprCheck(t, mem, dwarfRegisters(bi, ®s), bi, testCases, mainfn)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDwarfExprComposite(t *testing.T) {
|
func TestDwarfExprComposite(t *testing.T) {
|
||||||
@ -178,7 +178,7 @@ func TestDwarfExprComposite(t *testing.T) {
|
|||||||
regs.Regs.Rcx = uint64(testCases["pair.k"])
|
regs.Regs.Rcx = uint64(testCases["pair.k"])
|
||||||
regs.Regs.Rbx = uint64(testCases["n"])
|
regs.Regs.Rbx = uint64(testCases["n"])
|
||||||
|
|
||||||
scope := dwarfExprCheck(t, mem, dwarfRegisters(®s), bi, testCases, mainfn)
|
scope := dwarfExprCheck(t, mem, dwarfRegisters(bi, ®s), bi, testCases, mainfn)
|
||||||
|
|
||||||
thevar, err := scope.EvalExpression("s", normalLoadConfig)
|
thevar, err := scope.EvalExpression("s", normalLoadConfig)
|
||||||
assertNoError(err, t, fmt.Sprintf("EvalExpression(%s)", "s"))
|
assertNoError(err, t, fmt.Sprintf("EvalExpression(%s)", "s"))
|
||||||
@ -213,7 +213,7 @@ func TestDwarfExprLoclist(t *testing.T) {
|
|||||||
mem := newFakeMemory(defaultCFA, uint16(before), uint16(after))
|
mem := newFakeMemory(defaultCFA, uint16(before), uint16(after))
|
||||||
regs := linutil.AMD64Registers{Regs: &linutil.AMD64PtraceRegs{}}
|
regs := linutil.AMD64Registers{Regs: &linutil.AMD64PtraceRegs{}}
|
||||||
|
|
||||||
scope := &proc.EvalScope{Location: proc.Location{PC: 0x40100, Fn: mainfn}, Regs: dwarfRegisters(®s), Mem: mem, Gvar: nil, BinInfo: bi}
|
scope := &proc.EvalScope{Location: proc.Location{PC: 0x40100, Fn: mainfn}, Regs: dwarfRegisters(bi, ®s), Mem: mem, Gvar: nil, BinInfo: bi}
|
||||||
|
|
||||||
uintExprCheck(t, scope, "a", before)
|
uintExprCheck(t, scope, "a", before)
|
||||||
scope.PC = 0x40800
|
scope.PC = 0x40800
|
||||||
|
@ -295,7 +295,7 @@ func funcCallArgFrame(fn *Function, actualArgs []*Variable, g *G, bi *BinaryInfo
|
|||||||
|
|
||||||
func funcCallArgs(fn *Function, bi *BinaryInfo, includeRet bool) (argFrameSize int64, formalArgs []funcCallArg, err error) {
|
func funcCallArgs(fn *Function, bi *BinaryInfo, includeRet bool) (argFrameSize int64, formalArgs []funcCallArg, err error) {
|
||||||
const CFA = 0x1000
|
const CFA = 0x1000
|
||||||
vrdr := reader.Variables(bi.dwarf, fn.offset, reader.ToRelAddr(fn.Entry, bi.staticBase), int(^uint(0)>>1), false)
|
vrdr := reader.Variables(fn.cu.image.dwarf, fn.offset, reader.ToRelAddr(fn.Entry, fn.cu.image.StaticBase), int(^uint(0)>>1), false)
|
||||||
|
|
||||||
// typechecks arguments, calculates argument frame size
|
// typechecks arguments, calculates argument frame size
|
||||||
for vrdr.Next() {
|
for vrdr.Next() {
|
||||||
@ -303,7 +303,7 @@ func funcCallArgs(fn *Function, bi *BinaryInfo, includeRet bool) (argFrameSize i
|
|||||||
if e.Tag != dwarf.TagFormalParameter {
|
if e.Tag != dwarf.TagFormalParameter {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
entry, argname, typ, err := readVarEntry(e, bi)
|
entry, argname, typ, err := readVarEntry(e, fn.cu.image)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, err
|
return 0, nil, err
|
||||||
}
|
}
|
||||||
@ -548,8 +548,8 @@ func fakeFunctionEntryScope(scope *EvalScope, fn *Function, cfa int64, sp uint64
|
|||||||
scope.Regs.CFA = cfa
|
scope.Regs.CFA = cfa
|
||||||
scope.Regs.Regs[scope.Regs.SPRegNum].Uint64Val = sp
|
scope.Regs.Regs[scope.Regs.SPRegNum].Uint64Val = sp
|
||||||
|
|
||||||
scope.BinInfo.dwarfReader.Seek(fn.offset)
|
fn.cu.image.dwarfReader.Seek(fn.offset)
|
||||||
e, err := scope.BinInfo.dwarfReader.Next()
|
e, err := fn.cu.image.dwarfReader.Next()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -7,77 +7,83 @@ import (
|
|||||||
|
|
||||||
// delve counterpart to runtime.moduledata
|
// delve counterpart to runtime.moduledata
|
||||||
type moduleData struct {
|
type moduleData struct {
|
||||||
|
text, etext uintptr
|
||||||
types, etypes uintptr
|
types, etypes uintptr
|
||||||
typemapVar *Variable
|
typemapVar *Variable
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadModuleData(bi *BinaryInfo, mem MemoryReadWriter) (err error) {
|
func loadModuleData(bi *BinaryInfo, mem MemoryReadWriter) ([]moduleData, error) {
|
||||||
bi.loadModuleDataOnce.Do(func() {
|
scope := globalScope(bi, bi.Images[0], mem)
|
||||||
scope := globalScope(bi, mem)
|
var md *Variable
|
||||||
var md *Variable
|
md, err := scope.findGlobal("runtime.firstmoduledata")
|
||||||
md, err = scope.findGlobal("runtime.firstmoduledata")
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for md.Addr != 0 {
|
|
||||||
var typesVar, etypesVar, nextVar, typemapVar *Variable
|
|
||||||
var types, etypes uint64
|
|
||||||
|
|
||||||
if typesVar, err = md.structMember("types"); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if etypesVar, err = md.structMember("etypes"); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if nextVar, err = md.structMember("next"); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if typemapVar, err = md.structMember("typemap"); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if types, err = typesVar.asUint(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if etypes, err = etypesVar.asUint(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
bi.moduleData = append(bi.moduleData, moduleData{uintptr(types), uintptr(etypes), typemapVar})
|
|
||||||
|
|
||||||
md = nextVar.maybeDereference()
|
|
||||||
if md.Unreadable != nil {
|
|
||||||
err = md.Unreadable
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func findModuleDataForType(bi *BinaryInfo, typeAddr uintptr, mem MemoryReadWriter) (*moduleData, error) {
|
|
||||||
if err := loadModuleData(bi, mem); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var md *moduleData
|
|
||||||
for i := range bi.moduleData {
|
|
||||||
if typeAddr >= bi.moduleData[i].types && typeAddr < bi.moduleData[i].etypes {
|
|
||||||
md = &bi.moduleData[i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return md, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func resolveTypeOff(bi *BinaryInfo, typeAddr uintptr, off uintptr, mem MemoryReadWriter) (*Variable, error) {
|
|
||||||
// See runtime.(*_type).typeOff in $GOROOT/src/runtime/type.go
|
|
||||||
md, err := findModuleDataForType(bi, typeAddr, mem)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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")
|
rtyp, err := bi.findType("runtime._type")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -102,13 +108,9 @@ func resolveTypeOff(bi *BinaryInfo, typeAddr uintptr, off uintptr, mem MemoryRea
|
|||||||
return newVariable("", res, rtyp, bi, mem), nil
|
return newVariable("", res, rtyp, bi, mem), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func resolveNameOff(bi *BinaryInfo, typeAddr uintptr, off uintptr, mem MemoryReadWriter) (name, tag string, pkgpathoff int32, err error) {
|
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
|
// See runtime.resolveNameOff in $GOROOT/src/runtime/type.go
|
||||||
if err = loadModuleData(bi, mem); err != nil {
|
for _, md := range mds {
|
||||||
return "", "", 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, md := range bi.moduleData {
|
|
||||||
if typeAddr >= md.types && typeAddr < md.etypes {
|
if typeAddr >= md.types && typeAddr < md.etypes {
|
||||||
return loadName(bi, md.types+off, mem)
|
return loadName(bi, md.types+off, mem)
|
||||||
}
|
}
|
||||||
@ -128,7 +130,7 @@ func resolveNameOff(bi *BinaryInfo, typeAddr uintptr, off uintptr, mem MemoryRea
|
|||||||
}
|
}
|
||||||
|
|
||||||
func reflectOffsMapAccess(bi *BinaryInfo, off uintptr, mem MemoryReadWriter) (*Variable, error) {
|
func reflectOffsMapAccess(bi *BinaryInfo, off uintptr, mem MemoryReadWriter) (*Variable, error) {
|
||||||
scope := globalScope(bi, mem)
|
scope := globalScope(bi, bi.Images[0], mem)
|
||||||
reflectOffs, err := scope.findGlobal("runtime.reflectOffs")
|
reflectOffs, err := scope.findGlobal("runtime.reflectOffs")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -58,12 +58,14 @@ func PostInitializationSetup(p Process, path string, debugInfoDirs []string, wri
|
|||||||
}
|
}
|
||||||
|
|
||||||
err = p.BinInfo().LoadBinaryInfo(path, entryPoint, debugInfoDirs)
|
err = p.BinInfo().LoadBinaryInfo(path, entryPoint, debugInfoDirs)
|
||||||
if err == nil {
|
|
||||||
err = p.BinInfo().LoadError()
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
for _, image := range p.BinInfo().Images {
|
||||||
|
if image.loadErr != nil {
|
||||||
|
return image.loadErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
g, _ := GetG(p.CurrentThread())
|
g, _ := GetG(p.CurrentThread())
|
||||||
p.SetSelectedGoroutine(g)
|
p.SetSelectedGoroutine(g)
|
||||||
@ -515,10 +517,12 @@ func GoroutinesInfo(dbp Process, start, count int) ([]*G, int, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
exeimage := dbp.BinInfo().Images[0] // Image corresponding to the executable file
|
||||||
|
|
||||||
var (
|
var (
|
||||||
threadg = map[int]*G{}
|
threadg = map[int]*G{}
|
||||||
allg []*G
|
allg []*G
|
||||||
rdr = dbp.BinInfo().DwarfReader()
|
rdr = exeimage.DwarfReader()
|
||||||
)
|
)
|
||||||
|
|
||||||
threads := dbp.ThreadList()
|
threads := dbp.ThreadList()
|
||||||
@ -532,7 +536,7 @@ func GoroutinesInfo(dbp Process, start, count int) ([]*G, int, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addr, err := rdr.AddrFor("runtime.allglen", dbp.BinInfo().staticBase)
|
addr, err := rdr.AddrFor("runtime.allglen", exeimage.StaticBase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, -1, err
|
return nil, -1, err
|
||||||
}
|
}
|
||||||
@ -544,10 +548,10 @@ func GoroutinesInfo(dbp Process, start, count int) ([]*G, int, error) {
|
|||||||
allglen := binary.LittleEndian.Uint64(allglenBytes)
|
allglen := binary.LittleEndian.Uint64(allglenBytes)
|
||||||
|
|
||||||
rdr.Seek(0)
|
rdr.Seek(0)
|
||||||
allgentryaddr, err := rdr.AddrFor("runtime.allgs", dbp.BinInfo().staticBase)
|
allgentryaddr, err := rdr.AddrFor("runtime.allgs", exeimage.StaticBase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// try old name (pre Go 1.6)
|
// try old name (pre Go 1.6)
|
||||||
allgentryaddr, err = rdr.AddrFor("runtime.allg", dbp.BinInfo().staticBase)
|
allgentryaddr, err = rdr.AddrFor("runtime.allg", exeimage.StaticBase)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, -1, err
|
return nil, -1, err
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@ -184,7 +185,11 @@ func setFunctionBreakpoint(p proc.Process, fname string) (*proc.Breakpoint, erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
func setFileBreakpoint(p proc.Process, t *testing.T, fixture protest.Fixture, lineno int) *proc.Breakpoint {
|
func setFileBreakpoint(p proc.Process, t *testing.T, fixture protest.Fixture, lineno int) *proc.Breakpoint {
|
||||||
addr, err := proc.FindFileLocation(p, fixture.Source, lineno)
|
return setFileLineBreakpoint(p, t, fixture.Source, lineno)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setFileLineBreakpoint(p proc.Process, t *testing.T, path string, lineno int) *proc.Breakpoint {
|
||||||
|
addr, err := proc.FindFileLocation(p, path, lineno)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("FindFileLocation: %v", err)
|
t.Fatalf("FindFileLocation: %v", err)
|
||||||
}
|
}
|
||||||
@ -366,7 +371,7 @@ const (
|
|||||||
|
|
||||||
type seqTest struct {
|
type seqTest struct {
|
||||||
cf contFunc
|
cf contFunc
|
||||||
pos int
|
pos interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func testseq(program string, contFunc contFunc, testcases []nextTest, initialLocation string, t *testing.T) {
|
func testseq(program string, contFunc contFunc, testcases []nextTest, initialLocation string, t *testing.T) {
|
||||||
@ -398,7 +403,7 @@ func testseq2Args(wd string, args []string, buildFlags protest.BuildFlags, t *te
|
|||||||
bp, err = setFunctionBreakpoint(p, initialLocation)
|
bp, err = setFunctionBreakpoint(p, initialLocation)
|
||||||
} else if testcases[0].cf == contContinue {
|
} else if testcases[0].cf == contContinue {
|
||||||
var pc uint64
|
var pc uint64
|
||||||
pc, err = proc.FindFileLocation(p, fixture.Source, testcases[0].pos)
|
pc, err = proc.FindFileLocation(p, fixture.Source, testcases[0].pos.(int))
|
||||||
assertNoError(err, t, "FindFileLocation()")
|
assertNoError(err, t, "FindFileLocation()")
|
||||||
bp, err = p.SetBreakpoint(pc, proc.UserBreakpoint, nil)
|
bp, err = p.SetBreakpoint(pc, proc.UserBreakpoint, nil)
|
||||||
} else {
|
} else {
|
||||||
@ -449,9 +454,19 @@ func testseq2Args(wd string, args []string, buildFlags protest.BuildFlags, t *te
|
|||||||
|
|
||||||
if traceTestseq2 {
|
if traceTestseq2 {
|
||||||
t.Logf("at %#x %s:%d", pc, f, ln)
|
t.Logf("at %#x %s:%d", pc, f, ln)
|
||||||
|
fmt.Printf("at %#x %s:%d", pc, f, ln)
|
||||||
}
|
}
|
||||||
if ln != tc.pos {
|
switch pos := tc.pos.(type) {
|
||||||
t.Fatalf("Program did not continue to correct next location expected %d was %s:%d (%#x) (testcase %d)", tc.pos, filepath.Base(f), ln, pc, i)
|
case int:
|
||||||
|
if ln != pos {
|
||||||
|
t.Fatalf("Program did not continue to correct next location expected %d was %s:%d (%#x) (testcase %d)", pos, filepath.Base(f), ln, pc, i)
|
||||||
|
}
|
||||||
|
case string:
|
||||||
|
v := strings.Split(pos, ":")
|
||||||
|
tgtln, _ := strconv.Atoi(v[1])
|
||||||
|
if !strings.HasSuffix(f, v[0]) || (ln != tgtln) {
|
||||||
|
t.Fatalf("Program did not continue to correct next location, expected %s was %s:%d (%#x) (testcase %d)", pos, filepath.Base(f), ln, pc, i)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4220,10 +4235,11 @@ func TestListImages(t *testing.T) {
|
|||||||
|
|
||||||
withTestProcessArgs("plugintest", t, ".", []string{pluginFixtures[0].Path, pluginFixtures[1].Path}, 0, func(p proc.Process, fixture protest.Fixture) {
|
withTestProcessArgs("plugintest", t, ".", []string{pluginFixtures[0].Path, pluginFixtures[1].Path}, 0, func(p proc.Process, fixture protest.Fixture) {
|
||||||
assertNoError(proc.Continue(p), t, "first continue")
|
assertNoError(proc.Continue(p), t, "first continue")
|
||||||
|
f, l := currentLineNumber(p, t)
|
||||||
plugin1Found := false
|
plugin1Found := false
|
||||||
t.Logf("Libraries before:")
|
t.Logf("Libraries before %s:%d:", f, l)
|
||||||
for _, image := range p.BinInfo().Images {
|
for _, image := range p.BinInfo().Images {
|
||||||
t.Logf("\t%#v", image)
|
t.Logf("\t%#x %q err:%v", image.StaticBase, image.Path, image.LoadError())
|
||||||
if image.Path == pluginFixtures[0].Path {
|
if image.Path == pluginFixtures[0].Path {
|
||||||
plugin1Found = true
|
plugin1Found = true
|
||||||
}
|
}
|
||||||
@ -4232,10 +4248,11 @@ func TestListImages(t *testing.T) {
|
|||||||
t.Fatalf("Could not find plugin1")
|
t.Fatalf("Could not find plugin1")
|
||||||
}
|
}
|
||||||
assertNoError(proc.Continue(p), t, "second continue")
|
assertNoError(proc.Continue(p), t, "second continue")
|
||||||
|
f, l = currentLineNumber(p, t)
|
||||||
plugin1Found, plugin2Found := false, false
|
plugin1Found, plugin2Found := false, false
|
||||||
t.Logf("Libraries after:")
|
t.Logf("Libraries after %s:%d:", f, l)
|
||||||
for _, image := range p.BinInfo().Images {
|
for _, image := range p.BinInfo().Images {
|
||||||
t.Logf("\t%#v", image)
|
t.Logf("\t%#x %q err:%v", image.StaticBase, image.Path, image.LoadError())
|
||||||
switch image.Path {
|
switch image.Path {
|
||||||
case pluginFixtures[0].Path:
|
case pluginFixtures[0].Path:
|
||||||
plugin1Found = true
|
plugin1Found = true
|
||||||
@ -4332,3 +4349,20 @@ func TestCallConcurrent(t *testing.T) {
|
|||||||
proc.Continue(p)
|
proc.Continue(p)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPluginStepping(t *testing.T) {
|
||||||
|
pluginFixtures := protest.WithPlugins(t, "plugin1/", "plugin2/")
|
||||||
|
|
||||||
|
testseq2Args(".", []string{pluginFixtures[0].Path, pluginFixtures[1].Path}, 0, t, "plugintest2", "", []seqTest{
|
||||||
|
{contContinue, 41},
|
||||||
|
{contStep, "plugin1.go:9"},
|
||||||
|
{contStep, "plugin1.go:10"},
|
||||||
|
{contStep, "plugin1.go:11"},
|
||||||
|
{contNext, "plugin1.go:12"},
|
||||||
|
{contNext, "plugintest2.go:41"},
|
||||||
|
{contNext, "plugintest2.go:42"},
|
||||||
|
{contStep, "plugin2.go:22"},
|
||||||
|
{contNext, "plugin2.go:23"},
|
||||||
|
{contNext, "plugin2.go:26"},
|
||||||
|
{contNext, "plugintest2.go:42"}})
|
||||||
|
}
|
||||||
|
@ -100,7 +100,7 @@ func ThreadStacktrace(thread Thread, depth int) ([]Stackframe, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
it := newStackIterator(thread.BinInfo(), thread, thread.BinInfo().Arch.RegistersToDwarfRegisters(regs, thread.BinInfo().staticBase), 0, nil, -1, nil)
|
it := newStackIterator(thread.BinInfo(), thread, thread.BinInfo().Arch.RegistersToDwarfRegisters(thread.BinInfo(), regs), 0, nil, -1, nil)
|
||||||
return it.stacktrace(depth)
|
return it.stacktrace(depth)
|
||||||
}
|
}
|
||||||
return g.Stacktrace(depth, false)
|
return g.Stacktrace(depth, false)
|
||||||
@ -117,7 +117,7 @@ func (g *G) stackIterator() (*stackIterator, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return newStackIterator(g.variable.bi, g.Thread, g.variable.bi.Arch.RegistersToDwarfRegisters(regs, g.variable.bi.staticBase), g.stackhi, stkbar, g.stkbarPos, g), nil
|
return newStackIterator(g.variable.bi, g.Thread, g.variable.bi.Arch.RegistersToDwarfRegisters(g.variable.bi, regs), g.stackhi, stkbar, g.stkbarPos, g), nil
|
||||||
}
|
}
|
||||||
return newStackIterator(g.variable.bi, g.variable.mem, g.variable.bi.Arch.GoroutineToDwarfRegisters(g), g.stackhi, stkbar, g.stkbarPos, g), nil
|
return newStackIterator(g.variable.bi, g.variable.mem, g.variable.bi.Arch.GoroutineToDwarfRegisters(g), g.stackhi, stkbar, g.stkbarPos, g), nil
|
||||||
}
|
}
|
||||||
@ -168,8 +168,6 @@ type stackIterator struct {
|
|||||||
|
|
||||||
g *G // the goroutine being stacktraced, nil if we are stacktracing a goroutine-less thread
|
g *G // the goroutine being stacktraced, nil if we are stacktracing a goroutine-less thread
|
||||||
g0_sched_sp uint64 // value of g0.sched.sp (see comments around its use)
|
g0_sched_sp uint64 // value of g0.sched.sp (see comments around its use)
|
||||||
|
|
||||||
dwarfReader *dwarf.Reader
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type savedLR struct {
|
type savedLR struct {
|
||||||
@ -209,7 +207,7 @@ func newStackIterator(bi *BinaryInfo, mem MemoryReadWriter, regs op.DwarfRegiste
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return &stackIterator{pc: regs.PC(), regs: regs, top: true, bi: bi, mem: mem, err: nil, atend: false, stackhi: stackhi, stackBarrierPC: stackBarrierPC, stkbar: stkbar, systemstack: systemstack, g: g, g0_sched_sp: g0_sched_sp, dwarfReader: bi.dwarf.Reader()}
|
return &stackIterator{pc: regs.PC(), regs: regs, top: true, bi: bi, mem: mem, err: nil, atend: false, stackhi: stackhi, stackBarrierPC: stackBarrierPC, stkbar: stkbar, systemstack: systemstack, g: g, g0_sched_sp: g0_sched_sp}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next points the iterator to the next stack frame.
|
// Next points the iterator to the next stack frame.
|
||||||
@ -353,8 +351,9 @@ func (it *stackIterator) Err() error {
|
|||||||
// frameBase calculates the frame base pseudo-register for DWARF for fn and
|
// frameBase calculates the frame base pseudo-register for DWARF for fn and
|
||||||
// the current frame.
|
// the current frame.
|
||||||
func (it *stackIterator) frameBase(fn *Function) int64 {
|
func (it *stackIterator) frameBase(fn *Function) int64 {
|
||||||
it.dwarfReader.Seek(fn.offset)
|
rdr := fn.cu.image.dwarfReader
|
||||||
e, err := it.dwarfReader.Next()
|
rdr.Seek(fn.offset)
|
||||||
|
e, err := rdr.Next()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@ -427,9 +426,11 @@ func (it *stackIterator) appendInlineCalls(frames []Stackframe, frame Stackframe
|
|||||||
callpc--
|
callpc--
|
||||||
}
|
}
|
||||||
|
|
||||||
irdr := reader.InlineStack(it.bi.dwarf, frame.Call.Fn.offset, reader.ToRelAddr(callpc, it.bi.staticBase))
|
image := frame.Call.Fn.cu.image
|
||||||
|
|
||||||
|
irdr := reader.InlineStack(image.dwarf, frame.Call.Fn.offset, reader.ToRelAddr(callpc, image.StaticBase))
|
||||||
for irdr.Next() {
|
for irdr.Next() {
|
||||||
entry, offset := reader.LoadAbstractOrigin(irdr.Entry(), it.dwarfReader)
|
entry, offset := reader.LoadAbstractOrigin(irdr.Entry(), image.dwarfReader)
|
||||||
|
|
||||||
fnname, okname := entry.Val(dwarf.AttrName).(string)
|
fnname, okname := entry.Val(dwarf.AttrName).(string)
|
||||||
fileidx, okfileidx := entry.Val(dwarf.AttrCallFile).(int64)
|
fileidx, okfileidx := entry.Val(dwarf.AttrCallFile).(int64)
|
||||||
@ -483,11 +484,13 @@ func (it *stackIterator) advanceRegs() (callFrameRegs op.DwarfRegisters, ret uin
|
|||||||
cfareg, err := it.executeFrameRegRule(0, framectx.CFA, 0)
|
cfareg, err := it.executeFrameRegRule(0, framectx.CFA, 0)
|
||||||
if cfareg == nil {
|
if cfareg == nil {
|
||||||
it.err = fmt.Errorf("CFA becomes undefined at PC %#x", it.pc)
|
it.err = fmt.Errorf("CFA becomes undefined at PC %#x", it.pc)
|
||||||
return op.DwarfRegisters{StaticBase: it.bi.staticBase}, 0, 0
|
return op.DwarfRegisters{}, 0, 0
|
||||||
}
|
}
|
||||||
it.regs.CFA = int64(cfareg.Uint64Val)
|
it.regs.CFA = int64(cfareg.Uint64Val)
|
||||||
|
|
||||||
callFrameRegs = op.DwarfRegisters{StaticBase: it.bi.staticBase, ByteOrder: it.regs.ByteOrder, PCRegNum: it.regs.PCRegNum, SPRegNum: it.regs.SPRegNum, BPRegNum: it.regs.BPRegNum}
|
callimage := it.bi.pcToImage(it.pc)
|
||||||
|
|
||||||
|
callFrameRegs = op.DwarfRegisters{StaticBase: callimage.StaticBase, ByteOrder: it.regs.ByteOrder, PCRegNum: it.regs.PCRegNum, SPRegNum: it.regs.SPRegNum, BPRegNum: it.regs.BPRegNum}
|
||||||
|
|
||||||
// According to the standard the compiler should be responsible for emitting
|
// According to the standard the compiler should be responsible for emitting
|
||||||
// rules for the RSP register so that it can then be used to calculate CFA,
|
// rules for the RSP register so that it can then be used to calculate CFA,
|
||||||
@ -699,8 +702,9 @@ func (d *Defer) EvalScope(thread Thread) (*EvalScope, error) {
|
|||||||
scope.Regs.CFA = (int64(d.variable.Addr) + d.variable.RealType.Common().ByteSize)
|
scope.Regs.CFA = (int64(d.variable.Addr) + d.variable.RealType.Common().ByteSize)
|
||||||
scope.Regs.Regs[scope.Regs.SPRegNum].Uint64Val = uint64(scope.Regs.CFA - int64(bi.Arch.PtrSize()))
|
scope.Regs.Regs[scope.Regs.SPRegNum].Uint64Val = uint64(scope.Regs.CFA - int64(bi.Arch.PtrSize()))
|
||||||
|
|
||||||
bi.dwarfReader.Seek(scope.Fn.offset)
|
rdr := scope.Fn.cu.image.dwarfReader
|
||||||
e, err := bi.dwarfReader.Next()
|
rdr.Seek(scope.Fn.offset)
|
||||||
|
e, err := rdr.Next()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("could not read DWARF function entry: %v", err)
|
return nil, fmt.Errorf("could not read DWARF function entry: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -343,19 +343,20 @@ func findDeferReturnCalls(text []AsmInstruction) []uint64 {
|
|||||||
// If includeCurrentFn is true it will also remove all instructions
|
// If includeCurrentFn is true it will also remove all instructions
|
||||||
// belonging to the current function.
|
// belonging to the current function.
|
||||||
func removeInlinedCalls(dbp Process, pcs []uint64, topframe Stackframe) ([]uint64, error) {
|
func removeInlinedCalls(dbp Process, pcs []uint64, topframe Stackframe) ([]uint64, error) {
|
||||||
bi := dbp.BinInfo()
|
image := topframe.Call.Fn.cu.image
|
||||||
irdr := reader.InlineStack(bi.dwarf, topframe.Call.Fn.offset, 0)
|
dwarf := image.dwarf
|
||||||
|
irdr := reader.InlineStack(dwarf, topframe.Call.Fn.offset, 0)
|
||||||
for irdr.Next() {
|
for irdr.Next() {
|
||||||
e := irdr.Entry()
|
e := irdr.Entry()
|
||||||
if e.Offset == topframe.Call.Fn.offset {
|
if e.Offset == topframe.Call.Fn.offset {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
ranges, err := bi.dwarf.Ranges(e)
|
ranges, err := dwarf.Ranges(e)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return pcs, err
|
return pcs, err
|
||||||
}
|
}
|
||||||
for _, rng := range ranges {
|
for _, rng := range ranges {
|
||||||
pcs = removePCsBetween(pcs, rng[0], rng[1], bi.staticBase)
|
pcs = removePCsBetween(pcs, rng[0], rng[1], image.StaticBase)
|
||||||
}
|
}
|
||||||
irdr.SkipChildren()
|
irdr.SkipChildren()
|
||||||
}
|
}
|
||||||
|
@ -56,11 +56,12 @@ const (
|
|||||||
|
|
||||||
// Do not call this function directly it isn't able to deal correctly with package paths
|
// Do not call this function directly it isn't able to deal correctly with package paths
|
||||||
func (bi *BinaryInfo) findType(name string) (godwarf.Type, error) {
|
func (bi *BinaryInfo) findType(name string) (godwarf.Type, error) {
|
||||||
off, found := bi.types[name]
|
ref, found := bi.types[name]
|
||||||
if !found {
|
if !found {
|
||||||
return nil, reader.TypeNotFoundErr
|
return nil, reader.TypeNotFoundErr
|
||||||
}
|
}
|
||||||
return godwarf.ReadType(bi.dwarf, off, bi.typeCache)
|
image := bi.Images[ref.imageIndex]
|
||||||
|
return godwarf.ReadType(image.dwarf, ref.imageIndex, ref.offset, image.typeCache)
|
||||||
}
|
}
|
||||||
|
|
||||||
func pointerTo(typ godwarf.Type, arch Arch) godwarf.Type {
|
func pointerTo(typ godwarf.Type, arch Arch) godwarf.Type {
|
||||||
@ -76,7 +77,6 @@ func pointerTo(typ godwarf.Type, arch Arch) godwarf.Type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (bi *BinaryInfo) findTypeExpr(expr ast.Expr) (godwarf.Type, error) {
|
func (bi *BinaryInfo) findTypeExpr(expr ast.Expr) (godwarf.Type, error) {
|
||||||
bi.loadPackageMap()
|
|
||||||
if lit, islit := expr.(*ast.BasicLit); islit && lit.Kind == token.STRING {
|
if lit, islit := expr.(*ast.BasicLit); islit && lit.Kind == token.STRING {
|
||||||
// Allow users to specify type names verbatim as quoted
|
// Allow users to specify type names verbatim as quoted
|
||||||
// string. Useful as a catch-all workaround for cases where we don't
|
// string. Useful as a catch-all workaround for cases where we don't
|
||||||
@ -135,39 +135,27 @@ func complexType(typename string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bi *BinaryInfo) loadPackageMap() error {
|
func (bi *BinaryInfo) registerTypeToPackageMap(entry *dwarf.Entry) {
|
||||||
if bi.packageMap != nil {
|
if entry.Tag != dwarf.TagTypedef && entry.Tag != dwarf.TagBaseType && entry.Tag != dwarf.TagClassType && entry.Tag != dwarf.TagStructType {
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
bi.packageMap = map[string]string{}
|
|
||||||
reader := bi.DwarfReader()
|
|
||||||
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if entry.Tag != dwarf.TagTypedef && entry.Tag != dwarf.TagBaseType && entry.Tag != dwarf.TagClassType && entry.Tag != dwarf.TagStructType {
|
typename, ok := entry.Val(dwarf.AttrName).(string)
|
||||||
continue
|
if !ok || complexType(typename) {
|
||||||
}
|
return
|
||||||
|
|
||||||
typename, ok := entry.Val(dwarf.AttrName).(string)
|
|
||||||
if !ok || complexType(typename) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
dot := strings.LastIndex(typename, ".")
|
|
||||||
if dot < 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
path := typename[:dot]
|
|
||||||
slash := strings.LastIndex(path, "/")
|
|
||||||
if slash < 0 || slash+1 >= len(path) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
name := path[slash+1:]
|
|
||||||
bi.packageMap[name] = path
|
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
|
dot := strings.LastIndex(typename, ".")
|
||||||
|
if dot < 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
path := typename[:dot]
|
||||||
|
slash := strings.LastIndex(path, "/")
|
||||||
|
if slash < 0 || slash+1 >= len(path) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
name := path[slash+1:]
|
||||||
|
bi.packageMap[name] = path
|
||||||
}
|
}
|
||||||
|
|
||||||
type functionsDebugInfoByEntry []Function
|
type functionsDebugInfoByEntry []Function
|
||||||
@ -188,7 +176,7 @@ func (v packageVarsByAddr) Len() int { return len(v) }
|
|||||||
func (v packageVarsByAddr) Less(i int, j int) bool { return v[i].addr < v[j].addr }
|
func (v packageVarsByAddr) Less(i int, j int) bool { return v[i].addr < v[j].addr }
|
||||||
func (v packageVarsByAddr) Swap(i int, j int) { v[i], v[j] = v[j], v[i] }
|
func (v packageVarsByAddr) Swap(i int, j int) { v[i], v[j] = v[j], v[i] }
|
||||||
|
|
||||||
func (bi *BinaryInfo) loadDebugInfoMaps(debugLineBytes []byte, wg *sync.WaitGroup, cont func()) {
|
func (bi *BinaryInfo) loadDebugInfoMaps(image *Image, debugLineBytes []byte, wg *sync.WaitGroup, cont func()) {
|
||||||
if wg != nil {
|
if wg != nil {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
}
|
}
|
||||||
@ -198,16 +186,23 @@ func (bi *BinaryInfo) loadDebugInfoMaps(debugLineBytes []byte, wg *sync.WaitGrou
|
|||||||
var partialUnits = make(map[dwarf.Offset]*partialUnit)
|
var partialUnits = make(map[dwarf.Offset]*partialUnit)
|
||||||
var lastOffset dwarf.Offset
|
var lastOffset dwarf.Offset
|
||||||
|
|
||||||
bi.types = make(map[string]dwarf.Offset)
|
if !bi.initialized {
|
||||||
bi.packageVars = []packageVar{}
|
bi.types = make(map[string]dwarfRef)
|
||||||
bi.Functions = []Function{}
|
bi.consts = make(map[dwarfRef]*constantType)
|
||||||
bi.compileUnits = []*compileUnit{}
|
bi.packageMap = make(map[string]string)
|
||||||
bi.consts = make(map[dwarf.Offset]*constantType)
|
bi.initialized = true
|
||||||
bi.runtimeTypeToDIE = make(map[uint64]runtimeTypeDIE)
|
}
|
||||||
reader := bi.DwarfReader()
|
image.runtimeTypeToDIE = make(map[uint64]runtimeTypeDIE)
|
||||||
ardr := bi.DwarfReader()
|
|
||||||
|
reader := image.DwarfReader()
|
||||||
|
ardr := image.DwarfReader()
|
||||||
abstractOriginNameTable := make(map[dwarf.Offset]string)
|
abstractOriginNameTable := make(map[dwarf.Offset]string)
|
||||||
|
|
||||||
|
knownPackageVars := map[string]struct{}{}
|
||||||
|
for _, v := range bi.packageVars {
|
||||||
|
knownPackageVars[v.name] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
outer:
|
outer:
|
||||||
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -224,6 +219,7 @@ outer:
|
|||||||
cu.endOffset = entry.Offset
|
cu.endOffset = entry.Offset
|
||||||
}
|
}
|
||||||
cu = &compileUnit{}
|
cu = &compileUnit{}
|
||||||
|
cu.image = image
|
||||||
cu.entry = entry
|
cu.entry = entry
|
||||||
cu.startOffset = entry.Offset
|
cu.startOffset = entry.Offset
|
||||||
if lang, _ := entry.Val(dwarf.AttrLanguage).(int64); lang == dwarfGoLanguage {
|
if lang, _ := entry.Val(dwarf.AttrLanguage).(int64); lang == dwarfGoLanguage {
|
||||||
@ -234,10 +230,10 @@ outer:
|
|||||||
if compdir != "" {
|
if compdir != "" {
|
||||||
cu.name = filepath.Join(compdir, cu.name)
|
cu.name = filepath.Join(compdir, cu.name)
|
||||||
}
|
}
|
||||||
cu.ranges, _ = bi.dwarf.Ranges(entry)
|
cu.ranges, _ = image.dwarf.Ranges(entry)
|
||||||
for i := range cu.ranges {
|
for i := range cu.ranges {
|
||||||
cu.ranges[i][0] += bi.staticBase
|
cu.ranges[i][0] += image.StaticBase
|
||||||
cu.ranges[i][1] += bi.staticBase
|
cu.ranges[i][1] += image.StaticBase
|
||||||
}
|
}
|
||||||
if len(cu.ranges) >= 1 {
|
if len(cu.ranges) >= 1 {
|
||||||
cu.lowPC = cu.ranges[0][0]
|
cu.lowPC = cu.ranges[0][0]
|
||||||
@ -252,7 +248,7 @@ outer:
|
|||||||
logger.Printf(fmt, args)
|
logger.Printf(fmt, args)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cu.lineInfo = line.Parse(compdir, bytes.NewBuffer(debugLineBytes[lineInfoOffset:]), logfn, bi.staticBase)
|
cu.lineInfo = line.Parse(compdir, bytes.NewBuffer(debugLineBytes[lineInfoOffset:]), logfn, image.StaticBase)
|
||||||
}
|
}
|
||||||
cu.producer, _ = entry.Val(dwarf.AttrProducer).(string)
|
cu.producer, _ = entry.Val(dwarf.AttrProducer).(string)
|
||||||
if cu.isgo && cu.producer != "" {
|
if cu.isgo && cu.producer != "" {
|
||||||
@ -284,7 +280,7 @@ outer:
|
|||||||
if !cu.isgo {
|
if !cu.isgo {
|
||||||
name = "C." + name
|
name = "C." + name
|
||||||
}
|
}
|
||||||
bi.types[name] = offset
|
bi.types[name] = dwarfRef{image.index, offset}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,10 +288,14 @@ outer:
|
|||||||
if pu != nil {
|
if pu != nil {
|
||||||
pu.variables = append(pu.variables, pVar)
|
pu.variables = append(pu.variables, pVar)
|
||||||
} else {
|
} else {
|
||||||
|
pVar2 := pVar
|
||||||
if !cu.isgo {
|
if !cu.isgo {
|
||||||
pVar.name = "C." + pVar.name
|
pVar2.name = "C." + pVar2.name
|
||||||
|
}
|
||||||
|
pVar2.cu = cu
|
||||||
|
if _, known := knownPackageVars[pVar2.name]; !known {
|
||||||
|
bi.packageVars = append(bi.packageVars, pVar2)
|
||||||
}
|
}
|
||||||
bi.packageVars = append(bi.packageVars, pVar)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -306,10 +306,10 @@ outer:
|
|||||||
if !cu.isgo {
|
if !cu.isgo {
|
||||||
pCt.name = "C." + pCt.name
|
pCt.name = "C." + pCt.name
|
||||||
}
|
}
|
||||||
ct := bi.consts[pCt.typ]
|
ct := bi.consts[dwarfRef{image.index, pCt.typ}]
|
||||||
if ct == nil {
|
if ct == nil {
|
||||||
ct = &constantType{}
|
ct = &constantType{}
|
||||||
bi.consts[pCt.typ] = ct
|
bi.consts[dwarfRef{image.index, pCt.typ}] = ct
|
||||||
}
|
}
|
||||||
ct.values = append(ct.values, constantValue{name: pCt.name, fullName: pCt.name, value: pCt.value})
|
ct.values = append(ct.values, constantValue{name: pCt.name, fullName: pCt.name, value: pCt.value})
|
||||||
}
|
}
|
||||||
@ -339,11 +339,14 @@ outer:
|
|||||||
name = "C." + name
|
name = "C." + name
|
||||||
}
|
}
|
||||||
if _, exists := bi.types[name]; !exists {
|
if _, exists := bi.types[name]; !exists {
|
||||||
bi.types[name] = entry.Offset
|
bi.types[name] = dwarfRef{image.index, entry.Offset}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bi.registerRuntimeTypeToDIE(entry, ardr)
|
if cu != nil && cu.isgo {
|
||||||
|
bi.registerTypeToPackageMap(entry)
|
||||||
|
}
|
||||||
|
image.registerRuntimeTypeToDIE(entry, ardr)
|
||||||
reader.SkipChildren()
|
reader.SkipChildren()
|
||||||
|
|
||||||
case dwarf.TagVariable:
|
case dwarf.TagVariable:
|
||||||
@ -355,12 +358,14 @@ outer:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if pu != nil {
|
if pu != nil {
|
||||||
pu.variables = append(pu.variables, packageVar{n, entry.Offset, addr + bi.staticBase})
|
pu.variables = append(pu.variables, packageVar{n, nil, entry.Offset, addr + image.StaticBase})
|
||||||
} else {
|
} else {
|
||||||
if !cu.isgo {
|
if !cu.isgo {
|
||||||
n = "C." + n
|
n = "C." + n
|
||||||
}
|
}
|
||||||
bi.packageVars = append(bi.packageVars, packageVar{n, entry.Offset, addr + bi.staticBase})
|
if _, known := knownPackageVars[n]; !known {
|
||||||
|
bi.packageVars = append(bi.packageVars, packageVar{n, cu, entry.Offset, addr + image.StaticBase})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -375,10 +380,10 @@ outer:
|
|||||||
if !cu.isgo {
|
if !cu.isgo {
|
||||||
name = "C." + name
|
name = "C." + name
|
||||||
}
|
}
|
||||||
ct := bi.consts[typ]
|
ct := bi.consts[dwarfRef{image.index, typ}]
|
||||||
if ct == nil {
|
if ct == nil {
|
||||||
ct = &constantType{}
|
ct = &constantType{}
|
||||||
bi.consts[typ] = ct
|
bi.consts[dwarfRef{image.index, typ}] = ct
|
||||||
}
|
}
|
||||||
ct.values = append(ct.values, constantValue{name: name, fullName: name, value: val})
|
ct.values = append(ct.values, constantValue{name: name, fullName: name, value: val})
|
||||||
}
|
}
|
||||||
@ -391,10 +396,10 @@ outer:
|
|||||||
if inval, ok := entry.Val(dwarf.AttrInline).(int64); ok {
|
if inval, ok := entry.Val(dwarf.AttrInline).(int64); ok {
|
||||||
inlined = inval == 1
|
inlined = inval == 1
|
||||||
}
|
}
|
||||||
if ranges, _ := bi.dwarf.Ranges(entry); len(ranges) == 1 {
|
if ranges, _ := image.dwarf.Ranges(entry); len(ranges) == 1 {
|
||||||
ok1 = true
|
ok1 = true
|
||||||
lowpc = ranges[0][0] + bi.staticBase
|
lowpc = ranges[0][0] + image.StaticBase
|
||||||
highpc = ranges[0][1] + bi.staticBase
|
highpc = ranges[0][1] + image.StaticBase
|
||||||
}
|
}
|
||||||
name, ok2 := entry.Val(dwarf.AttrName).(string)
|
name, ok2 := entry.Val(dwarf.AttrName).(string)
|
||||||
if !ok2 {
|
if !ok2 {
|
||||||
@ -442,7 +447,7 @@ outer:
|
|||||||
if entry.Tag == dwarf.TagInlinedSubroutine {
|
if entry.Tag == dwarf.TagInlinedSubroutine {
|
||||||
originOffset := entry.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
|
originOffset := entry.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
|
||||||
name := abstractOriginNameTable[originOffset]
|
name := abstractOriginNameTable[originOffset]
|
||||||
if ranges, _ := bi.dwarf.Ranges(entry); len(ranges) == 1 {
|
if ranges, _ := image.dwarf.Ranges(entry); len(ranges) == 1 {
|
||||||
ok1 = true
|
ok1 = true
|
||||||
lowpc = ranges[0][0]
|
lowpc = ranges[0][0]
|
||||||
highpc = ranges[0][1]
|
highpc = ranges[0][1]
|
||||||
@ -453,8 +458,8 @@ outer:
|
|||||||
callfile := cu.lineInfo.FileNames[callfileidx-1].Path
|
callfile := cu.lineInfo.FileNames[callfileidx-1].Path
|
||||||
cu.concreteInlinedFns = append(cu.concreteInlinedFns, inlinedFn{
|
cu.concreteInlinedFns = append(cu.concreteInlinedFns, inlinedFn{
|
||||||
Name: name,
|
Name: name,
|
||||||
LowPC: lowpc + bi.staticBase,
|
LowPC: lowpc + image.StaticBase,
|
||||||
HighPC: highpc + bi.staticBase,
|
HighPC: highpc + image.StaticBase,
|
||||||
CallFile: callfile,
|
CallFile: callfile,
|
||||||
CallLine: callline,
|
CallLine: callline,
|
||||||
Parent: &fn,
|
Parent: &fn,
|
||||||
@ -547,10 +552,10 @@ func (bi *BinaryInfo) expandPackagesInType(expr ast.Expr) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bi *BinaryInfo) registerRuntimeTypeToDIE(entry *dwarf.Entry, ardr *reader.Reader) {
|
func (image *Image) registerRuntimeTypeToDIE(entry *dwarf.Entry, ardr *reader.Reader) {
|
||||||
if off, ok := entry.Val(godwarf.AttrGoRuntimeType).(uint64); ok {
|
if off, ok := entry.Val(godwarf.AttrGoRuntimeType).(uint64); ok {
|
||||||
if _, ok := bi.runtimeTypeToDIE[off]; !ok {
|
if _, ok := image.runtimeTypeToDIE[off]; !ok {
|
||||||
bi.runtimeTypeToDIE[off+bi.staticBase] = runtimeTypeDIE{entry.Offset, -1}
|
image.runtimeTypeToDIE[off+image.StaticBase] = runtimeTypeDIE{entry.Offset, -1}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -571,13 +576,16 @@ func runtimeTypeToDIE(_type *Variable, dataAddr uintptr) (typ godwarf.Type, kind
|
|||||||
|
|
||||||
// go 1.11 implementation: use extended attribute in debug_info
|
// go 1.11 implementation: use extended attribute in debug_info
|
||||||
|
|
||||||
md, err := findModuleDataForType(bi, _type.Addr, _type.mem)
|
mds, err := loadModuleData(bi, _type.mem)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, fmt.Errorf("error loading module data: %v", err)
|
return nil, 0, fmt.Errorf("error loading module data: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
md := findModuleDataForType(bi, mds, _type.Addr, _type.mem)
|
||||||
if md != nil {
|
if md != nil {
|
||||||
if rtdie, ok := bi.runtimeTypeToDIE[uint64(_type.Addr-md.types)]; ok {
|
so := bi.moduleDataToImage(md)
|
||||||
typ, err := godwarf.ReadType(bi.dwarf, rtdie.offset, bi.typeCache)
|
if rtdie, ok := so.runtimeTypeToDIE[uint64(_type.Addr-md.types)]; ok {
|
||||||
|
typ, err := godwarf.ReadType(so.dwarf, so.index, rtdie.offset, so.typeCache)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, fmt.Errorf("invalid interface type: %v", err)
|
return nil, 0, fmt.Errorf("invalid interface type: %v", err)
|
||||||
}
|
}
|
||||||
@ -592,7 +600,7 @@ func runtimeTypeToDIE(_type *Variable, dataAddr uintptr) (typ godwarf.Type, kind
|
|||||||
|
|
||||||
// go1.7 to go1.10 implementation: convert runtime._type structs to type names
|
// go1.7 to go1.10 implementation: convert runtime._type structs to type names
|
||||||
|
|
||||||
typename, kind, err := nameOfRuntimeType(_type)
|
typename, kind, err := nameOfRuntimeType(mds, _type)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, fmt.Errorf("invalid interface type: %v", err)
|
return nil, 0, fmt.Errorf("invalid interface type: %v", err)
|
||||||
}
|
}
|
||||||
@ -613,7 +621,7 @@ type nameOfRuntimeTypeEntry struct {
|
|||||||
// Returns the type name of the type described in _type.
|
// Returns the type name of the type described in _type.
|
||||||
// _type is a non-loaded Variable pointing to runtime._type struct in the target.
|
// _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
|
// The returned string is in the format that's used in DWARF data
|
||||||
func nameOfRuntimeType(_type *Variable) (typename string, kind int64, err error) {
|
func nameOfRuntimeType(mds []moduleData, _type *Variable) (typename string, kind int64, err error) {
|
||||||
if e, ok := _type.bi.nameOfRuntimeType[_type.Addr]; ok {
|
if e, ok := _type.bi.nameOfRuntimeType[_type.Addr]; ok {
|
||||||
return e.typename, e.kind, nil
|
return e.typename, e.kind, nil
|
||||||
}
|
}
|
||||||
@ -630,11 +638,17 @@ func nameOfRuntimeType(_type *Variable) (typename string, kind int64, err error)
|
|||||||
// Named types are defined by a 'type' expression, everything else
|
// Named types are defined by a 'type' expression, everything else
|
||||||
// (for example pointers to named types) are not considered named.
|
// (for example pointers to named types) are not considered named.
|
||||||
if tflag&tflagNamed != 0 {
|
if tflag&tflagNamed != 0 {
|
||||||
typename, err = nameOfNamedRuntimeType(_type, kind, tflag)
|
typename, err = nameOfNamedRuntimeType(mds, _type, kind, tflag)
|
||||||
|
if err == nil {
|
||||||
|
_type.bi.nameOfRuntimeType[_type.Addr] = nameOfRuntimeTypeEntry{typename: typename, kind: kind}
|
||||||
|
}
|
||||||
return typename, kind, err
|
return typename, kind, err
|
||||||
}
|
}
|
||||||
|
|
||||||
typename, err = nameOfUnnamedRuntimeType(_type, kind, tflag)
|
typename, err = nameOfUnnamedRuntimeType(mds, _type, kind, tflag)
|
||||||
|
if err == nil {
|
||||||
|
_type.bi.nameOfRuntimeType[_type.Addr] = nameOfRuntimeTypeEntry{typename: typename, kind: kind}
|
||||||
|
}
|
||||||
return typename, kind, err
|
return typename, kind, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -654,7 +668,7 @@ func nameOfRuntimeType(_type *Variable) (typename string, kind int64, err error)
|
|||||||
// to a struct specific to the type's kind (for example, if the type
|
// 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
|
// being described is a slice type the variable will be specialized
|
||||||
// to a runtime.slicetype).
|
// to a runtime.slicetype).
|
||||||
func nameOfNamedRuntimeType(_type *Variable, kind, tflag int64) (typename string, err error) {
|
func nameOfNamedRuntimeType(mds []moduleData, _type *Variable, kind, tflag int64) (typename string, err error) {
|
||||||
var strOff int64
|
var strOff int64
|
||||||
if strField := _type.loadFieldNamed("str"); strField != nil && strField.Value != nil {
|
if strField := _type.loadFieldNamed("str"); strField != nil && strField.Value != nil {
|
||||||
strOff, _ = constant.Int64Val(strField.Value)
|
strOff, _ = constant.Int64Val(strField.Value)
|
||||||
@ -666,7 +680,7 @@ func nameOfNamedRuntimeType(_type *Variable, kind, tflag int64) (typename string
|
|||||||
// For a description of how memory is organized for type names read
|
// For a description of how memory is organized for type names read
|
||||||
// the comment to 'type name struct' in $GOROOT/src/reflect/type.go
|
// the comment to 'type name struct' in $GOROOT/src/reflect/type.go
|
||||||
|
|
||||||
typename, _, _, err = resolveNameOff(_type.bi, _type.Addr, uintptr(strOff), _type.mem)
|
typename, _, _, err = resolveNameOff(_type.bi, mds, _type.Addr, uintptr(strOff), _type.mem)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -692,7 +706,7 @@ func nameOfNamedRuntimeType(_type *Variable, kind, tflag int64) (typename string
|
|||||||
if ut := uncommon(_type, tflag); ut != nil {
|
if ut := uncommon(_type, tflag); ut != nil {
|
||||||
if pkgPathField := ut.loadFieldNamed("pkgpath"); pkgPathField != nil && pkgPathField.Value != nil {
|
if pkgPathField := ut.loadFieldNamed("pkgpath"); pkgPathField != nil && pkgPathField.Value != nil {
|
||||||
pkgPathOff, _ := constant.Int64Val(pkgPathField.Value)
|
pkgPathOff, _ := constant.Int64Val(pkgPathField.Value)
|
||||||
pkgPath, _, _, err := resolveNameOff(_type.bi, _type.Addr, uintptr(pkgPathOff), _type.mem)
|
pkgPath, _, _, err := resolveNameOff(_type.bi, mds, _type.Addr, uintptr(pkgPathOff), _type.mem)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -709,7 +723,7 @@ func nameOfNamedRuntimeType(_type *Variable, kind, tflag int64) (typename string
|
|||||||
return typename, nil
|
return typename, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func nameOfUnnamedRuntimeType(_type *Variable, kind, tflag int64) (string, error) {
|
func nameOfUnnamedRuntimeType(mds []moduleData, _type *Variable, kind, tflag int64) (string, error) {
|
||||||
_type, err := specificRuntimeType(_type, kind)
|
_type, err := specificRuntimeType(_type, kind)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@ -722,47 +736,47 @@ func nameOfUnnamedRuntimeType(_type *Variable, kind, tflag int64) (string, error
|
|||||||
if lenField := _type.loadFieldNamed("len"); lenField != nil && lenField.Value != nil {
|
if lenField := _type.loadFieldNamed("len"); lenField != nil && lenField.Value != nil {
|
||||||
len, _ = constant.Int64Val(lenField.Value)
|
len, _ = constant.Int64Val(lenField.Value)
|
||||||
}
|
}
|
||||||
elemname, err := fieldToType(_type, "elem")
|
elemname, err := fieldToType(mds, _type, "elem")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("[%d]%s", len, elemname), nil
|
return fmt.Sprintf("[%d]%s", len, elemname), nil
|
||||||
case reflect.Chan:
|
case reflect.Chan:
|
||||||
elemname, err := fieldToType(_type, "elem")
|
elemname, err := fieldToType(mds, _type, "elem")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return "chan " + elemname, nil
|
return "chan " + elemname, nil
|
||||||
case reflect.Func:
|
case reflect.Func:
|
||||||
return nameOfFuncRuntimeType(_type, tflag, true)
|
return nameOfFuncRuntimeType(mds, _type, tflag, true)
|
||||||
case reflect.Interface:
|
case reflect.Interface:
|
||||||
return nameOfInterfaceRuntimeType(_type, kind, tflag)
|
return nameOfInterfaceRuntimeType(mds, _type, kind, tflag)
|
||||||
case reflect.Map:
|
case reflect.Map:
|
||||||
keyname, err := fieldToType(_type, "key")
|
keyname, err := fieldToType(mds, _type, "key")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
elemname, err := fieldToType(_type, "elem")
|
elemname, err := fieldToType(mds, _type, "elem")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return "map[" + keyname + "]" + elemname, nil
|
return "map[" + keyname + "]" + elemname, nil
|
||||||
case reflect.Ptr:
|
case reflect.Ptr:
|
||||||
elemname, err := fieldToType(_type, "elem")
|
elemname, err := fieldToType(mds, _type, "elem")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return "*" + elemname, nil
|
return "*" + elemname, nil
|
||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
elemname, err := fieldToType(_type, "elem")
|
elemname, err := fieldToType(mds, _type, "elem")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return "[]" + elemname, nil
|
return "[]" + elemname, nil
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
return nameOfStructRuntimeType(_type, kind, tflag)
|
return nameOfStructRuntimeType(mds, _type, kind, tflag)
|
||||||
default:
|
default:
|
||||||
return nameOfNamedRuntimeType(_type, kind, tflag)
|
return nameOfNamedRuntimeType(mds, _type, kind, tflag)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -770,7 +784,7 @@ func nameOfUnnamedRuntimeType(_type *Variable, kind, tflag int64) (string, error
|
|||||||
// A runtime.functype is followed by a runtime.uncommontype
|
// A runtime.functype is followed by a runtime.uncommontype
|
||||||
// (optional) and then by an array of pointers to runtime._type,
|
// (optional) and then by an array of pointers to runtime._type,
|
||||||
// one for each input and output argument.
|
// one for each input and output argument.
|
||||||
func nameOfFuncRuntimeType(_type *Variable, tflag int64, anonymous bool) (string, error) {
|
func nameOfFuncRuntimeType(mds []moduleData, _type *Variable, tflag int64, anonymous bool) (string, error) {
|
||||||
rtyp, err := _type.bi.findType("runtime._type")
|
rtyp, err := _type.bi.findType("runtime._type")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@ -803,7 +817,7 @@ func nameOfFuncRuntimeType(_type *Variable, tflag int64, anonymous bool) (string
|
|||||||
for i := int64(0); i < inCount; i++ {
|
for i := int64(0); i < inCount; i++ {
|
||||||
argtype := cursortyp.maybeDereference()
|
argtype := cursortyp.maybeDereference()
|
||||||
cursortyp.Addr += uintptr(_type.bi.Arch.PtrSize())
|
cursortyp.Addr += uintptr(_type.bi.Arch.PtrSize())
|
||||||
argtypename, _, err := nameOfRuntimeType(argtype)
|
argtypename, _, err := nameOfRuntimeType(mds, argtype)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -820,7 +834,7 @@ func nameOfFuncRuntimeType(_type *Variable, tflag int64, anonymous bool) (string
|
|||||||
case 1:
|
case 1:
|
||||||
buf.WriteString(" ")
|
buf.WriteString(" ")
|
||||||
argtype := cursortyp.maybeDereference()
|
argtype := cursortyp.maybeDereference()
|
||||||
argtypename, _, err := nameOfRuntimeType(argtype)
|
argtypename, _, err := nameOfRuntimeType(mds, argtype)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -830,7 +844,7 @@ func nameOfFuncRuntimeType(_type *Variable, tflag int64, anonymous bool) (string
|
|||||||
for i := int64(0); i < outCount; i++ {
|
for i := int64(0); i < outCount; i++ {
|
||||||
argtype := cursortyp.maybeDereference()
|
argtype := cursortyp.maybeDereference()
|
||||||
cursortyp.Addr += uintptr(_type.bi.Arch.PtrSize())
|
cursortyp.Addr += uintptr(_type.bi.Arch.PtrSize())
|
||||||
argtypename, _, err := nameOfRuntimeType(argtype)
|
argtypename, _, err := nameOfRuntimeType(mds, argtype)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -844,7 +858,7 @@ func nameOfFuncRuntimeType(_type *Variable, tflag int64, anonymous bool) (string
|
|||||||
return buf.String(), nil
|
return buf.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func nameOfInterfaceRuntimeType(_type *Variable, kind, tflag int64) (string, error) {
|
func nameOfInterfaceRuntimeType(mds []moduleData, _type *Variable, kind, tflag int64) (string, error) {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
buf.WriteString("interface {")
|
buf.WriteString("interface {")
|
||||||
|
|
||||||
@ -867,14 +881,14 @@ func nameOfInterfaceRuntimeType(_type *Variable, kind, tflag int64) (string, err
|
|||||||
case imethodFieldName:
|
case imethodFieldName:
|
||||||
nameoff, _ := constant.Int64Val(im.Children[i].Value)
|
nameoff, _ := constant.Int64Val(im.Children[i].Value)
|
||||||
var err error
|
var err error
|
||||||
methodname, _, _, err = resolveNameOff(_type.bi, _type.Addr, uintptr(nameoff), _type.mem)
|
methodname, _, _, err = resolveNameOff(_type.bi, mds, _type.Addr, uintptr(nameoff), _type.mem)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
case imethodFieldItyp:
|
case imethodFieldItyp:
|
||||||
typeoff, _ := constant.Int64Val(im.Children[i].Value)
|
typeoff, _ := constant.Int64Val(im.Children[i].Value)
|
||||||
typ, err := resolveTypeOff(_type.bi, _type.Addr, uintptr(typeoff), _type.mem)
|
typ, err := resolveTypeOff(_type.bi, mds, _type.Addr, uintptr(typeoff), _type.mem)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -886,7 +900,7 @@ func nameOfInterfaceRuntimeType(_type *Variable, kind, tflag int64) (string, err
|
|||||||
if tflagField := typ.loadFieldNamed("tflag"); tflagField != nil && tflagField.Value != nil {
|
if tflagField := typ.loadFieldNamed("tflag"); tflagField != nil && tflagField.Value != nil {
|
||||||
tflag, _ = constant.Int64Val(tflagField.Value)
|
tflag, _ = constant.Int64Val(tflagField.Value)
|
||||||
}
|
}
|
||||||
methodtype, err = nameOfFuncRuntimeType(typ, tflag, false)
|
methodtype, err = nameOfFuncRuntimeType(mds, typ, tflag, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -905,7 +919,7 @@ func nameOfInterfaceRuntimeType(_type *Variable, kind, tflag int64) (string, err
|
|||||||
return buf.String(), nil
|
return buf.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func nameOfStructRuntimeType(_type *Variable, kind, tflag int64) (string, error) {
|
func nameOfStructRuntimeType(mds []moduleData, _type *Variable, kind, tflag int64) (string, error) {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
buf.WriteString("struct {")
|
buf.WriteString("struct {")
|
||||||
|
|
||||||
@ -945,7 +959,7 @@ func nameOfStructRuntimeType(_type *Variable, kind, tflag int64) (string, error)
|
|||||||
case "typ":
|
case "typ":
|
||||||
typeField = field.Children[i].maybeDereference()
|
typeField = field.Children[i].maybeDereference()
|
||||||
var err error
|
var err error
|
||||||
fieldtypename, _, err = nameOfRuntimeType(typeField)
|
fieldtypename, _, err = nameOfRuntimeType(mds, typeField)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@ -979,13 +993,13 @@ func nameOfStructRuntimeType(_type *Variable, kind, tflag int64) (string, error)
|
|||||||
return buf.String(), nil
|
return buf.String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func fieldToType(_type *Variable, fieldName string) (string, error) {
|
func fieldToType(mds []moduleData, _type *Variable, fieldName string) (string, error) {
|
||||||
typeField, err := _type.structMember(fieldName)
|
typeField, err := _type.structMember(fieldName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
typeField = typeField.maybeDereference()
|
typeField = typeField.maybeDereference()
|
||||||
typename, _, err := nameOfRuntimeType(typeField)
|
typename, _, err := nameOfRuntimeType(mds, typeField)
|
||||||
return typename, err
|
return typename, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1224,7 +1238,8 @@ func constructTypeForKind(kind int64, bi *BinaryInfo) (*godwarf.StructType, erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
rdr := bi.DwarfReader()
|
so := bi.typeToImage(typ)
|
||||||
|
rdr := so.DwarfReader()
|
||||||
rdr.Seek(typ.Common().Offset)
|
rdr.Seek(typ.Common().Offset)
|
||||||
e, err := rdr.Next()
|
e, err := rdr.Next()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1235,13 +1250,21 @@ func dwarfToRuntimeType(bi *BinaryInfo, mem MemoryReadWriter, typ godwarf.Type)
|
|||||||
return 0, 0, false, nil
|
return 0, 0, false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := loadModuleData(bi, mem); err != nil {
|
mds, err := loadModuleData(bi, mem)
|
||||||
|
if err != nil {
|
||||||
return 0, 0, false, err
|
return 0, 0, false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO(aarzilli): when we support plugins this should be the plugin
|
md := bi.imageToModuleData(so, mds)
|
||||||
//corresponding to the shared object containing entry 'e'.
|
if md == nil {
|
||||||
typeAddr = uint64(bi.moduleData[0].types) + off
|
if so.index > 0 {
|
||||||
|
return 0, 0, false, fmt.Errorf("could not find module data for type %s (shared object: %q)", typ, so.Path)
|
||||||
|
} else {
|
||||||
|
return 0, 0, false, fmt.Errorf("could not find module data for type %s", typ)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typeAddr = uint64(md.types) + off
|
||||||
|
|
||||||
rtyp, err := bi.findType("runtime._type")
|
rtyp, err := bi.findType("runtime._type")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -232,8 +232,8 @@ func (err *IsNilErr) Error() string {
|
|||||||
return fmt.Sprintf("%s is nil", err.name)
|
return fmt.Sprintf("%s is nil", err.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func globalScope(bi *BinaryInfo, mem MemoryReadWriter) *EvalScope {
|
func globalScope(bi *BinaryInfo, image *Image, mem MemoryReadWriter) *EvalScope {
|
||||||
return &EvalScope{Location: Location{}, Regs: op.DwarfRegisters{StaticBase: bi.staticBase}, Mem: mem, Gvar: nil, BinInfo: bi, frameOffset: 0}
|
return &EvalScope{Location: Location{}, Regs: op.DwarfRegisters{StaticBase: image.StaticBase}, Mem: mem, Gvar: nil, BinInfo: bi, frameOffset: 0}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (scope *EvalScope) newVariable(name string, addr uintptr, dwarfType godwarf.Type, mem MemoryReadWriter) *Variable {
|
func (scope *EvalScope) newVariable(name string, addr uintptr, dwarfType godwarf.Type, mem MemoryReadWriter) *Variable {
|
||||||
@ -434,10 +434,23 @@ func (v *Variable) toField(field *godwarf.StructField) (*Variable, error) {
|
|||||||
return v.newVariable(name, uintptr(int64(v.Addr)+field.ByteOffset), field.Type, v.mem), nil
|
return v.newVariable(name, uintptr(int64(v.Addr)+field.ByteOffset), field.Type, v.mem), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// image returns the image containing the current function.
|
||||||
|
func (scope *EvalScope) image() *Image {
|
||||||
|
return scope.BinInfo.funcToImage(scope.Fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// globalFor returns a global scope for 'image' with the register values of 'scope'.
|
||||||
|
func (scope *EvalScope) globalFor(image *Image) *EvalScope {
|
||||||
|
r := *scope
|
||||||
|
r.Regs.StaticBase = image.StaticBase
|
||||||
|
r.Fn = &Function{cu: &compileUnit{image: image}}
|
||||||
|
return &r
|
||||||
|
}
|
||||||
|
|
||||||
// DwarfReader returns the DwarfReader containing the
|
// DwarfReader returns the DwarfReader containing the
|
||||||
// Dwarf information for the target process.
|
// Dwarf information for the target process.
|
||||||
func (scope *EvalScope) DwarfReader() *reader.Reader {
|
func (scope *EvalScope) DwarfReader() *reader.Reader {
|
||||||
return scope.BinInfo.DwarfReader()
|
return scope.image().DwarfReader()
|
||||||
}
|
}
|
||||||
|
|
||||||
// PtrSize returns the size of a pointer.
|
// PtrSize returns the size of a pointer.
|
||||||
@ -630,7 +643,7 @@ var errTracebackAncestorsDisabled = errors.New("tracebackancestors is disabled")
|
|||||||
|
|
||||||
// Ancestors returns the list of ancestors for g.
|
// Ancestors returns the list of ancestors for g.
|
||||||
func (g *G) Ancestors(n int) ([]Ancestor, error) {
|
func (g *G) Ancestors(n int) ([]Ancestor, error) {
|
||||||
scope := globalScope(g.Thread.BinInfo(), g.Thread)
|
scope := globalScope(g.Thread.BinInfo(), g.Thread.BinInfo().Images[0], g.Thread)
|
||||||
tbav, err := scope.EvalExpression("runtime.debug.tracebackancestors", loadSingleValue)
|
tbav, err := scope.EvalExpression("runtime.debug.tracebackancestors", loadSingleValue)
|
||||||
if err == nil && tbav.Unreadable == nil && tbav.Kind == reflect.Int {
|
if err == nil && tbav.Unreadable == nil && tbav.Kind == reflect.Int {
|
||||||
tba, _ := constant.Int64Val(tbav.Value)
|
tba, _ := constant.Int64Val(tbav.Value)
|
||||||
@ -820,30 +833,35 @@ func filterVariables(vars []*Variable, pred func(v *Variable) bool) []*Variable
|
|||||||
// PackageVariables returns the name, value, and type of all package variables in the application.
|
// PackageVariables returns the name, value, and type of all package variables in the application.
|
||||||
func (scope *EvalScope) PackageVariables(cfg LoadConfig) ([]*Variable, error) {
|
func (scope *EvalScope) PackageVariables(cfg LoadConfig) ([]*Variable, error) {
|
||||||
var vars []*Variable
|
var vars []*Variable
|
||||||
reader := scope.DwarfReader()
|
for _, image := range scope.BinInfo.Images {
|
||||||
|
if image.loadErr != nil {
|
||||||
var utypoff dwarf.Offset
|
|
||||||
utypentry, err := reader.SeekToTypeNamed("<unspecified>")
|
|
||||||
if err == nil {
|
|
||||||
utypoff = utypentry.Offset
|
|
||||||
}
|
|
||||||
|
|
||||||
for entry, err := reader.NextPackageVariable(); entry != nil; entry, err = reader.NextPackageVariable() {
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if typoff, ok := entry.Val(dwarf.AttrType).(dwarf.Offset); !ok || typoff == utypoff {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
reader := reader.New(image.dwarf)
|
||||||
|
|
||||||
// Ignore errors trying to extract values
|
var utypoff dwarf.Offset
|
||||||
val, err := scope.extractVarInfoFromEntry(entry)
|
utypentry, err := reader.SeekToTypeNamed("<unspecified>")
|
||||||
if err != nil {
|
if err == nil {
|
||||||
continue
|
utypoff = utypentry.Offset
|
||||||
|
}
|
||||||
|
|
||||||
|
for entry, err := reader.NextPackageVariable(); entry != nil; entry, err = reader.NextPackageVariable() {
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if typoff, ok := entry.Val(dwarf.AttrType).(dwarf.Offset); !ok || typoff == utypoff {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore errors trying to extract values
|
||||||
|
val, err := scope.globalFor(image).extractVarInfoFromEntry(entry)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
val.loadValue(cfg)
|
||||||
|
vars = append(vars, val)
|
||||||
}
|
}
|
||||||
val.loadValue(cfg)
|
|
||||||
vars = append(vars, val)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return vars, nil
|
return vars, nil
|
||||||
@ -852,33 +870,33 @@ func (scope *EvalScope) PackageVariables(cfg LoadConfig) ([]*Variable, error) {
|
|||||||
func (scope *EvalScope) findGlobal(name string) (*Variable, error) {
|
func (scope *EvalScope) findGlobal(name string) (*Variable, error) {
|
||||||
for _, pkgvar := range scope.BinInfo.packageVars {
|
for _, pkgvar := range scope.BinInfo.packageVars {
|
||||||
if pkgvar.name == name || strings.HasSuffix(pkgvar.name, "/"+name) {
|
if pkgvar.name == name || strings.HasSuffix(pkgvar.name, "/"+name) {
|
||||||
reader := scope.DwarfReader()
|
reader := pkgvar.cu.image.dwarfReader
|
||||||
reader.Seek(pkgvar.offset)
|
reader.Seek(pkgvar.offset)
|
||||||
entry, err := reader.Next()
|
entry, err := reader.Next()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return scope.extractVarInfoFromEntry(entry)
|
return scope.globalFor(pkgvar.cu.image).extractVarInfoFromEntry(entry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for _, fn := range scope.BinInfo.Functions {
|
for _, fn := range scope.BinInfo.Functions {
|
||||||
if fn.Name == name || strings.HasSuffix(fn.Name, "/"+name) {
|
if fn.Name == name || strings.HasSuffix(fn.Name, "/"+name) {
|
||||||
//TODO(aarzilli): convert function entry into a function type?
|
//TODO(aarzilli): convert function entry into a function type?
|
||||||
r := scope.newVariable(fn.Name, uintptr(fn.Entry), &godwarf.FuncType{}, scope.Mem)
|
r := scope.globalFor(fn.cu.image).newVariable(fn.Name, uintptr(fn.Entry), &godwarf.FuncType{}, scope.Mem)
|
||||||
r.Value = constant.MakeString(fn.Name)
|
r.Value = constant.MakeString(fn.Name)
|
||||||
r.Base = uintptr(fn.Entry)
|
r.Base = uintptr(fn.Entry)
|
||||||
r.loaded = true
|
r.loaded = true
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for offset, ctyp := range scope.BinInfo.consts {
|
for dwref, ctyp := range scope.BinInfo.consts {
|
||||||
for _, cval := range ctyp.values {
|
for _, cval := range ctyp.values {
|
||||||
if cval.fullName == name || strings.HasSuffix(cval.fullName, "/"+name) {
|
if cval.fullName == name || strings.HasSuffix(cval.fullName, "/"+name) {
|
||||||
t, err := scope.BinInfo.Type(offset)
|
t, err := scope.BinInfo.Images[dwref.imageIndex].Type(dwref.offset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
v := scope.newVariable(name, 0x0, t, scope.Mem)
|
v := scope.globalFor(scope.BinInfo.Images[0]).newVariable(name, 0x0, t, scope.Mem)
|
||||||
switch v.Kind {
|
switch v.Kind {
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
v.Value = constant.MakeInt64(cval.value)
|
v.Value = constant.MakeInt64(cval.value)
|
||||||
@ -966,8 +984,8 @@ func (v *Variable) structMember(memberName string) (*Variable, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func readVarEntry(varEntry *dwarf.Entry, bi *BinaryInfo) (entry reader.Entry, name string, typ godwarf.Type, err error) {
|
func readVarEntry(varEntry *dwarf.Entry, image *Image) (entry reader.Entry, name string, typ godwarf.Type, err error) {
|
||||||
entry, _ = reader.LoadAbstractOrigin(varEntry, bi.dwarfReader)
|
entry, _ = reader.LoadAbstractOrigin(varEntry, image.dwarfReader)
|
||||||
|
|
||||||
name, ok := entry.Val(dwarf.AttrName).(string)
|
name, ok := entry.Val(dwarf.AttrName).(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -979,7 +997,7 @@ func readVarEntry(varEntry *dwarf.Entry, bi *BinaryInfo) (entry reader.Entry, na
|
|||||||
return nil, "", nil, fmt.Errorf("malformed variable DIE (offset)")
|
return nil, "", nil, fmt.Errorf("malformed variable DIE (offset)")
|
||||||
}
|
}
|
||||||
|
|
||||||
typ, err = bi.Type(offset)
|
typ, err = image.Type(offset)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", nil, err
|
return nil, "", nil, err
|
||||||
}
|
}
|
||||||
@ -998,7 +1016,7 @@ func (scope *EvalScope) extractVarInfoFromEntry(varEntry *dwarf.Entry) (*Variabl
|
|||||||
return nil, fmt.Errorf("invalid entry tag, only supports FormalParameter and Variable, got %s", varEntry.Tag.String())
|
return nil, fmt.Errorf("invalid entry tag, only supports FormalParameter and Variable, got %s", varEntry.Tag.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
entry, n, t, err := readVarEntry(varEntry, scope.BinInfo)
|
entry, n, t, err := readVarEntry(varEntry, scope.image())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -2139,7 +2157,7 @@ func popcnt(x uint64) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (cm constantsMap) Get(typ godwarf.Type) *constantType {
|
func (cm constantsMap) Get(typ godwarf.Type) *constantType {
|
||||||
ctyp := cm[typ.Common().Offset]
|
ctyp := cm[dwarfRef{typ.Common().Index, typ.Common().Offset}]
|
||||||
if ctyp == nil {
|
if ctyp == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -2211,7 +2229,7 @@ func (scope *EvalScope) Locals() ([]*Variable, error) {
|
|||||||
|
|
||||||
var vars []*Variable
|
var vars []*Variable
|
||||||
var depths []int
|
var depths []int
|
||||||
varReader := reader.Variables(scope.BinInfo.dwarf, scope.Fn.offset, reader.ToRelAddr(scope.PC, scope.BinInfo.staticBase), scope.Line, true)
|
varReader := reader.Variables(scope.image().dwarf, scope.Fn.offset, reader.ToRelAddr(scope.PC, scope.image().StaticBase), scope.Line, true)
|
||||||
hasScopes := false
|
hasScopes := false
|
||||||
for varReader.Next() {
|
for varReader.Next() {
|
||||||
entry := varReader.Entry()
|
entry := varReader.Entry()
|
||||||
|
@ -1636,7 +1636,7 @@ func libraries(t *Term, ctx callContext, args string) error {
|
|||||||
}
|
}
|
||||||
d := digits(len(libs))
|
d := digits(len(libs))
|
||||||
for i := range libs {
|
for i := range libs {
|
||||||
fmt.Printf("%"+strconv.Itoa(d)+"d. %s\n", i, libs[i].Path)
|
fmt.Printf("%"+strconv.Itoa(d)+"d. %#x %s\n", i, libs[i].Address, libs[i].Path)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -318,5 +318,5 @@ func ConvertCheckpoint(in proc.Checkpoint) (out Checkpoint) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ConvertImage(image *proc.Image) Image {
|
func ConvertImage(image *proc.Image) Image {
|
||||||
return Image{Path: image.Path}
|
return Image{Path: image.Path, Address: image.StaticBase}
|
||||||
}
|
}
|
||||||
|
@ -445,7 +445,8 @@ type Checkpoint struct {
|
|||||||
|
|
||||||
// Image represents a loaded shared object (go plugin or shared library)
|
// Image represents a loaded shared object (go plugin or shared library)
|
||||||
type Image struct {
|
type Image struct {
|
||||||
Path string
|
Path string
|
||||||
|
Address uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ancestor represents a goroutine ancestor
|
// Ancestor represents a goroutine ancestor
|
||||||
|
@ -1184,9 +1184,10 @@ func (d *Debugger) ListDynamicLibraries() []api.Image {
|
|||||||
d.processMutex.Lock()
|
d.processMutex.Lock()
|
||||||
defer d.processMutex.Unlock()
|
defer d.processMutex.Unlock()
|
||||||
bi := d.target.BinInfo()
|
bi := d.target.BinInfo()
|
||||||
r := make([]api.Image, len(bi.Images))
|
r := make([]api.Image, 0, len(bi.Images)-1)
|
||||||
for i := range bi.Images {
|
// skips the first image because it's the executable file
|
||||||
r[i] = api.ConvertImage(bi.Images[i])
|
for i := range bi.Images[1:] {
|
||||||
|
r = append(r, api.ConvertImage(bi.Images[i]))
|
||||||
}
|
}
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
@ -72,20 +72,19 @@ func findFirstNonRuntimeFrame(p proc.Process) (proc.Stackframe, error) {
|
|||||||
return proc.Stackframe{}, fmt.Errorf("non-runtime frame not found")
|
return proc.Stackframe{}, fmt.Errorf("non-runtime frame not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
func evalVariable(p proc.Process, symbol string, cfg proc.LoadConfig) (*proc.Variable, error) {
|
func evalScope(p proc.Process) (*proc.EvalScope, error) {
|
||||||
var scope *proc.EvalScope
|
if testBackend != "rr" {
|
||||||
var err error
|
return proc.GoroutineScope(p.CurrentThread())
|
||||||
|
|
||||||
if testBackend == "rr" {
|
|
||||||
var frame proc.Stackframe
|
|
||||||
frame, err = findFirstNonRuntimeFrame(p)
|
|
||||||
if err == nil {
|
|
||||||
scope = proc.FrameToScope(p.BinInfo(), p.CurrentThread(), nil, frame)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
scope, err = proc.GoroutineScope(p.CurrentThread())
|
|
||||||
}
|
}
|
||||||
|
frame, err := findFirstNonRuntimeFrame(p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return proc.FrameToScope(p.BinInfo(), p.CurrentThread(), nil, frame), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func evalVariable(p proc.Process, symbol string, cfg proc.LoadConfig) (*proc.Variable, error) {
|
||||||
|
scope, err := evalScope(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -108,9 +107,12 @@ func setVariable(p proc.Process, symbol, value string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func withTestProcess(name string, t *testing.T, fn func(p proc.Process, fixture protest.Fixture)) {
|
func withTestProcess(name string, t *testing.T, fn func(p proc.Process, fixture protest.Fixture)) {
|
||||||
var buildFlags protest.BuildFlags
|
withTestProcessArgs(name, t, ".", []string{}, 0, fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func withTestProcessArgs(name string, t *testing.T, wd string, args []string, buildFlags protest.BuildFlags, fn func(p proc.Process, fixture protest.Fixture)) {
|
||||||
if buildMode == "pie" {
|
if buildMode == "pie" {
|
||||||
buildFlags = protest.BuildModePIE
|
buildFlags |= protest.BuildModePIE
|
||||||
}
|
}
|
||||||
fixture := protest.BuildFixture(name, buildFlags)
|
fixture := protest.BuildFixture(name, buildFlags)
|
||||||
var p proc.Process
|
var p proc.Process
|
||||||
@ -118,13 +120,13 @@ func withTestProcess(name string, t *testing.T, fn func(p proc.Process, fixture
|
|||||||
var tracedir string
|
var tracedir string
|
||||||
switch testBackend {
|
switch testBackend {
|
||||||
case "native":
|
case "native":
|
||||||
p, err = native.Launch([]string{fixture.Path}, ".", false, []string{})
|
p, err = native.Launch(append([]string{fixture.Path}, args...), wd, false, []string{})
|
||||||
case "lldb":
|
case "lldb":
|
||||||
p, err = gdbserial.LLDBLaunch([]string{fixture.Path}, ".", false, []string{})
|
p, err = gdbserial.LLDBLaunch(append([]string{fixture.Path}, args...), wd, false, []string{})
|
||||||
case "rr":
|
case "rr":
|
||||||
protest.MustHaveRecordingAllowed(t)
|
protest.MustHaveRecordingAllowed(t)
|
||||||
t.Log("recording")
|
t.Log("recording")
|
||||||
p, tracedir, err = gdbserial.RecordAndReplay([]string{fixture.Path}, ".", true, []string{})
|
p, tracedir, err = gdbserial.RecordAndReplay(append([]string{fixture.Path}, args...), wd, true, []string{})
|
||||||
t.Logf("replaying %q", tracedir)
|
t.Logf("replaying %q", tracedir)
|
||||||
default:
|
default:
|
||||||
t.Fatalf("unknown backend %q", testBackend)
|
t.Fatalf("unknown backend %q", testBackend)
|
||||||
@ -1240,3 +1242,99 @@ func TestIssue1531(t *testing.T) {
|
|||||||
hasKeys(mmv, "r", "t", "v")
|
hasKeys(mmv, "r", "t", "v")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setFileLineBreakpoint(p proc.Process, t *testing.T, path string, lineno int) *proc.Breakpoint {
|
||||||
|
addr, err := proc.FindFileLocation(p, path, lineno)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("FindFileLocation: %v", err)
|
||||||
|
}
|
||||||
|
bp, err := p.SetBreakpoint(addr, proc.UserBreakpoint, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("SetBreakpoint: %v", err)
|
||||||
|
}
|
||||||
|
return bp
|
||||||
|
}
|
||||||
|
|
||||||
|
func currentLocation(p proc.Process, t *testing.T) (pc uint64, f string, ln int, fn *proc.Function) {
|
||||||
|
regs, err := p.CurrentThread().Registers(false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Registers error: %v", err)
|
||||||
|
}
|
||||||
|
f, l, fn := p.BinInfo().PCToLine(regs.PC())
|
||||||
|
t.Logf("at %#x %s:%d %v", regs.PC(), f, l, fn)
|
||||||
|
return regs.PC(), f, l, fn
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertCurrentLocationFunction(p proc.Process, t *testing.T, fnname string) {
|
||||||
|
_, _, _, fn := currentLocation(p, t)
|
||||||
|
if fn == nil {
|
||||||
|
t.Fatalf("Not in a function")
|
||||||
|
}
|
||||||
|
if fn.Name != fnname {
|
||||||
|
t.Fatalf("Wrong function %s %s", fn.Name, fnname)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPluginVariables(t *testing.T) {
|
||||||
|
pluginFixtures := protest.WithPlugins(t, "plugin1/", "plugin2/")
|
||||||
|
|
||||||
|
withTestProcessArgs("plugintest2", t, ".", []string{pluginFixtures[0].Path, pluginFixtures[1].Path}, 0, func(p proc.Process, fixture protest.Fixture) {
|
||||||
|
setFileLineBreakpoint(p, t, fixture.Source, 41)
|
||||||
|
assertNoError(proc.Continue(p), t, "Continue 1")
|
||||||
|
|
||||||
|
bp, err := setFunctionBreakpoint(p, "github.com/go-delve/delve/_fixtures/plugin2.TypesTest")
|
||||||
|
assertNoError(err, t, "SetBreakpoint(TypesTest)")
|
||||||
|
t.Logf("bp.Addr = %#x", bp.Addr)
|
||||||
|
_, err = setFunctionBreakpoint(p, "github.com/go-delve/delve/_fixtures/plugin2.aIsNotNil")
|
||||||
|
assertNoError(err, t, "SetBreakpoint(aIsNotNil)")
|
||||||
|
|
||||||
|
for _, image := range p.BinInfo().Images {
|
||||||
|
t.Logf("%#x %s\n", image.StaticBase, image.Path)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertNoError(proc.Continue(p), t, "Continue 2")
|
||||||
|
|
||||||
|
// test that PackageVariables returns variables from the executable and plugins
|
||||||
|
scope, err := evalScope(p)
|
||||||
|
assertNoError(err, t, "evalScope")
|
||||||
|
allvars, err := scope.PackageVariables(pnormalLoadConfig)
|
||||||
|
assertNoError(err, t, "PackageVariables")
|
||||||
|
var plugin2AFound, mainExeGlobalFound bool
|
||||||
|
for _, v := range allvars {
|
||||||
|
switch v.Name {
|
||||||
|
case "github.com/go-delve/delve/_fixtures/plugin2.A":
|
||||||
|
plugin2AFound = true
|
||||||
|
case "main.ExeGlobal":
|
||||||
|
mainExeGlobalFound = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !plugin2AFound {
|
||||||
|
t.Fatalf("variable plugin2.A not found in the output of PackageVariables")
|
||||||
|
}
|
||||||
|
if !mainExeGlobalFound {
|
||||||
|
t.Fatalf("variable main.ExeGlobal not found in the output of PackageVariables")
|
||||||
|
}
|
||||||
|
|
||||||
|
// read interface variable, inside plugin code, with a concrete type defined in the executable
|
||||||
|
vs, err := evalVariable(p, "s", pnormalLoadConfig)
|
||||||
|
assertNoError(err, t, "Eval(s)")
|
||||||
|
assertVariable(t, vs, varTest{"s", true, `github.com/go-delve/delve/_fixtures/internal/pluginsupport.Something(*main.asomething) *{n: 2}`, ``, `github.com/go-delve/delve/_fixtures/internal/pluginsupport.Something`, nil})
|
||||||
|
|
||||||
|
// test that the concrete type -> interface{} conversion works across plugins (mostly tests proc.dwarfToRuntimeType)
|
||||||
|
assertNoError(setVariable(p, "plugin2.A", "main.ExeGlobal"), t, "setVariable(plugin2.A = main.ExeGlobal)")
|
||||||
|
assertNoError(proc.Continue(p), t, "Continue 3")
|
||||||
|
assertCurrentLocationFunction(p, t, "github.com/go-delve/delve/_fixtures/plugin2.aIsNotNil")
|
||||||
|
vstr, err := evalVariable(p, "str", pnormalLoadConfig)
|
||||||
|
assertNoError(err, t, "Eval(str)")
|
||||||
|
assertVariable(t, vstr, varTest{"str", true, `"success"`, ``, `string`, nil})
|
||||||
|
|
||||||
|
assertNoError(proc.StepOut(p), t, "StepOut")
|
||||||
|
assertNoError(proc.StepOut(p), t, "StepOut")
|
||||||
|
assertNoError(proc.Next(p), t, "Next")
|
||||||
|
|
||||||
|
// read interface variable, inside executable code, with a concrete type defined in a plugin
|
||||||
|
vb, err := evalVariable(p, "b", pnormalLoadConfig)
|
||||||
|
assertNoError(err, t, "Eval(b)")
|
||||||
|
assertVariable(t, vb, varTest{"b", true, `github.com/go-delve/delve/_fixtures/internal/pluginsupport.SomethingElse(*github.com/go-delve/delve/_fixtures/plugin2.asomethingelse) *{x: 1, y: 4}`, ``, `github.com/go-delve/delve/_fixtures/internal/pluginsupport.SomethingElse`, nil})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user