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 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
|
||||
|
||||
func NewFrameIndex() FrameDescriptionEntries {
|
||||
|
||||
@ -8,38 +8,28 @@ import (
|
||||
"github.com/derekparker/delve/pkg/dwarf/util"
|
||||
)
|
||||
|
||||
type CurrentFrameAddress struct {
|
||||
register uint64
|
||||
offset int64
|
||||
expression []byte
|
||||
rule byte
|
||||
}
|
||||
|
||||
type DWRule struct {
|
||||
rule byte
|
||||
offset int64
|
||||
newreg uint64
|
||||
expression []byte
|
||||
Rule Rule
|
||||
Offset int64
|
||||
Reg uint64
|
||||
Expression []byte
|
||||
}
|
||||
|
||||
type FrameContext struct {
|
||||
loc uint64
|
||||
order binary.ByteOrder
|
||||
address uint64
|
||||
cfa CurrentFrameAddress
|
||||
regs map[uint64]DWRule
|
||||
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
|
||||
}
|
||||
|
||||
func (fctx *FrameContext) CFAOffset() int64 {
|
||||
return fctx.cfa.offset
|
||||
}
|
||||
|
||||
// Instructions used to recreate the table from the .debug_frame data.
|
||||
const (
|
||||
DW_CFA_nop = 0x0 // No ops
|
||||
@ -73,15 +63,19 @@ const (
|
||||
)
|
||||
|
||||
// Rules defined for register values.
|
||||
type Rule byte
|
||||
|
||||
const (
|
||||
rule_undefined = iota
|
||||
rule_sameval
|
||||
rule_offset
|
||||
rule_valoffset
|
||||
rule_register
|
||||
rule_expression
|
||||
rule_valexpression
|
||||
rule_architectural
|
||||
RuleUndefined Rule = iota
|
||||
RuleSameVal
|
||||
RuleOffset
|
||||
RuleValOffset
|
||||
RuleRegister
|
||||
RuleExpression
|
||||
RuleValExpression
|
||||
RuleArchitectural
|
||||
RuleCFA // Value is rule.Reg + rule.Offset
|
||||
RuleRegOffset // Value is stored at address rule.Reg + rule.Offset
|
||||
)
|
||||
|
||||
const low_6_offset = 0x3f
|
||||
@ -124,7 +118,8 @@ func executeCIEInstructions(cie *CommonInformationEntry) *FrameContext {
|
||||
copy(initialInstructions, cie.InitialInstructions)
|
||||
frame := &FrameContext{
|
||||
cie: cie,
|
||||
regs: make(map[uint64]DWRule),
|
||||
Regs: make(map[uint64]DWRule),
|
||||
RetAddrReg: cie.ReturnAddressRegister,
|
||||
initialRegs: make(map[uint64]DWRule),
|
||||
prevRegs: make(map[uint64]DWRule),
|
||||
codeAlignment: cie.CodeAlignmentFactor,
|
||||
@ -142,9 +137,7 @@ func executeDwarfProgramUntilPC(fde *FrameDescriptionEntry, pc uint64) *FrameCon
|
||||
frame.order = fde.order
|
||||
frame.loc = fde.Begin()
|
||||
frame.address = pc
|
||||
fdeInstructions := make([]byte, len(fde.Instructions))
|
||||
copy(fdeInstructions, fde.Instructions)
|
||||
frame.ExecuteUntilPC(fdeInstructions)
|
||||
frame.ExecuteUntilPC(fde.Instructions)
|
||||
|
||||
return frame
|
||||
}
|
||||
@ -262,7 +255,7 @@ func offset(frame *FrameContext) {
|
||||
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) {
|
||||
@ -274,9 +267,9 @@ func restore(frame *FrameContext) {
|
||||
reg := uint64(b & low_6_offset)
|
||||
oldrule, ok := frame.initialRegs[reg]
|
||||
if ok {
|
||||
frame.regs[reg] = DWRule{offset: oldrule.offset, rule: rule_offset}
|
||||
frame.Regs[reg] = DWRule{Offset: oldrule.Offset, Rule: RuleOffset}
|
||||
} 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)
|
||||
)
|
||||
|
||||
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) {
|
||||
reg, _ := util.DecodeULEB128(frame.buf)
|
||||
frame.regs[reg] = DWRule{rule: rule_undefined}
|
||||
frame.Regs[reg] = DWRule{Rule: RuleUndefined}
|
||||
}
|
||||
|
||||
func samevalue(frame *FrameContext) {
|
||||
reg, _ := util.DecodeULEB128(frame.buf)
|
||||
frame.regs[reg] = DWRule{rule: rule_sameval}
|
||||
frame.Regs[reg] = DWRule{Rule: RuleSameVal}
|
||||
}
|
||||
|
||||
func register(frame *FrameContext) {
|
||||
reg1, _ := 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) {
|
||||
frame.prevRegs = frame.regs
|
||||
frame.prevRegs = frame.Regs
|
||||
}
|
||||
|
||||
func restorestate(frame *FrameContext) {
|
||||
frame.regs = frame.prevRegs
|
||||
frame.Regs = frame.prevRegs
|
||||
}
|
||||
|
||||
func restoreextended(frame *FrameContext) {
|
||||
@ -325,9 +318,9 @@ func restoreextended(frame *FrameContext) {
|
||||
|
||||
oldrule, ok := frame.initialRegs[reg]
|
||||
if ok {
|
||||
frame.regs[reg] = DWRule{offset: oldrule.offset, rule: rule_offset}
|
||||
frame.Regs[reg] = DWRule{Offset: oldrule.Offset, Rule: RuleOffset}
|
||||
} 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)
|
||||
offset, _ := util.DecodeULEB128(frame.buf)
|
||||
|
||||
frame.cfa.register = reg
|
||||
frame.cfa.offset = int64(offset)
|
||||
frame.CFA.Rule = RuleCFA
|
||||
frame.CFA.Reg = reg
|
||||
frame.CFA.Offset = int64(offset)
|
||||
}
|
||||
|
||||
func defcfaregister(frame *FrameContext) {
|
||||
reg, _ := util.DecodeULEB128(frame.buf)
|
||||
frame.cfa.register = reg
|
||||
frame.CFA.Reg = reg
|
||||
}
|
||||
|
||||
func defcfaoffset(frame *FrameContext) {
|
||||
offset, _ := util.DecodeULEB128(frame.buf)
|
||||
frame.cfa.offset = int64(offset)
|
||||
frame.CFA.Offset = int64(offset)
|
||||
}
|
||||
|
||||
func defcfasf(frame *FrameContext) {
|
||||
reg, _ := util.DecodeULEB128(frame.buf)
|
||||
offset, _ := util.DecodeSLEB128(frame.buf)
|
||||
|
||||
frame.cfa.register = reg
|
||||
frame.cfa.offset = offset * frame.dataAlignment
|
||||
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
|
||||
frame.CFA.Offset = offset
|
||||
}
|
||||
|
||||
func defcfaexpression(frame *FrameContext) {
|
||||
@ -369,8 +364,8 @@ func defcfaexpression(frame *FrameContext) {
|
||||
expr = frame.buf.Next(int(l))
|
||||
)
|
||||
|
||||
frame.cfa.expression = expr
|
||||
frame.cfa.rule = rule_expression
|
||||
frame.CFA.Expression = expr
|
||||
frame.CFA.Rule = RuleExpression
|
||||
}
|
||||
|
||||
func expression(frame *FrameContext) {
|
||||
@ -380,7 +375,7 @@ func expression(frame *FrameContext) {
|
||||
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) {
|
||||
@ -389,7 +384,7 @@ func offsetextendedsf(frame *FrameContext) {
|
||||
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) {
|
||||
@ -398,7 +393,7 @@ func valoffset(frame *FrameContext) {
|
||||
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) {
|
||||
@ -407,7 +402,7 @@ func valoffsetsf(frame *FrameContext) {
|
||||
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) {
|
||||
@ -417,7 +412,7 @@ func valexpression(frame *FrameContext) {
|
||||
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) {
|
||||
|
||||
@ -17,7 +17,14 @@ const (
|
||||
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{
|
||||
DW_OP_call_frame_cfa: callframecfa,
|
||||
@ -27,58 +34,75 @@ var oplut = map[byte]stackfn{
|
||||
DW_OP_plus_uconsts: plusuconsts,
|
||||
}
|
||||
|
||||
func ExecuteStackProgram(cfa int64, instructions []byte) (int64, error) {
|
||||
stack := make([]int64, 0, 3)
|
||||
buf := bytes.NewBuffer(instructions)
|
||||
func ExecuteStackProgram(regs DwarfRegisters, instructions []byte) (int64, error) {
|
||||
ctxt := &context{
|
||||
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]
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("invalid instruction %#v", opcode)
|
||||
}
|
||||
|
||||
stack, err = fn(buf, stack, cfa)
|
||||
err = fn(opcode, ctxt)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(stack) == 0 {
|
||||
if len(ctxt.stack) == 0 {
|
||||
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) {
|
||||
if cfa == 0 {
|
||||
return stack, fmt.Errorf("Could not retrieve CFA for current PC")
|
||||
func callframecfa(opcode byte, ctxt *context) error {
|
||||
if ctxt.CFA == 0 {
|
||||
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) {
|
||||
return append(stack, int64(binary.LittleEndian.Uint64(buf.Next(8)))), nil
|
||||
func addr(opcode byte, ctxt *context) error {
|
||||
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 (
|
||||
slen = len(stack)
|
||||
digits = stack[slen-2 : slen]
|
||||
st = stack[:slen-2]
|
||||
slen = len(ctxt.stack)
|
||||
digits = ctxt.stack[slen-2 : slen]
|
||||
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) {
|
||||
slen := len(stack)
|
||||
num, _ := util.DecodeULEB128(buf)
|
||||
stack[slen-1] = stack[slen-1] + int64(num)
|
||||
return stack, nil
|
||||
func plusuconsts(opcode byte, ctxt *context) error {
|
||||
slen := len(ctxt.stack)
|
||||
num, _ := util.DecodeULEB128(ctxt.buf)
|
||||
ctxt.stack[slen-1] = ctxt.stack[slen-1] + int64(num)
|
||||
return nil
|
||||
}
|
||||
|
||||
func consts(buf *bytes.Buffer, stack []int64, cfa int64) ([]int64, error) {
|
||||
num, _ := util.DecodeSLEB128(buf)
|
||||
return append(stack, num), nil
|
||||
func consts(opcode byte, ctxt *context) error {
|
||||
num, _ := util.DecodeSLEB128(ctxt.buf)
|
||||
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}
|
||||
expected = int64(56)
|
||||
)
|
||||
actual, err := ExecuteStackProgram(0, instructions)
|
||||
actual, err := ExecuteStackProgram(DwarfRegisters{}, instructions)
|
||||
if err != nil {
|
||||
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 {
|
||||
return 0, fmt.Errorf("type assertion failed")
|
||||
}
|
||||
addr, err := op.ExecuteStackProgram(0, instructions)
|
||||
addr, err := op.ExecuteStackProgram(op.DwarfRegisters{}, instructions)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@ -99,7 +99,7 @@ func (reader *Reader) AddrForMember(member string, initialInstructions []byte) (
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
addr, err := op.ExecuteStackProgram(0, append(initialInstructions, instructions...))
|
||||
addr, err := op.ExecuteStackProgram(op.DwarfRegisters{}, append(initialInstructions, instructions...))
|
||||
return uint64(addr), err
|
||||
}
|
||||
}
|
||||
|
||||
197
pkg/proc/arch.go
197
pkg/proc/arch.go
@ -1,5 +1,13 @@
|
||||
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
|
||||
// CPU architecture.
|
||||
type Arch interface {
|
||||
@ -7,6 +15,10 @@ type Arch interface {
|
||||
BreakpointInstruction() []byte
|
||||
BreakpointSize() int
|
||||
DerefTLS() bool
|
||||
FixFrameUnwindContext(*frame.FrameContext) *frame.FrameContext
|
||||
RegSize(uint64) int
|
||||
RegistersToDwarfRegisters(Registers) op.DwarfRegisters
|
||||
GoroutineToDwarfRegisters(*G) op.DwarfRegisters
|
||||
}
|
||||
|
||||
// AMD64 represents the AMD64 CPU architecture.
|
||||
@ -19,6 +31,12 @@ type AMD64 struct {
|
||||
goos string
|
||||
}
|
||||
|
||||
const (
|
||||
amd64DwarfIPRegNum uint64 = 16
|
||||
amd64DwarfSPRegNum uint64 = 7
|
||||
amd64DwarfBPRegNum uint64 = 6
|
||||
)
|
||||
|
||||
// AMD64Arch returns an initialized AMD64
|
||||
// struct.
|
||||
func AMD64Arch(goos string) *AMD64 {
|
||||
@ -56,3 +74,182 @@ func (a *AMD64) BreakpointSize() int {
|
||||
func (a *AMD64) DerefTLS() bool {
|
||||
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)
|
||||
retFrameCond := andFrameoffCondition(sameGCond, retframe.CFA-int64(retframe.StackHi))
|
||||
retFrameCond := andFrameoffCondition(sameGCond, retframe.Regs.CFA-int64(retframe.StackHi))
|
||||
|
||||
var deferpc uint64 = 0
|
||||
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)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// FrameToScope returns a new EvalScope for this frame
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
@ -2977,6 +2977,9 @@ func TestIssue893(t *testing.T) {
|
||||
if _, ok := err.(proc.ThreadBlockedError); ok {
|
||||
return
|
||||
}
|
||||
if _, ok := err.(*proc.NoSourceForPCError); ok {
|
||||
return
|
||||
}
|
||||
assertNoError(err, t, "Next")
|
||||
})
|
||||
}
|
||||
|
||||
@ -29,26 +29,35 @@ type Registers interface {
|
||||
|
||||
type Register struct {
|
||||
Name string
|
||||
Bytes []byte
|
||||
Value string
|
||||
}
|
||||
|
||||
// AppendWordReg appends a word (16 bit) register to regs.
|
||||
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.
|
||||
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.
|
||||
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 {
|
||||
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.
|
||||
@ -114,7 +123,11 @@ func AppendX87Reg(regs []Register, index int, exponent uint16, mantissa uint64)
|
||||
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.
|
||||
@ -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])
|
||||
|
||||
return append(regs, Register{name, out.String()})
|
||||
return append(regs, Register{name, xmm, out.String()})
|
||||
}
|
||||
|
||||
var UnknownRegisterError = errors.New("unknown register")
|
||||
|
||||
@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/derekparker/delve/pkg/dwarf/frame"
|
||||
"github.com/derekparker/delve/pkg/dwarf/op"
|
||||
)
|
||||
|
||||
// This code is partly adaped from runtime.gentraceback in
|
||||
@ -28,8 +29,8 @@ type Stackframe struct {
|
||||
Current Location
|
||||
// Address of the call instruction for the function above on the call stack.
|
||||
Call Location
|
||||
// Start address of the stack frame.
|
||||
CFA int64
|
||||
// Frame registers.
|
||||
Regs op.DwarfRegisters
|
||||
// High address of the stack.
|
||||
StackHi uint64
|
||||
// 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.
|
||||
// Note the locations in the array are return addresses not call addresses.
|
||||
func ThreadStacktrace(thread Thread, depth int) ([]Stackframe, error) {
|
||||
regs, err := thread.Registers(false)
|
||||
regs, err := thread.Registers(true)
|
||||
if err != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
@ -57,13 +58,13 @@ func (g *G) stackIterator() (*stackIterator, error) {
|
||||
return nil, err
|
||||
}
|
||||
if g.Thread != nil {
|
||||
regs, err := g.Thread.Registers(false)
|
||||
regs, err := g.Thread.Registers(true)
|
||||
if err != nil {
|
||||
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.
|
||||
@ -87,7 +88,7 @@ func (n NullAddrError) Error() string {
|
||||
// required to iterate and walk the program
|
||||
// stack.
|
||||
type stackIterator struct {
|
||||
pc, sp, bp uint64
|
||||
pc uint64
|
||||
top bool
|
||||
atend bool
|
||||
frame Stackframe
|
||||
@ -98,6 +99,10 @@ type stackIterator struct {
|
||||
stackhi uint64
|
||||
stackBarrierPC uint64
|
||||
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 {
|
||||
@ -105,27 +110,27 @@ type savedLR struct {
|
||||
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
|
||||
var stackBarrierPC uint64
|
||||
if stackBarrierFunc != nil && stkbar != nil {
|
||||
stackBarrierPC = stackBarrierFunc.Entry
|
||||
fn := bi.PCToFunc(pc)
|
||||
fn := bi.PCToFunc(regs.PC())
|
||||
if fn != nil && fn.Name == runtimeStackBarrier {
|
||||
// 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.
|
||||
if len(stkbar) > 0 && stkbar[stkbarPos].ptr < sp {
|
||||
if len(stkbar) > 0 && stkbar[stkbarPos].ptr < regs.SP() {
|
||||
// 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.
|
||||
stkbarPos--
|
||||
} 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:]
|
||||
}
|
||||
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.
|
||||
@ -133,16 +138,8 @@ func (it *stackIterator) Next() bool {
|
||||
if it.err != nil || it.atend {
|
||||
return false
|
||||
}
|
||||
it.frame, it.err = it.frameInfo(it.pc, it.sp, it.bp, it.top)
|
||||
if it.err != nil {
|
||||
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
|
||||
}
|
||||
ret, retaddr := it.advanceRegs()
|
||||
it.frame = it.newStackframe(ret, retaddr)
|
||||
|
||||
if it.frame.Ret <= 0 {
|
||||
it.atend = true
|
||||
@ -163,16 +160,12 @@ func (it *stackIterator) Next() bool {
|
||||
|
||||
it.top = false
|
||||
it.pc = it.frame.Ret
|
||||
it.sp = uint64(it.frame.CFA)
|
||||
it.bp, _ = readUintRaw(it.mem, uintptr(it.bp), int64(it.bi.Arch.PtrSize()))
|
||||
it.regs = it.callFrameRegs
|
||||
return true
|
||||
}
|
||||
|
||||
// Frame returns the frame the iterator is pointing at.
|
||||
func (it *stackIterator) Frame() Stackframe {
|
||||
if it.err != nil {
|
||||
panic(it.err)
|
||||
}
|
||||
return it.frame
|
||||
}
|
||||
|
||||
@ -181,42 +174,28 @@ func (it *stackIterator) Err() error {
|
||||
return it.err
|
||||
}
|
||||
|
||||
func (it *stackIterator) frameInfo(pc, sp, bp uint64, top bool) (Stackframe, error) {
|
||||
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) {
|
||||
func (it *stackIterator) newStackframe(ret, retaddr uint64) Stackframe {
|
||||
if retaddr == 0 {
|
||||
return Stackframe{}, NullAddrError{}
|
||||
it.err = NullAddrError{}
|
||||
return Stackframe{}
|
||||
}
|
||||
f, l, fn := it.bi.PCToLine(pc)
|
||||
ret, err := readUintRaw(it.mem, retaddr, int64(it.bi.Arch.PtrSize()))
|
||||
if err != nil {
|
||||
it.err = err
|
||||
f, l, fn := it.bi.PCToLine(it.pc)
|
||||
if fn == nil {
|
||||
f = "?"
|
||||
l = -1
|
||||
}
|
||||
r := Stackframe{Current: Location{PC: it.pc, File: f, Line: l, Fn: fn}, Regs: it.regs, Ret: ret, addrret: retaddr, StackHi: it.stackhi}
|
||||
if !it.top {
|
||||
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 := Stackframe{Current: Location{PC: pc, File: f, Line: l, Fn: fn}, CFA: cfa, Ret: ret, addrret: uint64(retaddr), StackHi: it.stackhi}
|
||||
if !top {
|
||||
r.Call.File, r.Call.Line, r.Call.Fn = it.bi.PCToLine(pc - 1)
|
||||
r.Call.PC = r.Current.PC
|
||||
} else {
|
||||
r.Call = r.Current
|
||||
}
|
||||
return r, nil
|
||||
return r
|
||||
}
|
||||
|
||||
func (it *stackIterator) stacktrace(depth int) ([]Stackframe, error) {
|
||||
@ -238,3 +217,102 @@ func (it *stackIterator) stacktrace(depth int) ([]Stackframe, error) {
|
||||
}
|
||||
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
|
||||
// a deferred function and set a breakpoint there too.
|
||||
// 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 {
|
||||
return fmt.Errorf("no source for pc %#x", topframe.Current.PC)
|
||||
return &NoSourceForPCError{topframe.Current.PC}
|
||||
}
|
||||
|
||||
success := false
|
||||
@ -133,8 +141,8 @@ func next(dbp Process, stepInto bool) error {
|
||||
}
|
||||
|
||||
sameGCond := SameGoroutineCondition(selg)
|
||||
retFrameCond := andFrameoffCondition(sameGCond, retframe.CFA-int64(retframe.StackHi))
|
||||
sameFrameCond := andFrameoffCondition(sameGCond, topframe.CFA-int64(topframe.StackHi))
|
||||
retFrameCond := andFrameoffCondition(sameGCond, retframe.Regs.CFA-int64(retframe.StackHi))
|
||||
sameFrameCond := andFrameoffCondition(sameGCond, topframe.Regs.CFA-int64(topframe.StackHi))
|
||||
var sameOrRetFrameCond ast.Expr
|
||||
if sameGCond != nil {
|
||||
sameOrRetFrameCond = &ast.BinaryExpr{
|
||||
@ -142,8 +150,8 @@ func next(dbp Process, stepInto bool) error {
|
||||
X: sameGCond,
|
||||
Y: &ast.BinaryExpr{
|
||||
Op: token.LOR,
|
||||
X: frameoffCondition(topframe.CFA - int64(topframe.StackHi)),
|
||||
Y: frameoffCondition(retframe.CFA - int64(retframe.StackHi)),
|
||||
X: frameoffCondition(topframe.Regs.CFA - int64(topframe.StackHi)),
|
||||
Y: frameoffCondition(retframe.Regs.CFA - int64(retframe.StackHi)),
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -375,7 +383,7 @@ func ThreadScope(thread Thread) (*EvalScope, error) {
|
||||
if len(locations) < 1 {
|
||||
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.
|
||||
@ -391,7 +399,7 @@ func GoroutineScope(thread Thread) (*EvalScope, error) {
|
||||
if err != nil {
|
||||
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 {
|
||||
|
||||
@ -130,6 +130,7 @@ type G struct {
|
||||
ID int // Goroutine ID
|
||||
PC uint64 // PC 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.
|
||||
WaitReason string // Reason for goroutine being parked.
|
||||
Status uint64
|
||||
@ -397,6 +398,10 @@ func (gvar *Variable) parseG() (*G, error) {
|
||||
schedVar := gvar.fieldVariable("sched")
|
||||
pc, _ := constant.Int64Val(schedVar.fieldVariable("pc").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)
|
||||
gopc, _ := constant.Int64Val(gvar.fieldVariable("gopc").Value)
|
||||
waitReason := ""
|
||||
@ -424,6 +429,7 @@ func (gvar *Variable) parseG() (*G, error) {
|
||||
GoPC: uint64(gopc),
|
||||
PC: uint64(pc),
|
||||
SP: uint64(sp),
|
||||
BP: uint64(bp),
|
||||
WaitReason: waitReason,
|
||||
Status: uint64(status),
|
||||
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")
|
||||
}
|
||||
|
||||
addr, err := op.ExecuteStackProgram(scope.CFA, instructions)
|
||||
addr, err := op.ExecuteStackProgram(op.DwarfRegisters{CFA: scope.CFA}, instructions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -860,7 +860,7 @@ func (d *Debugger) convertStacktrace(rawlocs []proc.Stackframe, cfg *proc.LoadCo
|
||||
for i := range rawlocs {
|
||||
frame := api.Stackframe{
|
||||
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 {
|
||||
frame.Err = rawlocs[i].Err.Error()
|
||||
|
||||
Reference in New Issue
Block a user