
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
87 lines
2.2 KiB
Go
87 lines
2.2 KiB
Go
package frame
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"sort"
|
|
)
|
|
|
|
// Represents a Common Information Entry in
|
|
// the Dwarf .debug_frame section.
|
|
type CommonInformationEntry struct {
|
|
Length uint32
|
|
CIE_id uint32
|
|
Version uint8
|
|
Augmentation string
|
|
CodeAlignmentFactor uint64
|
|
DataAlignmentFactor int64
|
|
ReturnAddressRegister uint64
|
|
InitialInstructions []byte
|
|
staticBase uint64
|
|
}
|
|
|
|
// Represents a Frame Descriptor Entry in the
|
|
// Dwarf .debug_frame section.
|
|
type FrameDescriptionEntry struct {
|
|
Length uint32
|
|
CIE *CommonInformationEntry
|
|
Instructions []byte
|
|
begin, size uint64
|
|
order binary.ByteOrder
|
|
}
|
|
|
|
// Returns whether or not the given address is within the
|
|
// bounds of this frame.
|
|
func (fde *FrameDescriptionEntry) Cover(addr uint64) bool {
|
|
return (addr - fde.begin) < fde.size
|
|
}
|
|
|
|
// Address of first location for this frame.
|
|
func (fde *FrameDescriptionEntry) Begin() uint64 {
|
|
return fde.begin
|
|
}
|
|
|
|
// Address of last location for this frame.
|
|
func (fde *FrameDescriptionEntry) End() uint64 {
|
|
return fde.begin + fde.size
|
|
}
|
|
|
|
// Set up frame for the given PC.
|
|
func (fde *FrameDescriptionEntry) EstablishFrame(pc uint64) *FrameContext {
|
|
return executeDwarfProgramUntilPC(fde, pc)
|
|
}
|
|
|
|
type FrameDescriptionEntries []*FrameDescriptionEntry
|
|
|
|
func NewFrameIndex() FrameDescriptionEntries {
|
|
return make(FrameDescriptionEntries, 0, 1000)
|
|
}
|
|
|
|
type ErrNoFDEForPC struct {
|
|
PC uint64
|
|
}
|
|
|
|
func (err *ErrNoFDEForPC) Error() string {
|
|
return fmt.Sprintf("could not find FDE for PC %#v", err.PC)
|
|
}
|
|
|
|
// Returns the Frame Description Entry for the given PC.
|
|
func (fdes FrameDescriptionEntries) FDEForPC(pc uint64) (*FrameDescriptionEntry, error) {
|
|
idx := sort.Search(len(fdes), func(i int) bool {
|
|
return fdes[i].Cover(pc) || fdes[i].Begin() >= pc
|
|
})
|
|
if idx == len(fdes) || !fdes[idx].Cover(pc) {
|
|
return nil, &ErrNoFDEForPC{pc}
|
|
}
|
|
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
|
|
}
|