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:
aarzilli
2017-10-05 09:26:19 +02:00
committed by Derek Parker
parent 94b50d0f60
commit f4e2000fc8
14 changed files with 587 additions and 174 deletions

View File

@ -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 {

View File

@ -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) {

View File

@ -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
} }

View File

@ -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
View 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}
}

View File

@ -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
} }
} }

View File

@ -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}
}

View File

@ -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}
} }

View File

@ -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")
}) })
} }

View File

@ -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")

View File

@ -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
}

View File

@ -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 {

View File

@ -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
} }

View File

@ -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()