
The current implementation has a bug to remove duplicates. It can be implemented by using fast-slow pointers.
159 lines
4.7 KiB
Go
159 lines
4.7 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()
|
|
})
|
|
if len(r) < 2 { // fast path, no duplicates
|
|
return r
|
|
}
|
|
|
|
// remove duplicates
|
|
slow := 1
|
|
for fast := 1; fast < len(r); fast++ {
|
|
if r[fast].Begin() != r[fast-1].Begin() || r[fast].End() != r[fast-1].End() {
|
|
if slow != fast {
|
|
r[slow] = r[fast]
|
|
}
|
|
slow++
|
|
}
|
|
}
|
|
return r[:slow]
|
|
}
|
|
|
|
// 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 == ptrEncFlagsMask) 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
|
|
|
|
ptrEncFlagsMask ptrEnc = 0xf0
|
|
|
|
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
|
|
|
|
ptrEncSupportedFlags = ptrEncPCRel
|
|
)
|
|
|
|
// 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&ptrEncFlagsMask)&^ptrEncSupportedFlags != 0 {
|
|
// Currently only the PC relative flag is supported
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|