
The repository is being switched from the personal account github.com/derekparker/delve to the organization account github.com/go-delve/delve. This patch updates imports and docs, while preserving things which should not be changed such as my name in the CHANGELOG and in TODO comments.
425 lines
11 KiB
Go
425 lines
11 KiB
Go
package frame
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"fmt"
|
|
|
|
"github.com/go-delve/delve/pkg/dwarf/util"
|
|
)
|
|
|
|
type DWRule struct {
|
|
Rule Rule
|
|
Offset int64
|
|
Reg uint64
|
|
Expression []byte
|
|
}
|
|
|
|
type FrameContext struct {
|
|
loc uint64
|
|
order binary.ByteOrder
|
|
address uint64
|
|
CFA DWRule
|
|
Regs map[uint64]DWRule
|
|
initialRegs map[uint64]DWRule
|
|
prevRegs map[uint64]DWRule
|
|
buf *bytes.Buffer
|
|
cie *CommonInformationEntry
|
|
RetAddrReg uint64
|
|
codeAlignment uint64
|
|
dataAlignment int64
|
|
}
|
|
|
|
// Instructions used to recreate the table from the .debug_frame data.
|
|
const (
|
|
DW_CFA_nop = 0x0 // No ops
|
|
DW_CFA_set_loc = 0x01 // op1: address
|
|
DW_CFA_advance_loc1 = iota // op1: 1-bytes delta
|
|
DW_CFA_advance_loc2 // op1: 2-byte delta
|
|
DW_CFA_advance_loc4 // op1: 4-byte delta
|
|
DW_CFA_offset_extended // op1: ULEB128 register, op2: ULEB128 offset
|
|
DW_CFA_restore_extended // op1: ULEB128 register
|
|
DW_CFA_undefined // op1: ULEB128 register
|
|
DW_CFA_same_value // op1: ULEB128 register
|
|
DW_CFA_register // op1: ULEB128 register, op2: ULEB128 register
|
|
DW_CFA_remember_state // No ops
|
|
DW_CFA_restore_state // No ops
|
|
DW_CFA_def_cfa // op1: ULEB128 register, op2: ULEB128 offset
|
|
DW_CFA_def_cfa_register // op1: ULEB128 register
|
|
DW_CFA_def_cfa_offset // op1: ULEB128 offset
|
|
DW_CFA_def_cfa_expression // op1: BLOCK
|
|
DW_CFA_expression // op1: ULEB128 register, op2: BLOCK
|
|
DW_CFA_offset_extended_sf // op1: ULEB128 register, op2: SLEB128 BLOCK
|
|
DW_CFA_def_cfa_sf // op1: ULEB128 register, op2: SLEB128 offset
|
|
DW_CFA_def_cfa_offset_sf // op1: SLEB128 offset
|
|
DW_CFA_val_offset // op1: ULEB128, op2: ULEB128
|
|
DW_CFA_val_offset_sf // op1: ULEB128, op2: SLEB128
|
|
DW_CFA_val_expression // op1: ULEB128, op2: BLOCK
|
|
DW_CFA_lo_user = 0x1c // op1: BLOCK
|
|
DW_CFA_hi_user = 0x3f // op1: ULEB128 register, op2: BLOCK
|
|
DW_CFA_advance_loc = (0x1 << 6) // High 2 bits: 0x1, low 6: delta
|
|
DW_CFA_offset = (0x2 << 6) // High 2 bits: 0x2, low 6: register
|
|
DW_CFA_restore = (0x3 << 6) // High 2 bits: 0x3, low 6: register
|
|
)
|
|
|
|
// Rules defined for register values.
|
|
type Rule byte
|
|
|
|
const (
|
|
RuleUndefined Rule = iota
|
|
RuleSameVal
|
|
RuleOffset
|
|
RuleValOffset
|
|
RuleRegister
|
|
RuleExpression
|
|
RuleValExpression
|
|
RuleArchitectural
|
|
RuleCFA // Value is rule.Reg + rule.Offset
|
|
RuleFramePointer // Value is stored at address rule.Reg + rule.Offset, but only if it's less than the current CFA, otherwise same value
|
|
)
|
|
|
|
const low_6_offset = 0x3f
|
|
|
|
type instruction func(frame *FrameContext)
|
|
|
|
// // Mapping from DWARF opcode to function.
|
|
var fnlookup = map[byte]instruction{
|
|
DW_CFA_advance_loc: advanceloc,
|
|
DW_CFA_offset: offset,
|
|
DW_CFA_restore: restore,
|
|
DW_CFA_set_loc: setloc,
|
|
DW_CFA_advance_loc1: advanceloc1,
|
|
DW_CFA_advance_loc2: advanceloc2,
|
|
DW_CFA_advance_loc4: advanceloc4,
|
|
DW_CFA_offset_extended: offsetextended,
|
|
DW_CFA_restore_extended: restoreextended,
|
|
DW_CFA_undefined: undefined,
|
|
DW_CFA_same_value: samevalue,
|
|
DW_CFA_register: register,
|
|
DW_CFA_remember_state: rememberstate,
|
|
DW_CFA_restore_state: restorestate,
|
|
DW_CFA_def_cfa: defcfa,
|
|
DW_CFA_def_cfa_register: defcfaregister,
|
|
DW_CFA_def_cfa_offset: defcfaoffset,
|
|
DW_CFA_def_cfa_expression: defcfaexpression,
|
|
DW_CFA_expression: expression,
|
|
DW_CFA_offset_extended_sf: offsetextendedsf,
|
|
DW_CFA_def_cfa_sf: defcfasf,
|
|
DW_CFA_def_cfa_offset_sf: defcfaoffsetsf,
|
|
DW_CFA_val_offset: valoffset,
|
|
DW_CFA_val_offset_sf: valoffsetsf,
|
|
DW_CFA_val_expression: valexpression,
|
|
DW_CFA_lo_user: louser,
|
|
DW_CFA_hi_user: hiuser,
|
|
}
|
|
|
|
func executeCIEInstructions(cie *CommonInformationEntry) *FrameContext {
|
|
initialInstructions := make([]byte, len(cie.InitialInstructions))
|
|
copy(initialInstructions, cie.InitialInstructions)
|
|
frame := &FrameContext{
|
|
cie: cie,
|
|
Regs: make(map[uint64]DWRule),
|
|
RetAddrReg: cie.ReturnAddressRegister,
|
|
initialRegs: make(map[uint64]DWRule),
|
|
prevRegs: make(map[uint64]DWRule),
|
|
codeAlignment: cie.CodeAlignmentFactor,
|
|
dataAlignment: cie.DataAlignmentFactor,
|
|
buf: bytes.NewBuffer(initialInstructions),
|
|
}
|
|
|
|
frame.ExecuteDwarfProgram()
|
|
return frame
|
|
}
|
|
|
|
// Unwind the stack to find the return address register.
|
|
func executeDwarfProgramUntilPC(fde *FrameDescriptionEntry, pc uint64) *FrameContext {
|
|
frame := executeCIEInstructions(fde.CIE)
|
|
frame.order = fde.order
|
|
frame.loc = fde.Begin()
|
|
frame.address = pc
|
|
frame.ExecuteUntilPC(fde.Instructions)
|
|
|
|
return frame
|
|
}
|
|
|
|
func (frame *FrameContext) ExecuteDwarfProgram() {
|
|
for frame.buf.Len() > 0 {
|
|
executeDwarfInstruction(frame)
|
|
}
|
|
}
|
|
|
|
// Execute dwarf instructions.
|
|
func (frame *FrameContext) ExecuteUntilPC(instructions []byte) {
|
|
frame.buf.Truncate(0)
|
|
frame.buf.Write(instructions)
|
|
|
|
// We only need to execute the instructions until
|
|
// ctx.loc > ctx.address (which is the address we
|
|
// are currently at in the traced process).
|
|
for frame.address >= frame.loc && frame.buf.Len() > 0 {
|
|
executeDwarfInstruction(frame)
|
|
}
|
|
}
|
|
|
|
func executeDwarfInstruction(frame *FrameContext) {
|
|
instruction, err := frame.buf.ReadByte()
|
|
if err != nil {
|
|
panic("Could not read from instruction buffer")
|
|
}
|
|
|
|
if instruction == DW_CFA_nop {
|
|
return
|
|
}
|
|
|
|
fn := lookupFunc(instruction, frame.buf)
|
|
|
|
fn(frame)
|
|
}
|
|
|
|
func lookupFunc(instruction byte, buf *bytes.Buffer) instruction {
|
|
const high_2_bits = 0xc0
|
|
var restore bool
|
|
|
|
// Special case the 3 opcodes that have their argument encoded in the opcode itself.
|
|
switch instruction & high_2_bits {
|
|
case DW_CFA_advance_loc:
|
|
instruction = DW_CFA_advance_loc
|
|
restore = true
|
|
|
|
case DW_CFA_offset:
|
|
instruction = DW_CFA_offset
|
|
restore = true
|
|
|
|
case DW_CFA_restore:
|
|
instruction = DW_CFA_restore
|
|
restore = true
|
|
}
|
|
|
|
if restore {
|
|
// Restore the last byte as it actually contains the argument for the opcode.
|
|
err := buf.UnreadByte()
|
|
if err != nil {
|
|
panic("Could not unread byte")
|
|
}
|
|
}
|
|
|
|
fn, ok := fnlookup[instruction]
|
|
if !ok {
|
|
panic(fmt.Sprintf("Encountered an unexpected DWARF CFA opcode: %#v", instruction))
|
|
}
|
|
|
|
return fn
|
|
}
|
|
|
|
func advanceloc(frame *FrameContext) {
|
|
b, err := frame.buf.ReadByte()
|
|
if err != nil {
|
|
panic("Could not read byte")
|
|
}
|
|
|
|
delta := b & low_6_offset
|
|
frame.loc += uint64(delta) * frame.codeAlignment
|
|
}
|
|
|
|
func advanceloc1(frame *FrameContext) {
|
|
delta, err := frame.buf.ReadByte()
|
|
if err != nil {
|
|
panic("Could not read byte")
|
|
}
|
|
|
|
frame.loc += uint64(delta) * frame.codeAlignment
|
|
}
|
|
|
|
func advanceloc2(frame *FrameContext) {
|
|
var delta uint16
|
|
binary.Read(frame.buf, frame.order, &delta)
|
|
|
|
frame.loc += uint64(delta) * frame.codeAlignment
|
|
}
|
|
|
|
func advanceloc4(frame *FrameContext) {
|
|
var delta uint32
|
|
binary.Read(frame.buf, frame.order, &delta)
|
|
|
|
frame.loc += uint64(delta) * frame.codeAlignment
|
|
}
|
|
|
|
func offset(frame *FrameContext) {
|
|
b, err := frame.buf.ReadByte()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
var (
|
|
reg = b & low_6_offset
|
|
offset, _ = util.DecodeULEB128(frame.buf)
|
|
)
|
|
|
|
frame.Regs[uint64(reg)] = DWRule{Offset: int64(offset) * frame.dataAlignment, Rule: RuleOffset}
|
|
}
|
|
|
|
func restore(frame *FrameContext) {
|
|
b, err := frame.buf.ReadByte()
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
reg := uint64(b & low_6_offset)
|
|
oldrule, ok := frame.initialRegs[reg]
|
|
if ok {
|
|
frame.Regs[reg] = DWRule{Offset: oldrule.Offset, Rule: RuleOffset}
|
|
} else {
|
|
frame.Regs[reg] = DWRule{Rule: RuleUndefined}
|
|
}
|
|
}
|
|
|
|
func setloc(frame *FrameContext) {
|
|
var loc uint64
|
|
binary.Read(frame.buf, frame.order, &loc)
|
|
|
|
frame.loc = loc + frame.cie.staticBase
|
|
}
|
|
|
|
func offsetextended(frame *FrameContext) {
|
|
var (
|
|
reg, _ = util.DecodeULEB128(frame.buf)
|
|
offset, _ = util.DecodeULEB128(frame.buf)
|
|
)
|
|
|
|
frame.Regs[reg] = DWRule{Offset: int64(offset) * frame.dataAlignment, Rule: RuleOffset}
|
|
}
|
|
|
|
func undefined(frame *FrameContext) {
|
|
reg, _ := util.DecodeULEB128(frame.buf)
|
|
frame.Regs[reg] = DWRule{Rule: RuleUndefined}
|
|
}
|
|
|
|
func samevalue(frame *FrameContext) {
|
|
reg, _ := util.DecodeULEB128(frame.buf)
|
|
frame.Regs[reg] = DWRule{Rule: RuleSameVal}
|
|
}
|
|
|
|
func register(frame *FrameContext) {
|
|
reg1, _ := util.DecodeULEB128(frame.buf)
|
|
reg2, _ := util.DecodeULEB128(frame.buf)
|
|
frame.Regs[reg1] = DWRule{Reg: reg2, Rule: RuleRegister}
|
|
}
|
|
|
|
func rememberstate(frame *FrameContext) {
|
|
frame.prevRegs = frame.Regs
|
|
}
|
|
|
|
func restorestate(frame *FrameContext) {
|
|
frame.Regs = frame.prevRegs
|
|
}
|
|
|
|
func restoreextended(frame *FrameContext) {
|
|
reg, _ := util.DecodeULEB128(frame.buf)
|
|
|
|
oldrule, ok := frame.initialRegs[reg]
|
|
if ok {
|
|
frame.Regs[reg] = DWRule{Offset: oldrule.Offset, Rule: RuleOffset}
|
|
} else {
|
|
frame.Regs[reg] = DWRule{Rule: RuleUndefined}
|
|
}
|
|
}
|
|
|
|
func defcfa(frame *FrameContext) {
|
|
reg, _ := util.DecodeULEB128(frame.buf)
|
|
offset, _ := util.DecodeULEB128(frame.buf)
|
|
|
|
frame.CFA.Rule = RuleCFA
|
|
frame.CFA.Reg = reg
|
|
frame.CFA.Offset = int64(offset)
|
|
}
|
|
|
|
func defcfaregister(frame *FrameContext) {
|
|
reg, _ := util.DecodeULEB128(frame.buf)
|
|
frame.CFA.Reg = reg
|
|
}
|
|
|
|
func defcfaoffset(frame *FrameContext) {
|
|
offset, _ := util.DecodeULEB128(frame.buf)
|
|
frame.CFA.Offset = int64(offset)
|
|
}
|
|
|
|
func defcfasf(frame *FrameContext) {
|
|
reg, _ := util.DecodeULEB128(frame.buf)
|
|
offset, _ := util.DecodeSLEB128(frame.buf)
|
|
|
|
frame.CFA.Rule = RuleCFA
|
|
frame.CFA.Reg = reg
|
|
frame.CFA.Offset = offset * frame.dataAlignment
|
|
}
|
|
|
|
func defcfaoffsetsf(frame *FrameContext) {
|
|
offset, _ := util.DecodeSLEB128(frame.buf)
|
|
offset *= frame.dataAlignment
|
|
frame.CFA.Offset = offset
|
|
}
|
|
|
|
func defcfaexpression(frame *FrameContext) {
|
|
var (
|
|
l, _ = util.DecodeULEB128(frame.buf)
|
|
expr = frame.buf.Next(int(l))
|
|
)
|
|
|
|
frame.CFA.Expression = expr
|
|
frame.CFA.Rule = RuleExpression
|
|
}
|
|
|
|
func expression(frame *FrameContext) {
|
|
var (
|
|
reg, _ = util.DecodeULEB128(frame.buf)
|
|
l, _ = util.DecodeULEB128(frame.buf)
|
|
expr = frame.buf.Next(int(l))
|
|
)
|
|
|
|
frame.Regs[reg] = DWRule{Rule: RuleExpression, Expression: expr}
|
|
}
|
|
|
|
func offsetextendedsf(frame *FrameContext) {
|
|
var (
|
|
reg, _ = util.DecodeULEB128(frame.buf)
|
|
offset, _ = util.DecodeSLEB128(frame.buf)
|
|
)
|
|
|
|
frame.Regs[reg] = DWRule{Offset: offset * frame.dataAlignment, Rule: RuleOffset}
|
|
}
|
|
|
|
func valoffset(frame *FrameContext) {
|
|
var (
|
|
reg, _ = util.DecodeULEB128(frame.buf)
|
|
offset, _ = util.DecodeULEB128(frame.buf)
|
|
)
|
|
|
|
frame.Regs[reg] = DWRule{Offset: int64(offset), Rule: RuleValOffset}
|
|
}
|
|
|
|
func valoffsetsf(frame *FrameContext) {
|
|
var (
|
|
reg, _ = util.DecodeULEB128(frame.buf)
|
|
offset, _ = util.DecodeSLEB128(frame.buf)
|
|
)
|
|
|
|
frame.Regs[reg] = DWRule{Offset: offset * frame.dataAlignment, Rule: RuleValOffset}
|
|
}
|
|
|
|
func valexpression(frame *FrameContext) {
|
|
var (
|
|
reg, _ = util.DecodeULEB128(frame.buf)
|
|
l, _ = util.DecodeULEB128(frame.buf)
|
|
expr = frame.buf.Next(int(l))
|
|
)
|
|
|
|
frame.Regs[reg] = DWRule{Rule: RuleValExpression, Expression: expr}
|
|
}
|
|
|
|
func louser(frame *FrameContext) {
|
|
frame.buf.Next(1)
|
|
}
|
|
|
|
func hiuser(frame *FrameContext) {
|
|
frame.buf.Next(1)
|
|
}
|