mirror of
https://github.com/go-delve/delve.git
synced 2025-11-02 12:59:01 +08:00
proc: refactor stack.go to use DWARF registers
Instead of only tracking a few cherrypicked registers in stack.go track all DWARF registers. This is needed for cgo code and for the locationlists emitted by go in 1.10: * The debug_frame sections emitted by C compilers can not be used without tracking all registers * the loclists emitted by go1.10 need all registers of a frame to be interpreted.
This commit is contained in:
@ -50,12 +50,6 @@ func (fde *FrameDescriptionEntry) EstablishFrame(pc uint64) *FrameContext {
|
|||||||
return executeDwarfProgramUntilPC(fde, pc)
|
return executeDwarfProgramUntilPC(fde, pc)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the offset from the current SP that the return address is stored at.
|
|
||||||
func (fde *FrameDescriptionEntry) ReturnAddressOffset(pc uint64) (frameOffset, returnAddressOffset int64) {
|
|
||||||
frame := fde.EstablishFrame(pc)
|
|
||||||
return frame.cfa.offset, frame.regs[fde.CIE.ReturnAddressRegister].offset
|
|
||||||
}
|
|
||||||
|
|
||||||
type FrameDescriptionEntries []*FrameDescriptionEntry
|
type FrameDescriptionEntries []*FrameDescriptionEntry
|
||||||
|
|
||||||
func NewFrameIndex() FrameDescriptionEntries {
|
func NewFrameIndex() FrameDescriptionEntries {
|
||||||
|
|||||||
@ -8,38 +8,28 @@ import (
|
|||||||
"github.com/derekparker/delve/pkg/dwarf/util"
|
"github.com/derekparker/delve/pkg/dwarf/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CurrentFrameAddress struct {
|
|
||||||
register uint64
|
|
||||||
offset int64
|
|
||||||
expression []byte
|
|
||||||
rule byte
|
|
||||||
}
|
|
||||||
|
|
||||||
type DWRule struct {
|
type DWRule struct {
|
||||||
rule byte
|
Rule Rule
|
||||||
offset int64
|
Offset int64
|
||||||
newreg uint64
|
Reg uint64
|
||||||
expression []byte
|
Expression []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
type FrameContext struct {
|
type FrameContext struct {
|
||||||
loc uint64
|
loc uint64
|
||||||
order binary.ByteOrder
|
order binary.ByteOrder
|
||||||
address uint64
|
address uint64
|
||||||
cfa CurrentFrameAddress
|
CFA DWRule
|
||||||
regs map[uint64]DWRule
|
Regs map[uint64]DWRule
|
||||||
initialRegs map[uint64]DWRule
|
initialRegs map[uint64]DWRule
|
||||||
prevRegs map[uint64]DWRule
|
prevRegs map[uint64]DWRule
|
||||||
buf *bytes.Buffer
|
buf *bytes.Buffer
|
||||||
cie *CommonInformationEntry
|
cie *CommonInformationEntry
|
||||||
|
RetAddrReg uint64
|
||||||
codeAlignment uint64
|
codeAlignment uint64
|
||||||
dataAlignment int64
|
dataAlignment int64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fctx *FrameContext) CFAOffset() int64 {
|
|
||||||
return fctx.cfa.offset
|
|
||||||
}
|
|
||||||
|
|
||||||
// Instructions used to recreate the table from the .debug_frame data.
|
// Instructions used to recreate the table from the .debug_frame data.
|
||||||
const (
|
const (
|
||||||
DW_CFA_nop = 0x0 // No ops
|
DW_CFA_nop = 0x0 // No ops
|
||||||
@ -73,15 +63,19 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Rules defined for register values.
|
// Rules defined for register values.
|
||||||
|
type Rule byte
|
||||||
|
|
||||||
const (
|
const (
|
||||||
rule_undefined = iota
|
RuleUndefined Rule = iota
|
||||||
rule_sameval
|
RuleSameVal
|
||||||
rule_offset
|
RuleOffset
|
||||||
rule_valoffset
|
RuleValOffset
|
||||||
rule_register
|
RuleRegister
|
||||||
rule_expression
|
RuleExpression
|
||||||
rule_valexpression
|
RuleValExpression
|
||||||
rule_architectural
|
RuleArchitectural
|
||||||
|
RuleCFA // Value is rule.Reg + rule.Offset
|
||||||
|
RuleRegOffset // Value is stored at address rule.Reg + rule.Offset
|
||||||
)
|
)
|
||||||
|
|
||||||
const low_6_offset = 0x3f
|
const low_6_offset = 0x3f
|
||||||
@ -124,7 +118,8 @@ func executeCIEInstructions(cie *CommonInformationEntry) *FrameContext {
|
|||||||
copy(initialInstructions, cie.InitialInstructions)
|
copy(initialInstructions, cie.InitialInstructions)
|
||||||
frame := &FrameContext{
|
frame := &FrameContext{
|
||||||
cie: cie,
|
cie: cie,
|
||||||
regs: make(map[uint64]DWRule),
|
Regs: make(map[uint64]DWRule),
|
||||||
|
RetAddrReg: cie.ReturnAddressRegister,
|
||||||
initialRegs: make(map[uint64]DWRule),
|
initialRegs: make(map[uint64]DWRule),
|
||||||
prevRegs: make(map[uint64]DWRule),
|
prevRegs: make(map[uint64]DWRule),
|
||||||
codeAlignment: cie.CodeAlignmentFactor,
|
codeAlignment: cie.CodeAlignmentFactor,
|
||||||
@ -142,9 +137,7 @@ func executeDwarfProgramUntilPC(fde *FrameDescriptionEntry, pc uint64) *FrameCon
|
|||||||
frame.order = fde.order
|
frame.order = fde.order
|
||||||
frame.loc = fde.Begin()
|
frame.loc = fde.Begin()
|
||||||
frame.address = pc
|
frame.address = pc
|
||||||
fdeInstructions := make([]byte, len(fde.Instructions))
|
frame.ExecuteUntilPC(fde.Instructions)
|
||||||
copy(fdeInstructions, fde.Instructions)
|
|
||||||
frame.ExecuteUntilPC(fdeInstructions)
|
|
||||||
|
|
||||||
return frame
|
return frame
|
||||||
}
|
}
|
||||||
@ -262,7 +255,7 @@ func offset(frame *FrameContext) {
|
|||||||
offset, _ = util.DecodeULEB128(frame.buf)
|
offset, _ = util.DecodeULEB128(frame.buf)
|
||||||
)
|
)
|
||||||
|
|
||||||
frame.regs[uint64(reg)] = DWRule{offset: int64(offset) * frame.dataAlignment, rule: rule_offset}
|
frame.Regs[uint64(reg)] = DWRule{Offset: int64(offset) * frame.dataAlignment, Rule: RuleOffset}
|
||||||
}
|
}
|
||||||
|
|
||||||
func restore(frame *FrameContext) {
|
func restore(frame *FrameContext) {
|
||||||
@ -274,9 +267,9 @@ func restore(frame *FrameContext) {
|
|||||||
reg := uint64(b & low_6_offset)
|
reg := uint64(b & low_6_offset)
|
||||||
oldrule, ok := frame.initialRegs[reg]
|
oldrule, ok := frame.initialRegs[reg]
|
||||||
if ok {
|
if ok {
|
||||||
frame.regs[reg] = DWRule{offset: oldrule.offset, rule: rule_offset}
|
frame.Regs[reg] = DWRule{Offset: oldrule.Offset, Rule: RuleOffset}
|
||||||
} else {
|
} else {
|
||||||
frame.regs[reg] = DWRule{rule: rule_undefined}
|
frame.Regs[reg] = DWRule{Rule: RuleUndefined}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -293,31 +286,31 @@ func offsetextended(frame *FrameContext) {
|
|||||||
offset, _ = util.DecodeULEB128(frame.buf)
|
offset, _ = util.DecodeULEB128(frame.buf)
|
||||||
)
|
)
|
||||||
|
|
||||||
frame.regs[reg] = DWRule{offset: int64(offset) * frame.dataAlignment, rule: rule_offset}
|
frame.Regs[reg] = DWRule{Offset: int64(offset) * frame.dataAlignment, Rule: RuleOffset}
|
||||||
}
|
}
|
||||||
|
|
||||||
func undefined(frame *FrameContext) {
|
func undefined(frame *FrameContext) {
|
||||||
reg, _ := util.DecodeULEB128(frame.buf)
|
reg, _ := util.DecodeULEB128(frame.buf)
|
||||||
frame.regs[reg] = DWRule{rule: rule_undefined}
|
frame.Regs[reg] = DWRule{Rule: RuleUndefined}
|
||||||
}
|
}
|
||||||
|
|
||||||
func samevalue(frame *FrameContext) {
|
func samevalue(frame *FrameContext) {
|
||||||
reg, _ := util.DecodeULEB128(frame.buf)
|
reg, _ := util.DecodeULEB128(frame.buf)
|
||||||
frame.regs[reg] = DWRule{rule: rule_sameval}
|
frame.Regs[reg] = DWRule{Rule: RuleSameVal}
|
||||||
}
|
}
|
||||||
|
|
||||||
func register(frame *FrameContext) {
|
func register(frame *FrameContext) {
|
||||||
reg1, _ := util.DecodeULEB128(frame.buf)
|
reg1, _ := util.DecodeULEB128(frame.buf)
|
||||||
reg2, _ := util.DecodeULEB128(frame.buf)
|
reg2, _ := util.DecodeULEB128(frame.buf)
|
||||||
frame.regs[reg1] = DWRule{newreg: reg2, rule: rule_register}
|
frame.Regs[reg1] = DWRule{Reg: reg2, Rule: RuleRegister}
|
||||||
}
|
}
|
||||||
|
|
||||||
func rememberstate(frame *FrameContext) {
|
func rememberstate(frame *FrameContext) {
|
||||||
frame.prevRegs = frame.regs
|
frame.prevRegs = frame.Regs
|
||||||
}
|
}
|
||||||
|
|
||||||
func restorestate(frame *FrameContext) {
|
func restorestate(frame *FrameContext) {
|
||||||
frame.regs = frame.prevRegs
|
frame.Regs = frame.prevRegs
|
||||||
}
|
}
|
||||||
|
|
||||||
func restoreextended(frame *FrameContext) {
|
func restoreextended(frame *FrameContext) {
|
||||||
@ -325,9 +318,9 @@ func restoreextended(frame *FrameContext) {
|
|||||||
|
|
||||||
oldrule, ok := frame.initialRegs[reg]
|
oldrule, ok := frame.initialRegs[reg]
|
||||||
if ok {
|
if ok {
|
||||||
frame.regs[reg] = DWRule{offset: oldrule.offset, rule: rule_offset}
|
frame.Regs[reg] = DWRule{Offset: oldrule.Offset, Rule: RuleOffset}
|
||||||
} else {
|
} else {
|
||||||
frame.regs[reg] = DWRule{rule: rule_undefined}
|
frame.Regs[reg] = DWRule{Rule: RuleUndefined}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -335,32 +328,34 @@ func defcfa(frame *FrameContext) {
|
|||||||
reg, _ := util.DecodeULEB128(frame.buf)
|
reg, _ := util.DecodeULEB128(frame.buf)
|
||||||
offset, _ := util.DecodeULEB128(frame.buf)
|
offset, _ := util.DecodeULEB128(frame.buf)
|
||||||
|
|
||||||
frame.cfa.register = reg
|
frame.CFA.Rule = RuleCFA
|
||||||
frame.cfa.offset = int64(offset)
|
frame.CFA.Reg = reg
|
||||||
|
frame.CFA.Offset = int64(offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
func defcfaregister(frame *FrameContext) {
|
func defcfaregister(frame *FrameContext) {
|
||||||
reg, _ := util.DecodeULEB128(frame.buf)
|
reg, _ := util.DecodeULEB128(frame.buf)
|
||||||
frame.cfa.register = reg
|
frame.CFA.Reg = reg
|
||||||
}
|
}
|
||||||
|
|
||||||
func defcfaoffset(frame *FrameContext) {
|
func defcfaoffset(frame *FrameContext) {
|
||||||
offset, _ := util.DecodeULEB128(frame.buf)
|
offset, _ := util.DecodeULEB128(frame.buf)
|
||||||
frame.cfa.offset = int64(offset)
|
frame.CFA.Offset = int64(offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
func defcfasf(frame *FrameContext) {
|
func defcfasf(frame *FrameContext) {
|
||||||
reg, _ := util.DecodeULEB128(frame.buf)
|
reg, _ := util.DecodeULEB128(frame.buf)
|
||||||
offset, _ := util.DecodeSLEB128(frame.buf)
|
offset, _ := util.DecodeSLEB128(frame.buf)
|
||||||
|
|
||||||
frame.cfa.register = reg
|
frame.CFA.Rule = RuleCFA
|
||||||
frame.cfa.offset = offset * frame.dataAlignment
|
frame.CFA.Reg = reg
|
||||||
|
frame.CFA.Offset = offset * frame.dataAlignment
|
||||||
}
|
}
|
||||||
|
|
||||||
func defcfaoffsetsf(frame *FrameContext) {
|
func defcfaoffsetsf(frame *FrameContext) {
|
||||||
offset, _ := util.DecodeSLEB128(frame.buf)
|
offset, _ := util.DecodeSLEB128(frame.buf)
|
||||||
offset *= frame.dataAlignment
|
offset *= frame.dataAlignment
|
||||||
frame.cfa.offset = offset
|
frame.CFA.Offset = offset
|
||||||
}
|
}
|
||||||
|
|
||||||
func defcfaexpression(frame *FrameContext) {
|
func defcfaexpression(frame *FrameContext) {
|
||||||
@ -369,8 +364,8 @@ func defcfaexpression(frame *FrameContext) {
|
|||||||
expr = frame.buf.Next(int(l))
|
expr = frame.buf.Next(int(l))
|
||||||
)
|
)
|
||||||
|
|
||||||
frame.cfa.expression = expr
|
frame.CFA.Expression = expr
|
||||||
frame.cfa.rule = rule_expression
|
frame.CFA.Rule = RuleExpression
|
||||||
}
|
}
|
||||||
|
|
||||||
func expression(frame *FrameContext) {
|
func expression(frame *FrameContext) {
|
||||||
@ -380,7 +375,7 @@ func expression(frame *FrameContext) {
|
|||||||
expr = frame.buf.Next(int(l))
|
expr = frame.buf.Next(int(l))
|
||||||
)
|
)
|
||||||
|
|
||||||
frame.regs[reg] = DWRule{rule: rule_expression, expression: expr}
|
frame.Regs[reg] = DWRule{Rule: RuleExpression, Expression: expr}
|
||||||
}
|
}
|
||||||
|
|
||||||
func offsetextendedsf(frame *FrameContext) {
|
func offsetextendedsf(frame *FrameContext) {
|
||||||
@ -389,7 +384,7 @@ func offsetextendedsf(frame *FrameContext) {
|
|||||||
offset, _ = util.DecodeSLEB128(frame.buf)
|
offset, _ = util.DecodeSLEB128(frame.buf)
|
||||||
)
|
)
|
||||||
|
|
||||||
frame.regs[reg] = DWRule{offset: offset * frame.dataAlignment, rule: rule_offset}
|
frame.Regs[reg] = DWRule{Offset: offset * frame.dataAlignment, Rule: RuleOffset}
|
||||||
}
|
}
|
||||||
|
|
||||||
func valoffset(frame *FrameContext) {
|
func valoffset(frame *FrameContext) {
|
||||||
@ -398,7 +393,7 @@ func valoffset(frame *FrameContext) {
|
|||||||
offset, _ = util.DecodeULEB128(frame.buf)
|
offset, _ = util.DecodeULEB128(frame.buf)
|
||||||
)
|
)
|
||||||
|
|
||||||
frame.regs[reg] = DWRule{offset: int64(offset), rule: rule_valoffset}
|
frame.Regs[reg] = DWRule{Offset: int64(offset), Rule: RuleValOffset}
|
||||||
}
|
}
|
||||||
|
|
||||||
func valoffsetsf(frame *FrameContext) {
|
func valoffsetsf(frame *FrameContext) {
|
||||||
@ -407,7 +402,7 @@ func valoffsetsf(frame *FrameContext) {
|
|||||||
offset, _ = util.DecodeSLEB128(frame.buf)
|
offset, _ = util.DecodeSLEB128(frame.buf)
|
||||||
)
|
)
|
||||||
|
|
||||||
frame.regs[reg] = DWRule{offset: offset * frame.dataAlignment, rule: rule_valoffset}
|
frame.Regs[reg] = DWRule{Offset: offset * frame.dataAlignment, Rule: RuleValOffset}
|
||||||
}
|
}
|
||||||
|
|
||||||
func valexpression(frame *FrameContext) {
|
func valexpression(frame *FrameContext) {
|
||||||
@ -417,7 +412,7 @@ func valexpression(frame *FrameContext) {
|
|||||||
expr = frame.buf.Next(int(l))
|
expr = frame.buf.Next(int(l))
|
||||||
)
|
)
|
||||||
|
|
||||||
frame.regs[reg] = DWRule{rule: rule_valexpression, expression: expr}
|
frame.Regs[reg] = DWRule{Rule: RuleValExpression, Expression: expr}
|
||||||
}
|
}
|
||||||
|
|
||||||
func louser(frame *FrameContext) {
|
func louser(frame *FrameContext) {
|
||||||
|
|||||||
@ -17,7 +17,14 @@ const (
|
|||||||
DW_OP_plus_uconsts = 0x23
|
DW_OP_plus_uconsts = 0x23
|
||||||
)
|
)
|
||||||
|
|
||||||
type stackfn func(*bytes.Buffer, []int64, int64) ([]int64, error)
|
type stackfn func(byte, *context) error
|
||||||
|
|
||||||
|
type context struct {
|
||||||
|
buf *bytes.Buffer
|
||||||
|
stack []int64
|
||||||
|
|
||||||
|
DwarfRegisters
|
||||||
|
}
|
||||||
|
|
||||||
var oplut = map[byte]stackfn{
|
var oplut = map[byte]stackfn{
|
||||||
DW_OP_call_frame_cfa: callframecfa,
|
DW_OP_call_frame_cfa: callframecfa,
|
||||||
@ -27,58 +34,75 @@ var oplut = map[byte]stackfn{
|
|||||||
DW_OP_plus_uconsts: plusuconsts,
|
DW_OP_plus_uconsts: plusuconsts,
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExecuteStackProgram(cfa int64, instructions []byte) (int64, error) {
|
func ExecuteStackProgram(regs DwarfRegisters, instructions []byte) (int64, error) {
|
||||||
stack := make([]int64, 0, 3)
|
ctxt := &context{
|
||||||
buf := bytes.NewBuffer(instructions)
|
buf: bytes.NewBuffer(instructions),
|
||||||
|
stack: make([]int64, 0, 3),
|
||||||
|
DwarfRegisters: regs,
|
||||||
|
}
|
||||||
|
|
||||||
for opcode, err := buf.ReadByte(); err == nil; opcode, err = buf.ReadByte() {
|
for {
|
||||||
|
opcode, err := ctxt.buf.ReadByte()
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
fn, ok := oplut[opcode]
|
fn, ok := oplut[opcode]
|
||||||
if !ok {
|
if !ok {
|
||||||
return 0, fmt.Errorf("invalid instruction %#v", opcode)
|
return 0, fmt.Errorf("invalid instruction %#v", opcode)
|
||||||
}
|
}
|
||||||
|
|
||||||
stack, err = fn(buf, stack, cfa)
|
err = fn(opcode, ctxt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(stack) == 0 {
|
if len(ctxt.stack) == 0 {
|
||||||
return 0, errors.New("empty OP stack")
|
return 0, errors.New("empty OP stack")
|
||||||
}
|
}
|
||||||
|
|
||||||
return stack[len(stack)-1], nil
|
return ctxt.stack[len(ctxt.stack)-1], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func callframecfa(buf *bytes.Buffer, stack []int64, cfa int64) ([]int64, error) {
|
func callframecfa(opcode byte, ctxt *context) error {
|
||||||
if cfa == 0 {
|
if ctxt.CFA == 0 {
|
||||||
return stack, fmt.Errorf("Could not retrieve CFA for current PC")
|
return fmt.Errorf("Could not retrieve call frame CFA for current PC")
|
||||||
}
|
}
|
||||||
return append(stack, int64(cfa)), nil
|
ctxt.stack = append(ctxt.stack, int64(ctxt.CFA))
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func addr(buf *bytes.Buffer, stack []int64, cfa int64) ([]int64, error) {
|
func addr(opcode byte, ctxt *context) error {
|
||||||
return append(stack, int64(binary.LittleEndian.Uint64(buf.Next(8)))), nil
|
ctxt.stack = append(ctxt.stack, int64(binary.LittleEndian.Uint64(ctxt.buf.Next(8))))
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func plus(buf *bytes.Buffer, stack []int64, cfa int64) ([]int64, error) {
|
func plus(opcode byte, ctxt *context) error {
|
||||||
var (
|
var (
|
||||||
slen = len(stack)
|
slen = len(ctxt.stack)
|
||||||
digits = stack[slen-2 : slen]
|
digits = ctxt.stack[slen-2 : slen]
|
||||||
st = stack[:slen-2]
|
st = ctxt.stack[:slen-2]
|
||||||
)
|
)
|
||||||
|
|
||||||
return append(st, digits[0]+digits[1]), nil
|
ctxt.stack = append(st, digits[0]+digits[1])
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func plusuconsts(buf *bytes.Buffer, stack []int64, cfa int64) ([]int64, error) {
|
func plusuconsts(opcode byte, ctxt *context) error {
|
||||||
slen := len(stack)
|
slen := len(ctxt.stack)
|
||||||
num, _ := util.DecodeULEB128(buf)
|
num, _ := util.DecodeULEB128(ctxt.buf)
|
||||||
stack[slen-1] = stack[slen-1] + int64(num)
|
ctxt.stack[slen-1] = ctxt.stack[slen-1] + int64(num)
|
||||||
return stack, nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func consts(buf *bytes.Buffer, stack []int64, cfa int64) ([]int64, error) {
|
func consts(opcode byte, ctxt *context) error {
|
||||||
num, _ := util.DecodeSLEB128(buf)
|
num, _ := util.DecodeSLEB128(ctxt.buf)
|
||||||
return append(stack, num), nil
|
ctxt.stack = append(ctxt.stack, num)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func framebase(opcode byte, ctxt *context) error {
|
||||||
|
num, _ := util.DecodeSLEB128(ctxt.buf)
|
||||||
|
ctxt.stack = append(ctxt.stack, ctxt.FrameBase+num)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,7 @@ func TestExecuteStackProgram(t *testing.T) {
|
|||||||
instructions = []byte{DW_OP_consts, 0x1c, DW_OP_consts, 0x1c, DW_OP_plus}
|
instructions = []byte{DW_OP_consts, 0x1c, DW_OP_consts, 0x1c, DW_OP_plus}
|
||||||
expected = int64(56)
|
expected = int64(56)
|
||||||
)
|
)
|
||||||
actual, err := ExecuteStackProgram(0, instructions)
|
actual, err := ExecuteStackProgram(DwarfRegisters{}, instructions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|||||||
95
pkg/dwarf/op/regs.go
Normal file
95
pkg/dwarf/op/regs.go
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
package op
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DwarfRegisters struct {
|
||||||
|
CFA int64
|
||||||
|
FrameBase int64
|
||||||
|
ObjBase int64
|
||||||
|
Regs []*DwarfRegister
|
||||||
|
|
||||||
|
ByteOrder binary.ByteOrder
|
||||||
|
PCRegNum uint64
|
||||||
|
SPRegNum uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type DwarfRegister struct {
|
||||||
|
Uint64Val uint64
|
||||||
|
Bytes []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint64Val returns the uint64 value of register idx.
|
||||||
|
func (regs *DwarfRegisters) Uint64Val(idx uint64) uint64 {
|
||||||
|
reg := regs.Reg(idx)
|
||||||
|
if reg == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return regs.Regs[idx].Uint64Val
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes returns the bytes value of register idx, nil if the register is not
|
||||||
|
// defined.
|
||||||
|
func (regs *DwarfRegisters) Bytes(idx uint64) []byte {
|
||||||
|
reg := regs.Reg(idx)
|
||||||
|
if reg == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if reg.Bytes == nil {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
binary.Write(&buf, regs.ByteOrder, reg.Uint64Val)
|
||||||
|
reg.Bytes = buf.Bytes()
|
||||||
|
}
|
||||||
|
return reg.Bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reg returns register idx or nil if the register is not defined.
|
||||||
|
func (regs *DwarfRegisters) Reg(idx uint64) *DwarfRegister {
|
||||||
|
if idx >= uint64(len(regs.Regs)) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return regs.Regs[idx]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (regs *DwarfRegisters) PC() uint64 {
|
||||||
|
return regs.Uint64Val(regs.PCRegNum)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (regs *DwarfRegisters) SP() uint64 {
|
||||||
|
return regs.Uint64Val(regs.SPRegNum)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddReg adds register idx to regs.
|
||||||
|
func (regs *DwarfRegisters) AddReg(idx uint64, reg *DwarfRegister) {
|
||||||
|
if idx >= uint64(len(regs.Regs)) {
|
||||||
|
newRegs := make([]*DwarfRegister, idx+1)
|
||||||
|
copy(newRegs, regs.Regs)
|
||||||
|
regs.Regs = newRegs
|
||||||
|
}
|
||||||
|
regs.Regs[idx] = reg
|
||||||
|
}
|
||||||
|
|
||||||
|
func DwarfRegisterFromUint64(v uint64) *DwarfRegister {
|
||||||
|
return &DwarfRegister{Uint64Val: v}
|
||||||
|
}
|
||||||
|
|
||||||
|
func DwarfRegisterFromBytes(bytes []byte) *DwarfRegister {
|
||||||
|
var v uint64
|
||||||
|
switch len(bytes) {
|
||||||
|
case 1:
|
||||||
|
v = uint64(bytes[0])
|
||||||
|
case 2:
|
||||||
|
x := binary.LittleEndian.Uint16(bytes)
|
||||||
|
v = uint64(x)
|
||||||
|
case 4:
|
||||||
|
x := binary.LittleEndian.Uint32(bytes)
|
||||||
|
v = uint64(x)
|
||||||
|
default:
|
||||||
|
if len(bytes) >= 8 {
|
||||||
|
v = binary.LittleEndian.Uint64(bytes[:8])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &DwarfRegister{Uint64Val: v, Bytes: bytes}
|
||||||
|
}
|
||||||
@ -73,7 +73,7 @@ func (reader *Reader) AddrFor(name string) (uint64, error) {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return 0, fmt.Errorf("type assertion failed")
|
return 0, fmt.Errorf("type assertion failed")
|
||||||
}
|
}
|
||||||
addr, err := op.ExecuteStackProgram(0, instructions)
|
addr, err := op.ExecuteStackProgram(op.DwarfRegisters{}, instructions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
@ -99,7 +99,7 @@ func (reader *Reader) AddrForMember(member string, initialInstructions []byte) (
|
|||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
addr, err := op.ExecuteStackProgram(0, append(initialInstructions, instructions...))
|
addr, err := op.ExecuteStackProgram(op.DwarfRegisters{}, append(initialInstructions, instructions...))
|
||||||
return uint64(addr), err
|
return uint64(addr), err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
197
pkg/proc/arch.go
197
pkg/proc/arch.go
@ -1,5 +1,13 @@
|
|||||||
package proc
|
package proc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
|
||||||
|
"github.com/derekparker/delve/pkg/dwarf/frame"
|
||||||
|
"github.com/derekparker/delve/pkg/dwarf/op"
|
||||||
|
"golang.org/x/arch/x86/x86asm"
|
||||||
|
)
|
||||||
|
|
||||||
// Arch defines an interface for representing a
|
// Arch defines an interface for representing a
|
||||||
// CPU architecture.
|
// CPU architecture.
|
||||||
type Arch interface {
|
type Arch interface {
|
||||||
@ -7,6 +15,10 @@ type Arch interface {
|
|||||||
BreakpointInstruction() []byte
|
BreakpointInstruction() []byte
|
||||||
BreakpointSize() int
|
BreakpointSize() int
|
||||||
DerefTLS() bool
|
DerefTLS() bool
|
||||||
|
FixFrameUnwindContext(*frame.FrameContext) *frame.FrameContext
|
||||||
|
RegSize(uint64) int
|
||||||
|
RegistersToDwarfRegisters(Registers) op.DwarfRegisters
|
||||||
|
GoroutineToDwarfRegisters(*G) op.DwarfRegisters
|
||||||
}
|
}
|
||||||
|
|
||||||
// AMD64 represents the AMD64 CPU architecture.
|
// AMD64 represents the AMD64 CPU architecture.
|
||||||
@ -19,6 +31,12 @@ type AMD64 struct {
|
|||||||
goos string
|
goos string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
amd64DwarfIPRegNum uint64 = 16
|
||||||
|
amd64DwarfSPRegNum uint64 = 7
|
||||||
|
amd64DwarfBPRegNum uint64 = 6
|
||||||
|
)
|
||||||
|
|
||||||
// AMD64Arch returns an initialized AMD64
|
// AMD64Arch returns an initialized AMD64
|
||||||
// struct.
|
// struct.
|
||||||
func AMD64Arch(goos string) *AMD64 {
|
func AMD64Arch(goos string) *AMD64 {
|
||||||
@ -56,3 +74,182 @@ func (a *AMD64) BreakpointSize() int {
|
|||||||
func (a *AMD64) DerefTLS() bool {
|
func (a *AMD64) DerefTLS() bool {
|
||||||
return a.goos == "windows"
|
return a.goos == "windows"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FixFrameUnwindContext adds default architecture rules to fctxt or returns
|
||||||
|
// the default frame unwind context if fctxt is nil.
|
||||||
|
func (a *AMD64) FixFrameUnwindContext(fctxt *frame.FrameContext) *frame.FrameContext {
|
||||||
|
if fctxt == nil {
|
||||||
|
// When there's no frame descriptor entry use BP (the frame pointer) instead
|
||||||
|
// - return register is [bp + a.PtrSize()] (i.e. [cfa-a.PtrSize()])
|
||||||
|
// - cfa is bp + a.PtrSize()*2
|
||||||
|
// - bp is [bp] (i.e. [cfa-a.PtrSize()*2])
|
||||||
|
// - sp is cfa
|
||||||
|
|
||||||
|
return &frame.FrameContext{
|
||||||
|
RetAddrReg: amd64DwarfIPRegNum,
|
||||||
|
Regs: map[uint64]frame.DWRule{
|
||||||
|
amd64DwarfIPRegNum: frame.DWRule{
|
||||||
|
Rule: frame.RuleOffset,
|
||||||
|
Offset: int64(-a.PtrSize()),
|
||||||
|
},
|
||||||
|
amd64DwarfBPRegNum: frame.DWRule{
|
||||||
|
Rule: frame.RuleOffset,
|
||||||
|
Offset: int64(-2 * a.PtrSize()),
|
||||||
|
},
|
||||||
|
amd64DwarfSPRegNum: frame.DWRule{
|
||||||
|
Rule: frame.RuleValOffset,
|
||||||
|
Offset: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CFA: frame.DWRule{
|
||||||
|
Rule: frame.RuleCFA,
|
||||||
|
Reg: amd64DwarfBPRegNum,
|
||||||
|
Offset: int64(2 * a.PtrSize()),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We assume that RBP is the frame pointer and we want to keep it updated,
|
||||||
|
// so that we can use it to unwind the stack even when we encounter frames
|
||||||
|
// without descriptor entries.
|
||||||
|
// If there isn't a rule already we emit one.
|
||||||
|
if fctxt.Regs[amd64DwarfBPRegNum].Rule == frame.RuleUndefined {
|
||||||
|
fctxt.Regs[amd64DwarfBPRegNum] = frame.DWRule{
|
||||||
|
Rule: frame.RuleRegOffset,
|
||||||
|
Reg: amd64DwarfBPRegNum,
|
||||||
|
Offset: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fctxt
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegSize returns the size (in bytes) of register regnum.
|
||||||
|
// The mapping between hardware registers and DWARF registers is specified
|
||||||
|
// in the System V ABI AMD64 Architecture Processor Supplement page 57,
|
||||||
|
// figure 3.36
|
||||||
|
// https://www.uclibc.org/docs/psABI-x86_64.pdf
|
||||||
|
func (a *AMD64) RegSize(regnum uint64) int {
|
||||||
|
// XMM registers
|
||||||
|
if regnum > amd64DwarfIPRegNum && regnum <= 32 {
|
||||||
|
return 16
|
||||||
|
}
|
||||||
|
// x87 registers
|
||||||
|
if regnum >= 33 && regnum <= 40 {
|
||||||
|
return 10
|
||||||
|
}
|
||||||
|
return 8
|
||||||
|
}
|
||||||
|
|
||||||
|
// The mapping between hardware registers and DWARF registers is specified
|
||||||
|
// in the System V ABI AMD64 Architecture Processor Supplement page 57,
|
||||||
|
// figure 3.36
|
||||||
|
// https://www.uclibc.org/docs/psABI-x86_64.pdf
|
||||||
|
|
||||||
|
var asm64DwarfToHardware = map[int]x86asm.Reg{
|
||||||
|
0: x86asm.RAX,
|
||||||
|
1: x86asm.RDX,
|
||||||
|
2: x86asm.RCX,
|
||||||
|
3: x86asm.RBX,
|
||||||
|
4: x86asm.RSI,
|
||||||
|
5: x86asm.RDI,
|
||||||
|
8: x86asm.R8,
|
||||||
|
9: x86asm.R9,
|
||||||
|
10: x86asm.R10,
|
||||||
|
11: x86asm.R11,
|
||||||
|
12: x86asm.R12,
|
||||||
|
13: x86asm.R13,
|
||||||
|
14: x86asm.R14,
|
||||||
|
15: x86asm.R15,
|
||||||
|
}
|
||||||
|
|
||||||
|
var amd64DwarfToName = map[int]string{
|
||||||
|
17: "XMM0",
|
||||||
|
18: "XMM1",
|
||||||
|
19: "XMM2",
|
||||||
|
20: "XMM3",
|
||||||
|
21: "XMM4",
|
||||||
|
22: "XMM5",
|
||||||
|
23: "XMM6",
|
||||||
|
24: "XMM7",
|
||||||
|
25: "XMM8",
|
||||||
|
26: "XMM9",
|
||||||
|
27: "XMM10",
|
||||||
|
28: "XMM11",
|
||||||
|
29: "XMM12",
|
||||||
|
30: "XMM13",
|
||||||
|
31: "XMM14",
|
||||||
|
32: "XMM15",
|
||||||
|
33: "ST(0)",
|
||||||
|
34: "ST(1)",
|
||||||
|
35: "ST(2)",
|
||||||
|
36: "ST(3)",
|
||||||
|
37: "ST(4)",
|
||||||
|
38: "ST(5)",
|
||||||
|
39: "ST(6)",
|
||||||
|
40: "ST(7)",
|
||||||
|
49: "Eflags",
|
||||||
|
50: "Es",
|
||||||
|
51: "Cs",
|
||||||
|
52: "Ss",
|
||||||
|
53: "Ds",
|
||||||
|
54: "Fs",
|
||||||
|
55: "Gs",
|
||||||
|
58: "Fs_base",
|
||||||
|
59: "Gs_base",
|
||||||
|
64: "MXCSR",
|
||||||
|
65: "CW",
|
||||||
|
66: "SW",
|
||||||
|
}
|
||||||
|
|
||||||
|
func maxAmd64DwarfRegister() int {
|
||||||
|
max := int(amd64DwarfIPRegNum)
|
||||||
|
for i := range asm64DwarfToHardware {
|
||||||
|
if i > max {
|
||||||
|
max = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for i := range amd64DwarfToName {
|
||||||
|
if i > max {
|
||||||
|
max = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegistersToDwarfRegisters converts hardware registers to the format used
|
||||||
|
// by the DWARF expression interpreter.
|
||||||
|
func (a *AMD64) RegistersToDwarfRegisters(regs Registers) op.DwarfRegisters {
|
||||||
|
dregs := make([]*op.DwarfRegister, maxAmd64DwarfRegister()+1)
|
||||||
|
|
||||||
|
dregs[amd64DwarfIPRegNum] = op.DwarfRegisterFromUint64(regs.PC())
|
||||||
|
dregs[amd64DwarfSPRegNum] = op.DwarfRegisterFromUint64(regs.SP())
|
||||||
|
dregs[amd64DwarfBPRegNum] = op.DwarfRegisterFromUint64(regs.BP())
|
||||||
|
|
||||||
|
for dwarfReg, asmReg := range asm64DwarfToHardware {
|
||||||
|
v, err := regs.Get(int(asmReg))
|
||||||
|
if err == nil {
|
||||||
|
dregs[dwarfReg] = op.DwarfRegisterFromUint64(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, reg := range regs.Slice() {
|
||||||
|
for dwarfReg, regName := range amd64DwarfToName {
|
||||||
|
if regName == reg.Name {
|
||||||
|
dregs[dwarfReg] = op.DwarfRegisterFromBytes(reg.Bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return op.DwarfRegisters{Regs: dregs, ByteOrder: binary.LittleEndian, PCRegNum: amd64DwarfIPRegNum, SPRegNum: amd64DwarfSPRegNum}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GoroutineToDwarfRegisters extract the saved DWARF registers from a parked
|
||||||
|
// goroutine in the format used by the DWARF expression interpreter.
|
||||||
|
func (a *AMD64) GoroutineToDwarfRegisters(g *G) op.DwarfRegisters {
|
||||||
|
dregs := make([]*op.DwarfRegister, amd64DwarfIPRegNum+1)
|
||||||
|
dregs[amd64DwarfIPRegNum] = op.DwarfRegisterFromUint64(g.PC)
|
||||||
|
dregs[amd64DwarfSPRegNum] = op.DwarfRegisterFromUint64(g.SP)
|
||||||
|
dregs[amd64DwarfBPRegNum] = op.DwarfRegisterFromUint64(g.BP)
|
||||||
|
return op.DwarfRegisters{Regs: dregs, ByteOrder: binary.LittleEndian, PCRegNum: amd64DwarfIPRegNum, SPRegNum: amd64DwarfSPRegNum}
|
||||||
|
}
|
||||||
|
|||||||
@ -290,7 +290,7 @@ func StepOut(dbp Process) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sameGCond := SameGoroutineCondition(selg)
|
sameGCond := SameGoroutineCondition(selg)
|
||||||
retFrameCond := andFrameoffCondition(sameGCond, retframe.CFA-int64(retframe.StackHi))
|
retFrameCond := andFrameoffCondition(sameGCond, retframe.Regs.CFA-int64(retframe.StackHi))
|
||||||
|
|
||||||
var deferpc uint64 = 0
|
var deferpc uint64 = 0
|
||||||
if filepath.Ext(topframe.Current.File) == ".go" {
|
if filepath.Ext(topframe.Current.File) == ".go" {
|
||||||
@ -485,12 +485,12 @@ func ConvertEvalScope(dbp Process, gid, frame int) (*EvalScope, error) {
|
|||||||
return nil, fmt.Errorf("Frame %d does not exist in goroutine %d", frame, gid)
|
return nil, fmt.Errorf("Frame %d does not exist in goroutine %d", frame, gid)
|
||||||
}
|
}
|
||||||
|
|
||||||
PC, CFA := locs[frame].Current.PC, locs[frame].CFA
|
PC, CFA := locs[frame].Current.PC, locs[frame].Regs.CFA
|
||||||
|
|
||||||
return &EvalScope{PC, CFA, thread, g.variable, dbp.BinInfo(), g.stackhi}, nil
|
return &EvalScope{PC, CFA, thread, g.variable, dbp.BinInfo(), g.stackhi}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FrameToScope returns a new EvalScope for this frame
|
// FrameToScope returns a new EvalScope for this frame
|
||||||
func FrameToScope(p Process, frame Stackframe) *EvalScope {
|
func FrameToScope(p Process, frame Stackframe) *EvalScope {
|
||||||
return &EvalScope{frame.Current.PC, frame.CFA, p.CurrentThread(), nil, p.BinInfo(), frame.StackHi}
|
return &EvalScope{frame.Current.PC, frame.Regs.CFA, p.CurrentThread(), nil, p.BinInfo(), frame.StackHi}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -930,7 +930,7 @@ func TestStacktraceGoroutine(t *testing.T) {
|
|||||||
if locations[i].Call.Fn != nil {
|
if locations[i].Call.Fn != nil {
|
||||||
name = locations[i].Call.Fn.Name
|
name = locations[i].Call.Fn.Name
|
||||||
}
|
}
|
||||||
t.Logf("\t%s:%d %s\n", locations[i].Call.File, locations[i].Call.Line, name)
|
t.Logf("\t%s:%d %s (%#x)\n", locations[i].Call.File, locations[i].Call.Line, name, locations[i].Current.PC)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2763,7 +2763,7 @@ func TestStacktraceWithBarriers(t *testing.T) {
|
|||||||
if frame.Current.Fn != nil {
|
if frame.Current.Fn != nil {
|
||||||
name = frame.Current.Fn.Name
|
name = frame.Current.Fn.Name
|
||||||
}
|
}
|
||||||
t.Logf("\t%s [CFA: %x Ret: %x] at %s:%d", name, frame.CFA, frame.Ret, frame.Current.File, frame.Current.Line)
|
t.Logf("\t%s [CFA: %x Ret: %x] at %s:%d", name, frame.Regs.CFA, frame.Ret, frame.Current.File, frame.Current.Line)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !found {
|
if !found {
|
||||||
@ -2977,6 +2977,9 @@ func TestIssue893(t *testing.T) {
|
|||||||
if _, ok := err.(proc.ThreadBlockedError); ok {
|
if _, ok := err.(proc.ThreadBlockedError); ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if _, ok := err.(*proc.NoSourceForPCError); ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
assertNoError(err, t, "Next")
|
assertNoError(err, t, "Next")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,26 +29,35 @@ type Registers interface {
|
|||||||
|
|
||||||
type Register struct {
|
type Register struct {
|
||||||
Name string
|
Name string
|
||||||
|
Bytes []byte
|
||||||
Value string
|
Value string
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppendWordReg appends a word (16 bit) register to regs.
|
// AppendWordReg appends a word (16 bit) register to regs.
|
||||||
func AppendWordReg(regs []Register, name string, value uint16) []Register {
|
func AppendWordReg(regs []Register, name string, value uint16) []Register {
|
||||||
return append(regs, Register{name, fmt.Sprintf("%#04x", value)})
|
var buf bytes.Buffer
|
||||||
|
binary.Write(&buf, binary.LittleEndian, value)
|
||||||
|
return append(regs, Register{name, buf.Bytes(), fmt.Sprintf("%#04x", value)})
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppendDwordReg appends a double word (32 bit) register to regs.
|
// AppendDwordReg appends a double word (32 bit) register to regs.
|
||||||
func AppendDwordReg(regs []Register, name string, value uint32) []Register {
|
func AppendDwordReg(regs []Register, name string, value uint32) []Register {
|
||||||
return append(regs, Register{name, fmt.Sprintf("%#08x", value)})
|
var buf bytes.Buffer
|
||||||
|
binary.Write(&buf, binary.LittleEndian, value)
|
||||||
|
return append(regs, Register{name, buf.Bytes(), fmt.Sprintf("%#08x", value)})
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppendQwordReg appends a quad word (64 bit) register to regs.
|
// AppendQwordReg appends a quad word (64 bit) register to regs.
|
||||||
func AppendQwordReg(regs []Register, name string, value uint64) []Register {
|
func AppendQwordReg(regs []Register, name string, value uint64) []Register {
|
||||||
return append(regs, Register{name, fmt.Sprintf("%#016x", value)})
|
var buf bytes.Buffer
|
||||||
|
binary.Write(&buf, binary.LittleEndian, value)
|
||||||
|
return append(regs, Register{name, buf.Bytes(), fmt.Sprintf("%#016x", value)})
|
||||||
}
|
}
|
||||||
|
|
||||||
func appendFlagReg(regs []Register, name string, value uint64, descr flagRegisterDescr, size int) []Register {
|
func appendFlagReg(regs []Register, name string, value uint64, descr flagRegisterDescr, size int) []Register {
|
||||||
return append(regs, Register{name, descr.Describe(value, size)})
|
var buf bytes.Buffer
|
||||||
|
binary.Write(&buf, binary.LittleEndian, value)
|
||||||
|
return append(regs, Register{name, buf.Bytes()[:size], descr.Describe(value, size)})
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppendEflagReg appends EFLAG register to regs.
|
// AppendEflagReg appends EFLAG register to regs.
|
||||||
@ -114,7 +123,11 @@ func AppendX87Reg(regs []Register, index int, exponent uint16, mantissa uint64)
|
|||||||
f = sign * math.Ldexp(significand, int(exponent-_EXP_BIAS))
|
f = sign * math.Ldexp(significand, int(exponent-_EXP_BIAS))
|
||||||
}
|
}
|
||||||
|
|
||||||
return append(regs, Register{fmt.Sprintf("ST(%d)", index), fmt.Sprintf("%#04x%016x\t%g", exponent, mantissa, f)})
|
var buf bytes.Buffer
|
||||||
|
binary.Write(&buf, binary.LittleEndian, exponent)
|
||||||
|
binary.Write(&buf, binary.LittleEndian, mantissa)
|
||||||
|
|
||||||
|
return append(regs, Register{fmt.Sprintf("ST(%d)", index), buf.Bytes(), fmt.Sprintf("%#04x%016x\t%g", exponent, mantissa, f)})
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppendSSEReg appends a 256 bit SSE register to regs.
|
// AppendSSEReg appends a 256 bit SSE register to regs.
|
||||||
@ -151,7 +164,7 @@ func AppendSSEReg(regs []Register, name string, xmm []byte) []Register {
|
|||||||
}
|
}
|
||||||
fmt.Fprintf(&out, "\tv4_float={ %g %g %g %g }", v4[0], v4[1], v4[2], v4[3])
|
fmt.Fprintf(&out, "\tv4_float={ %g %g %g %g }", v4[0], v4[1], v4[2], v4[3])
|
||||||
|
|
||||||
return append(regs, Register{name, out.String()})
|
return append(regs, Register{name, xmm, out.String()})
|
||||||
}
|
}
|
||||||
|
|
||||||
var UnknownRegisterError = errors.New("unknown register")
|
var UnknownRegisterError = errors.New("unknown register")
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/derekparker/delve/pkg/dwarf/frame"
|
"github.com/derekparker/delve/pkg/dwarf/frame"
|
||||||
|
"github.com/derekparker/delve/pkg/dwarf/op"
|
||||||
)
|
)
|
||||||
|
|
||||||
// This code is partly adaped from runtime.gentraceback in
|
// This code is partly adaped from runtime.gentraceback in
|
||||||
@ -28,8 +29,8 @@ type Stackframe struct {
|
|||||||
Current Location
|
Current Location
|
||||||
// Address of the call instruction for the function above on the call stack.
|
// Address of the call instruction for the function above on the call stack.
|
||||||
Call Location
|
Call Location
|
||||||
// Start address of the stack frame.
|
// Frame registers.
|
||||||
CFA int64
|
Regs op.DwarfRegisters
|
||||||
// High address of the stack.
|
// High address of the stack.
|
||||||
StackHi uint64
|
StackHi uint64
|
||||||
// Return address for this stack frame (as read from the stack frame itself).
|
// Return address for this stack frame (as read from the stack frame itself).
|
||||||
@ -43,11 +44,11 @@ type Stackframe struct {
|
|||||||
// ThreadStacktrace returns the stack trace for thread.
|
// ThreadStacktrace returns the stack trace for thread.
|
||||||
// Note the locations in the array are return addresses not call addresses.
|
// Note the locations in the array are return addresses not call addresses.
|
||||||
func ThreadStacktrace(thread Thread, depth int) ([]Stackframe, error) {
|
func ThreadStacktrace(thread Thread, depth int) ([]Stackframe, error) {
|
||||||
regs, err := thread.Registers(false)
|
regs, err := thread.Registers(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
it := newStackIterator(thread.BinInfo(), thread, regs.PC(), regs.SP(), regs.BP(), 0, nil, -1)
|
it := newStackIterator(thread.BinInfo(), thread, thread.BinInfo().Arch.RegistersToDwarfRegisters(regs), 0, nil, -1)
|
||||||
return it.stacktrace(depth)
|
return it.stacktrace(depth)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,13 +58,13 @@ func (g *G) stackIterator() (*stackIterator, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if g.Thread != nil {
|
if g.Thread != nil {
|
||||||
regs, err := g.Thread.Registers(false)
|
regs, err := g.Thread.Registers(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return newStackIterator(g.variable.bi, g.Thread, regs.PC(), regs.SP(), regs.BP(), g.stackhi, stkbar, g.stkbarPos), nil
|
return newStackIterator(g.variable.bi, g.Thread, g.variable.bi.Arch.RegistersToDwarfRegisters(regs), g.stackhi, stkbar, g.stkbarPos), nil
|
||||||
}
|
}
|
||||||
return newStackIterator(g.variable.bi, g.variable.mem, g.PC, g.SP, 0, g.stackhi, stkbar, g.stkbarPos), nil
|
return newStackIterator(g.variable.bi, g.variable.mem, g.variable.bi.Arch.GoroutineToDwarfRegisters(g), g.stackhi, stkbar, g.stkbarPos), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stacktrace returns the stack trace for a goroutine.
|
// Stacktrace returns the stack trace for a goroutine.
|
||||||
@ -87,17 +88,21 @@ func (n NullAddrError) Error() string {
|
|||||||
// required to iterate and walk the program
|
// required to iterate and walk the program
|
||||||
// stack.
|
// stack.
|
||||||
type stackIterator struct {
|
type stackIterator struct {
|
||||||
pc, sp, bp uint64
|
pc uint64
|
||||||
top bool
|
top bool
|
||||||
atend bool
|
atend bool
|
||||||
frame Stackframe
|
frame Stackframe
|
||||||
bi *BinaryInfo
|
bi *BinaryInfo
|
||||||
mem MemoryReadWriter
|
mem MemoryReadWriter
|
||||||
err error
|
err error
|
||||||
|
|
||||||
stackhi uint64
|
stackhi uint64
|
||||||
stackBarrierPC uint64
|
stackBarrierPC uint64
|
||||||
stkbar []savedLR
|
stkbar []savedLR
|
||||||
|
|
||||||
|
// regs is the register set for the next frame, callFrameRegs is the
|
||||||
|
// register set for the call frame of the next frame.
|
||||||
|
regs, callFrameRegs op.DwarfRegisters
|
||||||
}
|
}
|
||||||
|
|
||||||
type savedLR struct {
|
type savedLR struct {
|
||||||
@ -105,27 +110,27 @@ type savedLR struct {
|
|||||||
val uint64
|
val uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
func newStackIterator(bi *BinaryInfo, mem MemoryReadWriter, pc, sp, bp, stackhi uint64, stkbar []savedLR, stkbarPos int) *stackIterator {
|
func newStackIterator(bi *BinaryInfo, mem MemoryReadWriter, regs op.DwarfRegisters, stackhi uint64, stkbar []savedLR, stkbarPos int) *stackIterator {
|
||||||
stackBarrierFunc := bi.LookupFunc[runtimeStackBarrier] // stack barriers were removed in Go 1.9
|
stackBarrierFunc := bi.LookupFunc[runtimeStackBarrier] // stack barriers were removed in Go 1.9
|
||||||
var stackBarrierPC uint64
|
var stackBarrierPC uint64
|
||||||
if stackBarrierFunc != nil && stkbar != nil {
|
if stackBarrierFunc != nil && stkbar != nil {
|
||||||
stackBarrierPC = stackBarrierFunc.Entry
|
stackBarrierPC = stackBarrierFunc.Entry
|
||||||
fn := bi.PCToFunc(pc)
|
fn := bi.PCToFunc(regs.PC())
|
||||||
if fn != nil && fn.Name == runtimeStackBarrier {
|
if fn != nil && fn.Name == runtimeStackBarrier {
|
||||||
// We caught the goroutine as it's executing the stack barrier, we must
|
// We caught the goroutine as it's executing the stack barrier, we must
|
||||||
// determine whether or not g.stackPos has already been incremented or not.
|
// determine whether or not g.stackPos has already been incremented or not.
|
||||||
if len(stkbar) > 0 && stkbar[stkbarPos].ptr < sp {
|
if len(stkbar) > 0 && stkbar[stkbarPos].ptr < regs.SP() {
|
||||||
// runtime.stackBarrier has not incremented stkbarPos.
|
// runtime.stackBarrier has not incremented stkbarPos.
|
||||||
} else if stkbarPos > 0 && stkbar[stkbarPos-1].ptr < sp {
|
} else if stkbarPos > 0 && stkbar[stkbarPos-1].ptr < regs.SP() {
|
||||||
// runtime.stackBarrier has incremented stkbarPos.
|
// runtime.stackBarrier has incremented stkbarPos.
|
||||||
stkbarPos--
|
stkbarPos--
|
||||||
} else {
|
} else {
|
||||||
return &stackIterator{err: fmt.Errorf("failed to unwind through stackBarrier at SP %x", sp)}
|
return &stackIterator{err: fmt.Errorf("failed to unwind through stackBarrier at SP %x", regs.SP())}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stkbar = stkbar[stkbarPos:]
|
stkbar = stkbar[stkbarPos:]
|
||||||
}
|
}
|
||||||
return &stackIterator{pc: pc, sp: sp, bp: bp, top: true, bi: bi, mem: mem, err: nil, atend: false, stackhi: stackhi, stackBarrierPC: stackBarrierPC, stkbar: stkbar}
|
return &stackIterator{pc: regs.PC(), regs: regs, top: true, bi: bi, mem: mem, err: nil, atend: false, stackhi: stackhi, stackBarrierPC: stackBarrierPC, stkbar: stkbar}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next points the iterator to the next stack frame.
|
// Next points the iterator to the next stack frame.
|
||||||
@ -133,16 +138,8 @@ func (it *stackIterator) Next() bool {
|
|||||||
if it.err != nil || it.atend {
|
if it.err != nil || it.atend {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
it.frame, it.err = it.frameInfo(it.pc, it.sp, it.bp, it.top)
|
ret, retaddr := it.advanceRegs()
|
||||||
if it.err != nil {
|
it.frame = it.newStackframe(ret, retaddr)
|
||||||
if _, nofde := it.err.(*frame.NoFDEForPCError); nofde && !it.top {
|
|
||||||
it.frame = Stackframe{Current: Location{PC: it.pc, File: "?", Line: -1}, Call: Location{PC: it.pc, File: "?", Line: -1}, CFA: 0, Ret: 0}
|
|
||||||
it.atend = true
|
|
||||||
it.err = nil
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if it.frame.Ret <= 0 {
|
if it.frame.Ret <= 0 {
|
||||||
it.atend = true
|
it.atend = true
|
||||||
@ -163,16 +160,12 @@ func (it *stackIterator) Next() bool {
|
|||||||
|
|
||||||
it.top = false
|
it.top = false
|
||||||
it.pc = it.frame.Ret
|
it.pc = it.frame.Ret
|
||||||
it.sp = uint64(it.frame.CFA)
|
it.regs = it.callFrameRegs
|
||||||
it.bp, _ = readUintRaw(it.mem, uintptr(it.bp), int64(it.bi.Arch.PtrSize()))
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Frame returns the frame the iterator is pointing at.
|
// Frame returns the frame the iterator is pointing at.
|
||||||
func (it *stackIterator) Frame() Stackframe {
|
func (it *stackIterator) Frame() Stackframe {
|
||||||
if it.err != nil {
|
|
||||||
panic(it.err)
|
|
||||||
}
|
|
||||||
return it.frame
|
return it.frame
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,42 +174,28 @@ func (it *stackIterator) Err() error {
|
|||||||
return it.err
|
return it.err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it *stackIterator) frameInfo(pc, sp, bp uint64, top bool) (Stackframe, error) {
|
func (it *stackIterator) newStackframe(ret, retaddr uint64) Stackframe {
|
||||||
fde, err := it.bi.frameEntries.FDEForPC(pc)
|
|
||||||
if _, nofde := err.(*frame.NoFDEForPCError); nofde {
|
|
||||||
if bp == 0 {
|
|
||||||
return Stackframe{}, err
|
|
||||||
}
|
|
||||||
// When no FDE is available attempt to use BP instead
|
|
||||||
retaddr := uintptr(int(bp) + it.bi.Arch.PtrSize())
|
|
||||||
cfa := int64(retaddr) + int64(it.bi.Arch.PtrSize())
|
|
||||||
return it.newStackframe(pc, cfa, retaddr, nil, top)
|
|
||||||
}
|
|
||||||
|
|
||||||
spoffset, retoffset := fde.ReturnAddressOffset(pc)
|
|
||||||
cfa := int64(sp) + spoffset
|
|
||||||
|
|
||||||
retaddr := uintptr(cfa + retoffset)
|
|
||||||
return it.newStackframe(pc, cfa, retaddr, fde, top)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *stackIterator) newStackframe(pc uint64, cfa int64, retaddr uintptr, fde *frame.FrameDescriptionEntry, top bool) (Stackframe, error) {
|
|
||||||
if retaddr == 0 {
|
if retaddr == 0 {
|
||||||
return Stackframe{}, NullAddrError{}
|
it.err = NullAddrError{}
|
||||||
|
return Stackframe{}
|
||||||
}
|
}
|
||||||
f, l, fn := it.bi.PCToLine(pc)
|
f, l, fn := it.bi.PCToLine(it.pc)
|
||||||
ret, err := readUintRaw(it.mem, retaddr, int64(it.bi.Arch.PtrSize()))
|
if fn == nil {
|
||||||
if err != nil {
|
f = "?"
|
||||||
it.err = err
|
l = -1
|
||||||
}
|
}
|
||||||
r := Stackframe{Current: Location{PC: pc, File: f, Line: l, Fn: fn}, CFA: cfa, Ret: ret, addrret: uint64(retaddr), StackHi: it.stackhi}
|
r := Stackframe{Current: Location{PC: it.pc, File: f, Line: l, Fn: fn}, Regs: it.regs, Ret: ret, addrret: retaddr, StackHi: it.stackhi}
|
||||||
if !top {
|
if !it.top {
|
||||||
r.Call.File, r.Call.Line, r.Call.Fn = it.bi.PCToLine(pc - 1)
|
r.Call.File, r.Call.Line, r.Call.Fn = it.bi.PCToLine(it.pc - 1)
|
||||||
|
if r.Call.Fn == nil {
|
||||||
|
r.Call.File = "?"
|
||||||
|
r.Call.Line = -1
|
||||||
|
}
|
||||||
r.Call.PC = r.Current.PC
|
r.Call.PC = r.Current.PC
|
||||||
} else {
|
} else {
|
||||||
r.Call = r.Current
|
r.Call = r.Current
|
||||||
}
|
}
|
||||||
return r, nil
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
func (it *stackIterator) stacktrace(depth int) ([]Stackframe, error) {
|
func (it *stackIterator) stacktrace(depth int) ([]Stackframe, error) {
|
||||||
@ -238,3 +217,102 @@ func (it *stackIterator) stacktrace(depth int) ([]Stackframe, error) {
|
|||||||
}
|
}
|
||||||
return frames, nil
|
return frames, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// advanceRegs calculates it.callFrameRegs using it.regs and the frame
|
||||||
|
// descriptor entry for the current stack frame.
|
||||||
|
// it.regs.CallFrameCFA is updated.
|
||||||
|
func (it *stackIterator) advanceRegs() (ret uint64, retaddr uint64) {
|
||||||
|
fde, err := it.bi.frameEntries.FDEForPC(it.pc)
|
||||||
|
var framectx *frame.FrameContext
|
||||||
|
if _, nofde := err.(*frame.NoFDEForPCError); nofde {
|
||||||
|
framectx = it.bi.Arch.FixFrameUnwindContext(nil)
|
||||||
|
} else {
|
||||||
|
framectx = it.bi.Arch.FixFrameUnwindContext(fde.EstablishFrame(it.pc))
|
||||||
|
}
|
||||||
|
|
||||||
|
cfareg, err := it.executeFrameRegRule(0, framectx.CFA, 0)
|
||||||
|
if cfareg == nil {
|
||||||
|
it.err = fmt.Errorf("CFA becomes undefined at PC %#x", it.pc)
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
it.regs.CFA = int64(cfareg.Uint64Val)
|
||||||
|
|
||||||
|
it.callFrameRegs = op.DwarfRegisters{ByteOrder: it.regs.ByteOrder, PCRegNum: it.regs.PCRegNum, SPRegNum: it.regs.SPRegNum}
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
// however neither Go nor GCC do this.
|
||||||
|
// In the following line we copy GDB's behaviour by assuming this is
|
||||||
|
// implicit.
|
||||||
|
// See also the comment in dwarf2_frame_default_init in
|
||||||
|
// $GDB_SOURCE/dwarf2-frame.c
|
||||||
|
it.callFrameRegs.AddReg(uint64(amd64DwarfSPRegNum), cfareg)
|
||||||
|
|
||||||
|
for i, regRule := range framectx.Regs {
|
||||||
|
reg, err := it.executeFrameRegRule(i, regRule, it.regs.CFA)
|
||||||
|
it.callFrameRegs.AddReg(i, reg)
|
||||||
|
if i == framectx.RetAddrReg {
|
||||||
|
if reg == nil {
|
||||||
|
if err == nil {
|
||||||
|
err = fmt.Errorf("Undefined return address at %#x", it.pc)
|
||||||
|
}
|
||||||
|
it.err = err
|
||||||
|
} else {
|
||||||
|
ret = reg.Uint64Val
|
||||||
|
}
|
||||||
|
retaddr = uint64(it.regs.CFA + regRule.Offset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret, retaddr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *stackIterator) executeFrameRegRule(regnum uint64, rule frame.DWRule, cfa int64) (*op.DwarfRegister, error) {
|
||||||
|
switch rule.Rule {
|
||||||
|
default:
|
||||||
|
fallthrough
|
||||||
|
case frame.RuleUndefined:
|
||||||
|
return nil, nil
|
||||||
|
case frame.RuleSameVal:
|
||||||
|
return it.regs.Reg(regnum), nil
|
||||||
|
case frame.RuleOffset:
|
||||||
|
return it.readRegisterAt(regnum, uint64(cfa+rule.Offset))
|
||||||
|
case frame.RuleValOffset:
|
||||||
|
return op.DwarfRegisterFromUint64(uint64(cfa + rule.Offset)), nil
|
||||||
|
case frame.RuleRegister:
|
||||||
|
return it.regs.Reg(rule.Reg), nil
|
||||||
|
case frame.RuleExpression:
|
||||||
|
v, err := op.ExecuteStackProgram(it.regs, rule.Expression)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return it.readRegisterAt(regnum, uint64(v))
|
||||||
|
case frame.RuleValExpression:
|
||||||
|
v, err := op.ExecuteStackProgram(it.regs, rule.Expression)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return op.DwarfRegisterFromUint64(uint64(v)), nil
|
||||||
|
case frame.RuleArchitectural:
|
||||||
|
return nil, errors.New("architectural frame rules are unsupported")
|
||||||
|
case frame.RuleCFA:
|
||||||
|
if it.regs.Reg(rule.Reg) == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return op.DwarfRegisterFromUint64(uint64(int64(it.regs.Uint64Val(rule.Reg)) + rule.Offset)), nil
|
||||||
|
case frame.RuleRegOffset:
|
||||||
|
if it.regs.Reg(rule.Reg) == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return it.readRegisterAt(regnum, uint64(int64(it.regs.Uint64Val(rule.Reg))+rule.Offset))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (it *stackIterator) readRegisterAt(regnum uint64, addr uint64) (*op.DwarfRegister, error) {
|
||||||
|
buf := make([]byte, it.bi.Arch.RegSize(regnum))
|
||||||
|
_, err := it.mem.ReadMemory(buf, uintptr(addr))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return op.DwarfRegisterFromBytes(buf), nil
|
||||||
|
}
|
||||||
|
|||||||
@ -79,6 +79,14 @@ func topframe(g *G, thread Thread) (Stackframe, Stackframe, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type NoSourceForPCError struct {
|
||||||
|
pc uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err *NoSourceForPCError) Error() string {
|
||||||
|
return fmt.Sprintf("no source for pc %#x", err.pc)
|
||||||
|
}
|
||||||
|
|
||||||
// Set breakpoints at every line, and the return address. Also look for
|
// Set breakpoints at every line, and the return address. Also look for
|
||||||
// a deferred function and set a breakpoint there too.
|
// a deferred function and set a breakpoint there too.
|
||||||
// If stepInto is true it will also set breakpoints inside all
|
// If stepInto is true it will also set breakpoints inside all
|
||||||
@ -105,7 +113,7 @@ func next(dbp Process, stepInto bool) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if topframe.Current.Fn == nil {
|
if topframe.Current.Fn == nil {
|
||||||
return fmt.Errorf("no source for pc %#x", topframe.Current.PC)
|
return &NoSourceForPCError{topframe.Current.PC}
|
||||||
}
|
}
|
||||||
|
|
||||||
success := false
|
success := false
|
||||||
@ -133,8 +141,8 @@ func next(dbp Process, stepInto bool) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sameGCond := SameGoroutineCondition(selg)
|
sameGCond := SameGoroutineCondition(selg)
|
||||||
retFrameCond := andFrameoffCondition(sameGCond, retframe.CFA-int64(retframe.StackHi))
|
retFrameCond := andFrameoffCondition(sameGCond, retframe.Regs.CFA-int64(retframe.StackHi))
|
||||||
sameFrameCond := andFrameoffCondition(sameGCond, topframe.CFA-int64(topframe.StackHi))
|
sameFrameCond := andFrameoffCondition(sameGCond, topframe.Regs.CFA-int64(topframe.StackHi))
|
||||||
var sameOrRetFrameCond ast.Expr
|
var sameOrRetFrameCond ast.Expr
|
||||||
if sameGCond != nil {
|
if sameGCond != nil {
|
||||||
sameOrRetFrameCond = &ast.BinaryExpr{
|
sameOrRetFrameCond = &ast.BinaryExpr{
|
||||||
@ -142,8 +150,8 @@ func next(dbp Process, stepInto bool) error {
|
|||||||
X: sameGCond,
|
X: sameGCond,
|
||||||
Y: &ast.BinaryExpr{
|
Y: &ast.BinaryExpr{
|
||||||
Op: token.LOR,
|
Op: token.LOR,
|
||||||
X: frameoffCondition(topframe.CFA - int64(topframe.StackHi)),
|
X: frameoffCondition(topframe.Regs.CFA - int64(topframe.StackHi)),
|
||||||
Y: frameoffCondition(retframe.CFA - int64(retframe.StackHi)),
|
Y: frameoffCondition(retframe.Regs.CFA - int64(retframe.StackHi)),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -375,7 +383,7 @@ func ThreadScope(thread Thread) (*EvalScope, error) {
|
|||||||
if len(locations) < 1 {
|
if len(locations) < 1 {
|
||||||
return nil, errors.New("could not decode first frame")
|
return nil, errors.New("could not decode first frame")
|
||||||
}
|
}
|
||||||
return &EvalScope{locations[0].Current.PC, locations[0].CFA, thread, nil, thread.BinInfo(), 0}, nil
|
return &EvalScope{locations[0].Current.PC, locations[0].Regs.CFA, thread, nil, thread.BinInfo(), 0}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GoroutineScope returns an EvalScope for the goroutine running on this thread.
|
// GoroutineScope returns an EvalScope for the goroutine running on this thread.
|
||||||
@ -391,7 +399,7 @@ func GoroutineScope(thread Thread) (*EvalScope, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &EvalScope{locations[0].Current.PC, locations[0].CFA, thread, g.variable, thread.BinInfo(), g.stackhi}, nil
|
return &EvalScope{locations[0].Current.PC, locations[0].Regs.CFA, thread, g.variable, thread.BinInfo(), g.stackhi}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func onRuntimeBreakpoint(thread Thread) bool {
|
func onRuntimeBreakpoint(thread Thread) bool {
|
||||||
|
|||||||
@ -130,6 +130,7 @@ type G struct {
|
|||||||
ID int // Goroutine ID
|
ID int // Goroutine ID
|
||||||
PC uint64 // PC of goroutine when it was parked.
|
PC uint64 // PC of goroutine when it was parked.
|
||||||
SP uint64 // SP of goroutine when it was parked.
|
SP uint64 // SP of goroutine when it was parked.
|
||||||
|
BP uint64 // BP of goroutine when it was parked (go >= 1.7).
|
||||||
GoPC uint64 // PC of 'go' statement that created this goroutine.
|
GoPC uint64 // PC of 'go' statement that created this goroutine.
|
||||||
WaitReason string // Reason for goroutine being parked.
|
WaitReason string // Reason for goroutine being parked.
|
||||||
Status uint64
|
Status uint64
|
||||||
@ -397,6 +398,10 @@ func (gvar *Variable) parseG() (*G, error) {
|
|||||||
schedVar := gvar.fieldVariable("sched")
|
schedVar := gvar.fieldVariable("sched")
|
||||||
pc, _ := constant.Int64Val(schedVar.fieldVariable("pc").Value)
|
pc, _ := constant.Int64Val(schedVar.fieldVariable("pc").Value)
|
||||||
sp, _ := constant.Int64Val(schedVar.fieldVariable("sp").Value)
|
sp, _ := constant.Int64Val(schedVar.fieldVariable("sp").Value)
|
||||||
|
var bp int64
|
||||||
|
if bpvar := schedVar.fieldVariable("bp"); bpvar != nil && bpvar.Value != nil {
|
||||||
|
bp, _ = constant.Int64Val(bpvar.Value)
|
||||||
|
}
|
||||||
id, _ := constant.Int64Val(gvar.fieldVariable("goid").Value)
|
id, _ := constant.Int64Val(gvar.fieldVariable("goid").Value)
|
||||||
gopc, _ := constant.Int64Val(gvar.fieldVariable("gopc").Value)
|
gopc, _ := constant.Int64Val(gvar.fieldVariable("gopc").Value)
|
||||||
waitReason := ""
|
waitReason := ""
|
||||||
@ -424,6 +429,7 @@ func (gvar *Variable) parseG() (*G, error) {
|
|||||||
GoPC: uint64(gopc),
|
GoPC: uint64(gopc),
|
||||||
PC: uint64(pc),
|
PC: uint64(pc),
|
||||||
SP: uint64(sp),
|
SP: uint64(sp),
|
||||||
|
BP: uint64(bp),
|
||||||
WaitReason: waitReason,
|
WaitReason: waitReason,
|
||||||
Status: uint64(status),
|
Status: uint64(status),
|
||||||
CurrentLoc: Location{PC: uint64(pc), File: f, Line: l, Fn: fn},
|
CurrentLoc: Location{PC: uint64(pc), File: f, Line: l, Fn: fn},
|
||||||
@ -743,7 +749,7 @@ func (scope *EvalScope) extractVarInfoFromEntry(entry *dwarf.Entry) (*Variable,
|
|||||||
return nil, fmt.Errorf("type assertion failed")
|
return nil, fmt.Errorf("type assertion failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
addr, err := op.ExecuteStackProgram(scope.CFA, instructions)
|
addr, err := op.ExecuteStackProgram(op.DwarfRegisters{CFA: scope.CFA}, instructions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -860,7 +860,7 @@ func (d *Debugger) convertStacktrace(rawlocs []proc.Stackframe, cfg *proc.LoadCo
|
|||||||
for i := range rawlocs {
|
for i := range rawlocs {
|
||||||
frame := api.Stackframe{
|
frame := api.Stackframe{
|
||||||
Location: api.ConvertLocation(rawlocs[i].Call),
|
Location: api.ConvertLocation(rawlocs[i].Call),
|
||||||
FrameOffset: rawlocs[i].CFA - int64(rawlocs[i].StackHi),
|
FrameOffset: rawlocs[i].Regs.CFA - int64(rawlocs[i].StackHi),
|
||||||
}
|
}
|
||||||
if rawlocs[i].Err != nil {
|
if rawlocs[i].Err != nil {
|
||||||
frame.Err = rawlocs[i].Err.Error()
|
frame.Err = rawlocs[i].Err.Error()
|
||||||
|
|||||||
Reference in New Issue
Block a user