delve/pkg/dwarf/frame/entries.go
Alessandro Arzilli a3c7ba8808
proc: add workaround for debug_frame bug on macOS (#2374)
This adds a workaround for the bug described at:

https://github.com/golang/go/issues/25841

Because dsymutil running on PIE does not adjust the address of
debug_frame entries (but adjusts debug_info entries) we try to do the
adjustment ourselves.

Updates #2346
2021-03-09 11:35:24 +01:00

152 lines
4.5 KiB
Go

package frame
import (
"encoding/binary"
"fmt"
"sort"
)
// CommonInformationEntry 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
// eh_frame pointer encoding
ptrEncAddr ptrEnc
}
// FrameDescriptionEntry 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
}
// Cover 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
}
// Begin returns address of first location for this frame.
func (fde *FrameDescriptionEntry) Begin() uint64 {
return fde.begin
}
// End returns address of last location for this frame.
func (fde *FrameDescriptionEntry) End() uint64 {
return fde.begin + fde.size
}
// Translate moves the beginning of fde forward by delta.
func (fde *FrameDescriptionEntry) Translate(delta uint64) {
fde.begin += delta
}
// EstablishFrame 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)
}
// ErrNoFDEForPC FDE for PC not found error
type ErrNoFDEForPC struct {
PC uint64
}
func (err *ErrNoFDEForPC) Error() string {
return fmt.Sprintf("could not find FDE for PC %#v", err.PC)
}
// FDEForPC 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.SliceStable(r, func(i, j int) bool {
return r[i].Begin() < r[j].Begin()
})
// remove duplicates
uniqFDEs := fdes[:0]
for _, fde := range fdes {
if len(uniqFDEs) > 0 {
last := uniqFDEs[len(uniqFDEs)-1]
if last.Begin() == fde.Begin() && last.End() == fde.End() {
continue
}
}
uniqFDEs = append(uniqFDEs, fde)
}
return r
}
// ptrEnc represents a pointer encoding value, used during eh_frame decoding
// to determine how pointers were encoded.
// Least significant 4 (0xf) bytes encode the size as well as its
// signed-ness, most significant 4 bytes (0xf0) are flags describing how
// the value should be interpreted (absolute, relative...)
// See https://www.airs.com/blog/archives/460.
type ptrEnc uint8
const (
ptrEncAbs ptrEnc = 0x00 // pointer-sized unsigned integer
ptrEncOmit ptrEnc = 0xff // omitted
ptrEncUleb ptrEnc = 0x01 // ULEB128
ptrEncUdata2 ptrEnc = 0x02 // 2 bytes
ptrEncUdata4 ptrEnc = 0x03 // 4 bytes
ptrEncUdata8 ptrEnc = 0x04 // 8 bytes
ptrEncSigned ptrEnc = 0x08 // pointer-sized signed integer
ptrEncSleb ptrEnc = 0x09 // SLEB128
ptrEncSdata2 ptrEnc = 0x0a // 2 bytes, signed
ptrEncSdata4 ptrEnc = 0x0b // 4 bytes, signed
ptrEncSdata8 ptrEnc = 0x0c // 8 bytes, signed
ptrEncPCRel ptrEnc = 0x10 // value is relative to the memory address where it appears
ptrEncTextRel ptrEnc = 0x20 // value is relative to the address of the text section
ptrEncDataRel ptrEnc = 0x30 // value is relative to the address of the data section
ptrEncFuncRel ptrEnc = 0x40 // value is relative to the start of the function
ptrEncAligned ptrEnc = 0x50 // value should be aligned
ptrEncIndirect ptrEnc = 0x80 // value is an address where the real value of the pointer is stored
)
// Supported returns true if this pointer encoding is supported.
func (ptrEnc ptrEnc) Supported() bool {
if ptrEnc != ptrEncOmit {
szenc := ptrEnc & 0x0f
if ((szenc > ptrEncUdata8) && (szenc < ptrEncSigned)) || (szenc > ptrEncSdata8) {
// These values aren't defined at the moment
return false
}
if ptrEnc&0xf0 != ptrEncPCRel {
// Currently only the PC relative flag is supported
return false
}
}
return true
}