pkg/proc, pkg/dwarf/op: support DW_OP_piece, DW_OP_regX, DW_OP_fbreg

These are emitted by C compilers but also by the current development
version of the go compiler with the dwarflocationlists flag.
This commit is contained in:
aarzilli 2017-08-24 09:46:47 +02:00 committed by Derek Parker
parent f098915192
commit 99cad1044b
19 changed files with 1301 additions and 310 deletions

@ -34,7 +34,7 @@ func New() *Builder {
}
// Build closes b and returns all the dwarf sections.
func (b *Builder) Build() (abbrev, aranges, frame, info, line, pubnames, ranges, str []byte, err error) {
func (b *Builder) Build() (abbrev, aranges, frame, info, line, pubnames, ranges, str, loc []byte, err error) {
b.TagClose()
if len(b.tagStack) > 0 {
@ -45,6 +45,7 @@ func (b *Builder) Build() (abbrev, aranges, frame, info, line, pubnames, ranges,
abbrev = b.makeAbbrevTable()
info = b.info.Bytes()
binary.LittleEndian.PutUint32(info, uint32(len(info)-4))
loc = b.loc.Bytes()
return
}

@ -9,34 +9,33 @@ import (
"github.com/derekparker/delve/pkg/dwarf/util"
)
const (
DW_OP_addr = 0x3
DW_OP_call_frame_cfa = 0x9c
DW_OP_plus = 0x22
DW_OP_consts = 0x11
DW_OP_plus_uconsts = 0x23
)
type Opcode byte
type stackfn func(byte, *context) error
//go:generate go run ../../../scripts/gen-opcodes.go opcodes.table opcodes.go
type stackfn func(Opcode, *context) error
type context struct {
buf *bytes.Buffer
stack []int64
buf *bytes.Buffer
stack []int64
pieces []Piece
reg bool
DwarfRegisters
}
type Opcode byte
var oplut = map[Opcode]stackfn{
DW_OP_call_frame_cfa: callframecfa,
DW_OP_plus: plus,
DW_OP_consts: consts,
DW_OP_addr: addr,
DW_OP_plus_uconst: plusuconsts,
// Piece is a piece of memory stored either at an addres or in a register.
type Piece struct {
Size int
Addr int64
RegNum uint64
IsRegister bool
}
func ExecuteStackProgram(regs DwarfRegisters, instructions []byte) (int64, error) {
// ExecuteStackProgram executes a DWARF location expression and returns
// either an address (int64), or a slice of Pieces for location expressions
// that don't evaluate to an address (such as register and composite expressions).
func ExecuteStackProgram(regs DwarfRegisters, instructions []byte) (int64, []Piece, error) {
ctxt := &context{
buf: bytes.NewBuffer(instructions),
stack: make([]int64, 0, 3),
@ -44,42 +43,50 @@ func ExecuteStackProgram(regs DwarfRegisters, instructions []byte) (int64, error
}
for {
opcode, err := ctxt.buf.ReadByte()
opcodeByte, err := ctxt.buf.ReadByte()
if err != nil {
break
}
opcode := Opcode(opcodeByte)
if ctxt.reg && opcode != DW_OP_piece {
break
}
fn, ok := oplut[opcode]
if !ok {
return 0, fmt.Errorf("invalid instruction %#v", opcode)
return 0, nil, fmt.Errorf("invalid instruction %#v", opcode)
}
err = fn(opcode, ctxt)
if err != nil {
return 0, err
return 0, nil, err
}
}
if len(ctxt.stack) == 0 {
return 0, errors.New("empty OP stack")
if ctxt.pieces != nil {
return 0, ctxt.pieces, nil
}
return ctxt.stack[len(ctxt.stack)-1], nil
if len(ctxt.stack) == 0 {
return 0, nil, errors.New("empty OP stack")
}
return ctxt.stack[len(ctxt.stack)-1], nil, nil
}
func callframecfa(opcode byte, ctxt *context) error {
func callframecfa(opcode Opcode, ctxt *context) error {
if ctxt.CFA == 0 {
return fmt.Errorf("Could not retrieve call frame CFA for current PC")
return fmt.Errorf("Could not retrieve CFA for current PC")
}
ctxt.stack = append(ctxt.stack, int64(ctxt.CFA))
return nil
}
func addr(opcode byte, ctxt *context) error {
func addr(opcode Opcode, ctxt *context) error {
ctxt.stack = append(ctxt.stack, int64(binary.LittleEndian.Uint64(ctxt.buf.Next(8))))
return nil
}
func plus(opcode byte, ctxt *context) error {
func plus(opcode Opcode, ctxt *context) error {
var (
slen = len(ctxt.stack)
digits = ctxt.stack[slen-2 : slen]
@ -90,21 +97,50 @@ func plus(opcode byte, ctxt *context) error {
return nil
}
func plusuconsts(opcode byte, ctxt *context) error {
func plusuconsts(opcode Opcode, 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(opcode byte, ctxt *context) error {
func consts(opcode Opcode, ctxt *context) error {
num, _ := util.DecodeSLEB128(ctxt.buf)
ctxt.stack = append(ctxt.stack, num)
return nil
}
func framebase(opcode byte, ctxt *context) error {
func framebase(opcode Opcode, ctxt *context) error {
num, _ := util.DecodeSLEB128(ctxt.buf)
ctxt.stack = append(ctxt.stack, ctxt.FrameBase+num)
return nil
}
func register(opcode Opcode, ctxt *context) error {
ctxt.reg = true
if opcode == DW_OP_regx {
n, _ := util.DecodeSLEB128(ctxt.buf)
ctxt.pieces = append(ctxt.pieces, Piece{IsRegister: true, RegNum: uint64(n)})
} else {
ctxt.pieces = append(ctxt.pieces, Piece{IsRegister: true, RegNum: uint64(opcode - DW_OP_reg0)})
}
return nil
}
func piece(opcode Opcode, ctxt *context) error {
sz, _ := util.DecodeULEB128(ctxt.buf)
if ctxt.reg {
ctxt.reg = false
ctxt.pieces[len(ctxt.pieces)-1].Size = int(sz)
return nil
}
if len(ctxt.stack) == 0 {
return errors.New("empty OP stack")
}
addr := ctxt.stack[len(ctxt.stack)-1]
ctxt.pieces = append(ctxt.pieces, Piece{Size: int(sz), Addr: addr})
ctxt.stack = ctxt.stack[:0]
return nil
}

@ -1,161 +0,0 @@
package op
const (
DW_OP_addr Opcode = 0x03 // implemented
DW_OP_deref Opcode = 0x06
DW_OP_const1u Opcode = 0x08
DW_OP_const1s Opcode = 0x09
DW_OP_const2u Opcode = 0x0a
DW_OP_const2s Opcode = 0x0b
DW_OP_const4u Opcode = 0x0c
DW_OP_const4s Opcode = 0x0d
DW_OP_const8u Opcode = 0x0e
DW_OP_const8s Opcode = 0x0f
DW_OP_constu Opcode = 0x10
DW_OP_consts Opcode = 0x11 // implemented
DW_OP_dup Opcode = 0x12
DW_OP_drop Opcode = 0x13
DW_OP_over Opcode = 0x14
DW_OP_pick Opcode = 0x15
DW_OP_swap Opcode = 0x16
DW_OP_rot Opcode = 0x17
DW_OP_xderef Opcode = 0x18
DW_OP_abs Opcode = 0x19
DW_OP_and Opcode = 0x1a
DW_OP_div Opcode = 0x1b
DW_OP_minus Opcode = 0x1c
DW_OP_mod Opcode = 0x1d
DW_OP_mul Opcode = 0x1e
DW_OP_neg Opcode = 0x1f
DW_OP_not Opcode = 0x20
DW_OP_or Opcode = 0x21
DW_OP_plus Opcode = 0x22 // implemented
DW_OP_plus_uconst Opcode = 0x23 // implemented
DW_OP_shl Opcode = 0x24
DW_OP_shr Opcode = 0x25
DW_OP_shra Opcode = 0x26
DW_OP_xor Opcode = 0x27
DW_OP_bra Opcode = 0x28
DW_OP_eq Opcode = 0x29
DW_OP_ge Opcode = 0x2a
DW_OP_gt Opcode = 0x2b
DW_OP_le Opcode = 0x2c
DW_OP_lt Opcode = 0x2d
DW_OP_ne Opcode = 0x2e
DW_OP_skip Opcode = 0x2f
DW_OP_lit0 Opcode = 0x30
DW_OP_lit1 Opcode = 0x31
DW_OP_lit2 Opcode = 0x32
DW_OP_lit3 Opcode = 0x33
DW_OP_lit4 Opcode = 0x34
DW_OP_lit5 Opcode = 0x35
DW_OP_lit6 Opcode = 0x36
DW_OP_lit7 Opcode = 0x37
DW_OP_lit8 Opcode = 0x38
DW_OP_lit9 Opcode = 0x39
DW_OP_lit10 Opcode = 0x3a
DW_OP_lit11 Opcode = 0x3b
DW_OP_lit12 Opcode = 0x3c
DW_OP_lit13 Opcode = 0x3d
DW_OP_lit14 Opcode = 0x3e
DW_OP_lit15 Opcode = 0x3f
DW_OP_lit16 Opcode = 0x40
DW_OP_lit17 Opcode = 0x41
DW_OP_lit18 Opcode = 0x42
DW_OP_lit19 Opcode = 0x43
DW_OP_lit20 Opcode = 0x44
DW_OP_lit21 Opcode = 0x45
DW_OP_lit22 Opcode = 0x46
DW_OP_lit23 Opcode = 0x47
DW_OP_lit24 Opcode = 0x48
DW_OP_lit25 Opcode = 0x49
DW_OP_lit26 Opcode = 0x4a
DW_OP_lit27 Opcode = 0x4b
DW_OP_lit28 Opcode = 0x4c
DW_OP_lit29 Opcode = 0x4d
DW_OP_lit30 Opcode = 0x4e
DW_OP_lit31 Opcode = 0x4f
DW_OP_reg0 Opcode = 0x50
DW_OP_reg1 Opcode = 0x51
DW_OP_reg2 Opcode = 0x52
DW_OP_reg3 Opcode = 0x53
DW_OP_reg4 Opcode = 0x54
DW_OP_reg5 Opcode = 0x55
DW_OP_reg6 Opcode = 0x56
DW_OP_reg7 Opcode = 0x57
DW_OP_reg8 Opcode = 0x58
DW_OP_reg9 Opcode = 0x59
DW_OP_reg10 Opcode = 0x5a
DW_OP_reg11 Opcode = 0x5b
DW_OP_reg12 Opcode = 0x5c
DW_OP_reg13 Opcode = 0x5d
DW_OP_reg14 Opcode = 0x5e
DW_OP_reg15 Opcode = 0x5f
DW_OP_reg16 Opcode = 0x60
DW_OP_reg17 Opcode = 0x61
DW_OP_reg18 Opcode = 0x62
DW_OP_reg19 Opcode = 0x63
DW_OP_reg20 Opcode = 0x64
DW_OP_reg21 Opcode = 0x65
DW_OP_reg22 Opcode = 0x66
DW_OP_reg23 Opcode = 0x67
DW_OP_reg24 Opcode = 0x68
DW_OP_reg25 Opcode = 0x69
DW_OP_reg26 Opcode = 0x6a
DW_OP_reg27 Opcode = 0x6b
DW_OP_reg28 Opcode = 0x6c
DW_OP_reg29 Opcode = 0x6d
DW_OP_reg30 Opcode = 0x6e
DW_OP_reg31 Opcode = 0x6f
DW_OP_breg0 Opcode = 0x70
DW_OP_breg1 Opcode = 0x71
DW_OP_breg2 Opcode = 0x72
DW_OP_breg3 Opcode = 0x73
DW_OP_breg4 Opcode = 0x74
DW_OP_breg5 Opcode = 0x75
DW_OP_breg6 Opcode = 0x76
DW_OP_breg7 Opcode = 0x77
DW_OP_breg8 Opcode = 0x78
DW_OP_breg9 Opcode = 0x79
DW_OP_breg10 Opcode = 0x7a
DW_OP_breg11 Opcode = 0x7b
DW_OP_breg12 Opcode = 0x7c
DW_OP_breg13 Opcode = 0x7d
DW_OP_breg14 Opcode = 0x7e
DW_OP_breg15 Opcode = 0x7f
DW_OP_breg16 Opcode = 0x80
DW_OP_breg17 Opcode = 0x81
DW_OP_breg18 Opcode = 0x82
DW_OP_breg19 Opcode = 0x83
DW_OP_breg20 Opcode = 0x84
DW_OP_breg21 Opcode = 0x85
DW_OP_breg22 Opcode = 0x86
DW_OP_breg23 Opcode = 0x87
DW_OP_breg24 Opcode = 0x88
DW_OP_breg25 Opcode = 0x89
DW_OP_breg26 Opcode = 0x8a
DW_OP_breg27 Opcode = 0x8b
DW_OP_breg28 Opcode = 0x8c
DW_OP_breg29 Opcode = 0x8d
DW_OP_breg30 Opcode = 0x8e
DW_OP_breg31 Opcode = 0x8f
DW_OP_regx Opcode = 0x90
DW_OP_fbreg Opcode = 0x91
DW_OP_bregx Opcode = 0x92
DW_OP_piece Opcode = 0x93
DW_OP_deref_size Opcode = 0x94
DW_OP_xderef_size Opcode = 0x95
DW_OP_nop Opcode = 0x96
// DWARF 3 extensions.
DW_OP_push_object_address Opcode = 0x97
DW_OP_call2 Opcode = 0x98
DW_OP_call4 Opcode = 0x99
DW_OP_call_ref Opcode = 0x9a
DW_OP_form_tls_address Opcode = 0x9b
DW_OP_call_frame_cfa Opcode = 0x9c // implemented
DW_OP_bit_piece Opcode = 0x9d
// DWARF 4 extensions.
DW_OP_implicit_value Opcode = 0x9e
DW_OP_stack_value Opcode = 0x9f
)

@ -4,10 +4,10 @@ import "testing"
func TestExecuteStackProgram(t *testing.T) {
var (
instructions = []byte{DW_OP_consts, 0x1c, DW_OP_consts, 0x1c, DW_OP_plus}
instructions = []byte{byte(DW_OP_consts), 0x1c, byte(DW_OP_consts), 0x1c, byte(DW_OP_plus)}
expected = int64(56)
)
actual, err := ExecuteStackProgram(DwarfRegisters{}, instructions)
actual, _, err := ExecuteStackProgram(DwarfRegisters{}, instructions)
if err != nil {
t.Fatal(err)
}

515
pkg/dwarf/op/opcodes.go Normal file

@ -0,0 +1,515 @@
// THIS FILE IS AUTOGENERATED, EDIT opcodes.table INSTEAD
package op
const (
DW_OP_addr Opcode = 0x03
DW_OP_deref Opcode = 0x06
DW_OP_const1u Opcode = 0x08
DW_OP_const1s Opcode = 0x09
DW_OP_const2u Opcode = 0x0a
DW_OP_const2s Opcode = 0x0b
DW_OP_const4u Opcode = 0x0c
DW_OP_const4s Opcode = 0x0d
DW_OP_const8u Opcode = 0x0e
DW_OP_const8s Opcode = 0x0f
DW_OP_constu Opcode = 0x10
DW_OP_consts Opcode = 0x11
DW_OP_dup Opcode = 0x12
DW_OP_drop Opcode = 0x13
DW_OP_over Opcode = 0x14
DW_OP_pick Opcode = 0x15
DW_OP_swap Opcode = 0x16
DW_OP_rot Opcode = 0x17
DW_OP_xderef Opcode = 0x18
DW_OP_abs Opcode = 0x19
DW_OP_and Opcode = 0x1a
DW_OP_div Opcode = 0x1b
DW_OP_minus Opcode = 0x1c
DW_OP_mod Opcode = 0x1d
DW_OP_mul Opcode = 0x1e
DW_OP_neg Opcode = 0x1f
DW_OP_not Opcode = 0x20
DW_OP_or Opcode = 0x21
DW_OP_plus Opcode = 0x22
DW_OP_plus_uconst Opcode = 0x23
DW_OP_shl Opcode = 0x24
DW_OP_shr Opcode = 0x25
DW_OP_shra Opcode = 0x26
DW_OP_xor Opcode = 0x27
DW_OP_bra Opcode = 0x28
DW_OP_eq Opcode = 0x29
DW_OP_ge Opcode = 0x2a
DW_OP_gt Opcode = 0x2b
DW_OP_le Opcode = 0x2c
DW_OP_lt Opcode = 0x2d
DW_OP_ne Opcode = 0x2e
DW_OP_skip Opcode = 0x2f
DW_OP_lit0 Opcode = 0x30
DW_OP_lit1 Opcode = 0x31
DW_OP_lit2 Opcode = 0x32
DW_OP_lit3 Opcode = 0x33
DW_OP_lit4 Opcode = 0x34
DW_OP_lit5 Opcode = 0x35
DW_OP_lit6 Opcode = 0x36
DW_OP_lit7 Opcode = 0x37
DW_OP_lit8 Opcode = 0x38
DW_OP_lit9 Opcode = 0x39
DW_OP_lit10 Opcode = 0x3a
DW_OP_lit11 Opcode = 0x3b
DW_OP_lit12 Opcode = 0x3c
DW_OP_lit13 Opcode = 0x3d
DW_OP_lit14 Opcode = 0x3e
DW_OP_lit15 Opcode = 0x3f
DW_OP_lit16 Opcode = 0x40
DW_OP_lit17 Opcode = 0x41
DW_OP_lit18 Opcode = 0x42
DW_OP_lit19 Opcode = 0x43
DW_OP_lit20 Opcode = 0x44
DW_OP_lit21 Opcode = 0x45
DW_OP_lit22 Opcode = 0x46
DW_OP_lit23 Opcode = 0x47
DW_OP_lit24 Opcode = 0x48
DW_OP_lit25 Opcode = 0x49
DW_OP_lit26 Opcode = 0x4a
DW_OP_lit27 Opcode = 0x4b
DW_OP_lit28 Opcode = 0x4c
DW_OP_lit29 Opcode = 0x4d
DW_OP_lit30 Opcode = 0x4e
DW_OP_lit31 Opcode = 0x4f
DW_OP_reg0 Opcode = 0x50
DW_OP_reg1 Opcode = 0x51
DW_OP_reg2 Opcode = 0x52
DW_OP_reg3 Opcode = 0x53
DW_OP_reg4 Opcode = 0x54
DW_OP_reg5 Opcode = 0x55
DW_OP_reg6 Opcode = 0x56
DW_OP_reg7 Opcode = 0x57
DW_OP_reg8 Opcode = 0x58
DW_OP_reg9 Opcode = 0x59
DW_OP_reg10 Opcode = 0x5a
DW_OP_reg11 Opcode = 0x5b
DW_OP_reg12 Opcode = 0x5c
DW_OP_reg13 Opcode = 0x5d
DW_OP_reg14 Opcode = 0x5e
DW_OP_reg15 Opcode = 0x5f
DW_OP_reg16 Opcode = 0x60
DW_OP_reg17 Opcode = 0x61
DW_OP_reg18 Opcode = 0x62
DW_OP_reg19 Opcode = 0x63
DW_OP_reg20 Opcode = 0x64
DW_OP_reg21 Opcode = 0x65
DW_OP_reg22 Opcode = 0x66
DW_OP_reg23 Opcode = 0x67
DW_OP_reg24 Opcode = 0x68
DW_OP_reg25 Opcode = 0x69
DW_OP_reg26 Opcode = 0x6a
DW_OP_reg27 Opcode = 0x6b
DW_OP_reg28 Opcode = 0x6c
DW_OP_reg29 Opcode = 0x6d
DW_OP_reg30 Opcode = 0x6e
DW_OP_reg31 Opcode = 0x6f
DW_OP_breg0 Opcode = 0x70
DW_OP_breg1 Opcode = 0x71
DW_OP_breg2 Opcode = 0x72
DW_OP_breg3 Opcode = 0x73
DW_OP_breg4 Opcode = 0x74
DW_OP_breg5 Opcode = 0x75
DW_OP_breg6 Opcode = 0x76
DW_OP_breg7 Opcode = 0x77
DW_OP_breg8 Opcode = 0x78
DW_OP_breg9 Opcode = 0x79
DW_OP_breg10 Opcode = 0x7a
DW_OP_breg11 Opcode = 0x7b
DW_OP_breg12 Opcode = 0x7c
DW_OP_breg13 Opcode = 0x7d
DW_OP_breg14 Opcode = 0x7e
DW_OP_breg15 Opcode = 0x7f
DW_OP_breg16 Opcode = 0x80
DW_OP_breg17 Opcode = 0x81
DW_OP_breg18 Opcode = 0x82
DW_OP_breg19 Opcode = 0x83
DW_OP_breg20 Opcode = 0x84
DW_OP_breg21 Opcode = 0x85
DW_OP_breg22 Opcode = 0x86
DW_OP_breg23 Opcode = 0x87
DW_OP_breg24 Opcode = 0x88
DW_OP_breg25 Opcode = 0x89
DW_OP_breg26 Opcode = 0x8a
DW_OP_breg27 Opcode = 0x8b
DW_OP_breg28 Opcode = 0x8c
DW_OP_breg29 Opcode = 0x8d
DW_OP_breg30 Opcode = 0x8e
DW_OP_breg31 Opcode = 0x8f
DW_OP_regx Opcode = 0x90
DW_OP_fbreg Opcode = 0x91
DW_OP_bregx Opcode = 0x92
DW_OP_piece Opcode = 0x93
DW_OP_deref_size Opcode = 0x94
DW_OP_xderef_size Opcode = 0x95
DW_OP_nop Opcode = 0x96
DW_OP_push_object_address Opcode = 0x97
DW_OP_call2 Opcode = 0x98
DW_OP_call4 Opcode = 0x99
DW_OP_call_ref Opcode = 0x9a
DW_OP_form_tls_address Opcode = 0x9b
DW_OP_call_frame_cfa Opcode = 0x9c
DW_OP_bit_piece Opcode = 0x9d
DW_OP_implicit_value Opcode = 0x9e
DW_OP_stack_value Opcode = 0x9f
)
var opcodeName = map[Opcode]string{
DW_OP_addr: "DW_OP_addr",
DW_OP_deref: "DW_OP_deref",
DW_OP_const1u: "DW_OP_const1u",
DW_OP_const1s: "DW_OP_const1s",
DW_OP_const2u: "DW_OP_const2u",
DW_OP_const2s: "DW_OP_const2s",
DW_OP_const4u: "DW_OP_const4u",
DW_OP_const4s: "DW_OP_const4s",
DW_OP_const8u: "DW_OP_const8u",
DW_OP_const8s: "DW_OP_const8s",
DW_OP_constu: "DW_OP_constu",
DW_OP_consts: "DW_OP_consts",
DW_OP_dup: "DW_OP_dup",
DW_OP_drop: "DW_OP_drop",
DW_OP_over: "DW_OP_over",
DW_OP_pick: "DW_OP_pick",
DW_OP_swap: "DW_OP_swap",
DW_OP_rot: "DW_OP_rot",
DW_OP_xderef: "DW_OP_xderef",
DW_OP_abs: "DW_OP_abs",
DW_OP_and: "DW_OP_and",
DW_OP_div: "DW_OP_div",
DW_OP_minus: "DW_OP_minus",
DW_OP_mod: "DW_OP_mod",
DW_OP_mul: "DW_OP_mul",
DW_OP_neg: "DW_OP_neg",
DW_OP_not: "DW_OP_not",
DW_OP_or: "DW_OP_or",
DW_OP_plus: "DW_OP_plus",
DW_OP_plus_uconst: "DW_OP_plus_uconst",
DW_OP_shl: "DW_OP_shl",
DW_OP_shr: "DW_OP_shr",
DW_OP_shra: "DW_OP_shra",
DW_OP_xor: "DW_OP_xor",
DW_OP_bra: "DW_OP_bra",
DW_OP_eq: "DW_OP_eq",
DW_OP_ge: "DW_OP_ge",
DW_OP_gt: "DW_OP_gt",
DW_OP_le: "DW_OP_le",
DW_OP_lt: "DW_OP_lt",
DW_OP_ne: "DW_OP_ne",
DW_OP_skip: "DW_OP_skip",
DW_OP_lit0: "DW_OP_lit0",
DW_OP_lit1: "DW_OP_lit1",
DW_OP_lit2: "DW_OP_lit2",
DW_OP_lit3: "DW_OP_lit3",
DW_OP_lit4: "DW_OP_lit4",
DW_OP_lit5: "DW_OP_lit5",
DW_OP_lit6: "DW_OP_lit6",
DW_OP_lit7: "DW_OP_lit7",
DW_OP_lit8: "DW_OP_lit8",
DW_OP_lit9: "DW_OP_lit9",
DW_OP_lit10: "DW_OP_lit10",
DW_OP_lit11: "DW_OP_lit11",
DW_OP_lit12: "DW_OP_lit12",
DW_OP_lit13: "DW_OP_lit13",
DW_OP_lit14: "DW_OP_lit14",
DW_OP_lit15: "DW_OP_lit15",
DW_OP_lit16: "DW_OP_lit16",
DW_OP_lit17: "DW_OP_lit17",
DW_OP_lit18: "DW_OP_lit18",
DW_OP_lit19: "DW_OP_lit19",
DW_OP_lit20: "DW_OP_lit20",
DW_OP_lit21: "DW_OP_lit21",
DW_OP_lit22: "DW_OP_lit22",
DW_OP_lit23: "DW_OP_lit23",
DW_OP_lit24: "DW_OP_lit24",
DW_OP_lit25: "DW_OP_lit25",
DW_OP_lit26: "DW_OP_lit26",
DW_OP_lit27: "DW_OP_lit27",
DW_OP_lit28: "DW_OP_lit28",
DW_OP_lit29: "DW_OP_lit29",
DW_OP_lit30: "DW_OP_lit30",
DW_OP_lit31: "DW_OP_lit31",
DW_OP_reg0: "DW_OP_reg0",
DW_OP_reg1: "DW_OP_reg1",
DW_OP_reg2: "DW_OP_reg2",
DW_OP_reg3: "DW_OP_reg3",
DW_OP_reg4: "DW_OP_reg4",
DW_OP_reg5: "DW_OP_reg5",
DW_OP_reg6: "DW_OP_reg6",
DW_OP_reg7: "DW_OP_reg7",
DW_OP_reg8: "DW_OP_reg8",
DW_OP_reg9: "DW_OP_reg9",
DW_OP_reg10: "DW_OP_reg10",
DW_OP_reg11: "DW_OP_reg11",
DW_OP_reg12: "DW_OP_reg12",
DW_OP_reg13: "DW_OP_reg13",
DW_OP_reg14: "DW_OP_reg14",
DW_OP_reg15: "DW_OP_reg15",
DW_OP_reg16: "DW_OP_reg16",
DW_OP_reg17: "DW_OP_reg17",
DW_OP_reg18: "DW_OP_reg18",
DW_OP_reg19: "DW_OP_reg19",
DW_OP_reg20: "DW_OP_reg20",
DW_OP_reg21: "DW_OP_reg21",
DW_OP_reg22: "DW_OP_reg22",
DW_OP_reg23: "DW_OP_reg23",
DW_OP_reg24: "DW_OP_reg24",
DW_OP_reg25: "DW_OP_reg25",
DW_OP_reg26: "DW_OP_reg26",
DW_OP_reg27: "DW_OP_reg27",
DW_OP_reg28: "DW_OP_reg28",
DW_OP_reg29: "DW_OP_reg29",
DW_OP_reg30: "DW_OP_reg30",
DW_OP_reg31: "DW_OP_reg31",
DW_OP_breg0: "DW_OP_breg0",
DW_OP_breg1: "DW_OP_breg1",
DW_OP_breg2: "DW_OP_breg2",
DW_OP_breg3: "DW_OP_breg3",
DW_OP_breg4: "DW_OP_breg4",
DW_OP_breg5: "DW_OP_breg5",
DW_OP_breg6: "DW_OP_breg6",
DW_OP_breg7: "DW_OP_breg7",
DW_OP_breg8: "DW_OP_breg8",
DW_OP_breg9: "DW_OP_breg9",
DW_OP_breg10: "DW_OP_breg10",
DW_OP_breg11: "DW_OP_breg11",
DW_OP_breg12: "DW_OP_breg12",
DW_OP_breg13: "DW_OP_breg13",
DW_OP_breg14: "DW_OP_breg14",
DW_OP_breg15: "DW_OP_breg15",
DW_OP_breg16: "DW_OP_breg16",
DW_OP_breg17: "DW_OP_breg17",
DW_OP_breg18: "DW_OP_breg18",
DW_OP_breg19: "DW_OP_breg19",
DW_OP_breg20: "DW_OP_breg20",
DW_OP_breg21: "DW_OP_breg21",
DW_OP_breg22: "DW_OP_breg22",
DW_OP_breg23: "DW_OP_breg23",
DW_OP_breg24: "DW_OP_breg24",
DW_OP_breg25: "DW_OP_breg25",
DW_OP_breg26: "DW_OP_breg26",
DW_OP_breg27: "DW_OP_breg27",
DW_OP_breg28: "DW_OP_breg28",
DW_OP_breg29: "DW_OP_breg29",
DW_OP_breg30: "DW_OP_breg30",
DW_OP_breg31: "DW_OP_breg31",
DW_OP_regx: "DW_OP_regx",
DW_OP_fbreg: "DW_OP_fbreg",
DW_OP_bregx: "DW_OP_bregx",
DW_OP_piece: "DW_OP_piece",
DW_OP_deref_size: "DW_OP_deref_size",
DW_OP_xderef_size: "DW_OP_xderef_size",
DW_OP_nop: "DW_OP_nop",
DW_OP_push_object_address: "DW_OP_push_object_address",
DW_OP_call2: "DW_OP_call2",
DW_OP_call4: "DW_OP_call4",
DW_OP_call_ref: "DW_OP_call_ref",
DW_OP_form_tls_address: "DW_OP_form_tls_address",
DW_OP_call_frame_cfa: "DW_OP_call_frame_cfa",
DW_OP_bit_piece: "DW_OP_bit_piece",
DW_OP_implicit_value: "DW_OP_implicit_value",
DW_OP_stack_value: "DW_OP_stack_value",
}
var opcodeArgs = map[Opcode]string{
DW_OP_addr: "8",
DW_OP_deref: "",
DW_OP_const1u: "1",
DW_OP_const1s: "1",
DW_OP_const2u: "2",
DW_OP_const2s: "2",
DW_OP_const4u: "4",
DW_OP_const4s: "4",
DW_OP_const8u: "8",
DW_OP_const8s: "8",
DW_OP_constu: "u",
DW_OP_consts: "s",
DW_OP_dup: "",
DW_OP_drop: "",
DW_OP_over: "",
DW_OP_pick: "",
DW_OP_swap: "",
DW_OP_rot: "",
DW_OP_xderef: "",
DW_OP_abs: "",
DW_OP_and: "",
DW_OP_div: "",
DW_OP_minus: "",
DW_OP_mod: "",
DW_OP_mul: "",
DW_OP_neg: "",
DW_OP_not: "",
DW_OP_or: "",
DW_OP_plus: "",
DW_OP_plus_uconst: "u",
DW_OP_shl: "",
DW_OP_shr: "",
DW_OP_shra: "",
DW_OP_xor: "",
DW_OP_bra: "2",
DW_OP_eq: "",
DW_OP_ge: "",
DW_OP_gt: "",
DW_OP_le: "",
DW_OP_lt: "",
DW_OP_ne: "",
DW_OP_skip: "2",
DW_OP_lit0: "",
DW_OP_lit1: "",
DW_OP_lit2: "",
DW_OP_lit3: "",
DW_OP_lit4: "",
DW_OP_lit5: "",
DW_OP_lit6: "",
DW_OP_lit7: "",
DW_OP_lit8: "",
DW_OP_lit9: "",
DW_OP_lit10: "",
DW_OP_lit11: "",
DW_OP_lit12: "",
DW_OP_lit13: "",
DW_OP_lit14: "",
DW_OP_lit15: "",
DW_OP_lit16: "",
DW_OP_lit17: "",
DW_OP_lit18: "",
DW_OP_lit19: "",
DW_OP_lit20: "",
DW_OP_lit21: "",
DW_OP_lit22: "",
DW_OP_lit23: "",
DW_OP_lit24: "",
DW_OP_lit25: "",
DW_OP_lit26: "",
DW_OP_lit27: "",
DW_OP_lit28: "",
DW_OP_lit29: "",
DW_OP_lit30: "",
DW_OP_lit31: "",
DW_OP_reg0: "",
DW_OP_reg1: "",
DW_OP_reg2: "",
DW_OP_reg3: "",
DW_OP_reg4: "",
DW_OP_reg5: "",
DW_OP_reg6: "",
DW_OP_reg7: "",
DW_OP_reg8: "",
DW_OP_reg9: "",
DW_OP_reg10: "",
DW_OP_reg11: "",
DW_OP_reg12: "",
DW_OP_reg13: "",
DW_OP_reg14: "",
DW_OP_reg15: "",
DW_OP_reg16: "",
DW_OP_reg17: "",
DW_OP_reg18: "",
DW_OP_reg19: "",
DW_OP_reg20: "",
DW_OP_reg21: "",
DW_OP_reg22: "",
DW_OP_reg23: "",
DW_OP_reg24: "",
DW_OP_reg25: "",
DW_OP_reg26: "",
DW_OP_reg27: "",
DW_OP_reg28: "",
DW_OP_reg29: "",
DW_OP_reg30: "",
DW_OP_reg31: "",
DW_OP_breg0: "s",
DW_OP_breg1: "s",
DW_OP_breg2: "s",
DW_OP_breg3: "s",
DW_OP_breg4: "s",
DW_OP_breg5: "s",
DW_OP_breg6: "s",
DW_OP_breg7: "s",
DW_OP_breg8: "s",
DW_OP_breg9: "s",
DW_OP_breg10: "s",
DW_OP_breg11: "s",
DW_OP_breg12: "s",
DW_OP_breg13: "s",
DW_OP_breg14: "s",
DW_OP_breg15: "s",
DW_OP_breg16: "s",
DW_OP_breg17: "s",
DW_OP_breg18: "s",
DW_OP_breg19: "s",
DW_OP_breg20: "s",
DW_OP_breg21: "s",
DW_OP_breg22: "s",
DW_OP_breg23: "s",
DW_OP_breg24: "s",
DW_OP_breg25: "s",
DW_OP_breg26: "s",
DW_OP_breg27: "s",
DW_OP_breg28: "s",
DW_OP_breg29: "s",
DW_OP_breg30: "s",
DW_OP_breg31: "s",
DW_OP_regx: "s",
DW_OP_fbreg: "s",
DW_OP_bregx: "us",
DW_OP_piece: "u",
DW_OP_deref_size: "1",
DW_OP_xderef_size: "1",
DW_OP_nop: "",
DW_OP_push_object_address: "",
DW_OP_call2: "2",
DW_OP_call4: "4",
DW_OP_call_ref: "4",
DW_OP_form_tls_address: "",
DW_OP_call_frame_cfa: "",
DW_OP_bit_piece: "uu",
DW_OP_implicit_value: "B",
DW_OP_stack_value: "",
}
var oplut = map[Opcode]stackfn{
DW_OP_addr: addr,
DW_OP_consts: consts,
DW_OP_plus: plus,
DW_OP_plus_uconst: plusuconsts,
DW_OP_reg0: register,
DW_OP_reg1: register,
DW_OP_reg2: register,
DW_OP_reg3: register,
DW_OP_reg4: register,
DW_OP_reg5: register,
DW_OP_reg6: register,
DW_OP_reg7: register,
DW_OP_reg8: register,
DW_OP_reg9: register,
DW_OP_reg10: register,
DW_OP_reg11: register,
DW_OP_reg12: register,
DW_OP_reg13: register,
DW_OP_reg14: register,
DW_OP_reg15: register,
DW_OP_reg16: register,
DW_OP_reg17: register,
DW_OP_reg18: register,
DW_OP_reg19: register,
DW_OP_reg20: register,
DW_OP_reg21: register,
DW_OP_reg22: register,
DW_OP_reg23: register,
DW_OP_reg24: register,
DW_OP_reg25: register,
DW_OP_reg26: register,
DW_OP_reg27: register,
DW_OP_reg28: register,
DW_OP_reg29: register,
DW_OP_reg30: register,
DW_OP_reg31: register,
DW_OP_regx: register,
DW_OP_fbreg: framebase,
DW_OP_piece: piece,
DW_OP_call_frame_cfa: callframecfa,
}

175
pkg/dwarf/op/opcodes.table Normal file

@ -0,0 +1,175 @@
// This file is used by scripts/gen-opcodes.go to generate
// pkg/dwarf/op/opcodes.go
// Lines starting with // are comments and will be discarded.
// Non empty lines contain the following tab separated fields:
//
// <opcode name> <opcode code> <arguments> <function name>
//
// With the last column, <function name>, being optional.
//
// The arguments field should contain a string with one character for each
// argument of the opcode:
//
// s signed variable length integer
// u unsigned variable length integer
// 1 one byte unsigned integer
// 2 two bytes unsigned integer
// 4 four bytes unsigned integer
// 8 eight bytes unsigned integer
// B an unsigned variable length integer 'n' followed by n a block of n bytes
DW_OP_addr 0x03 "8" addr
DW_OP_deref 0x06 ""
DW_OP_const1u 0x08 "1"
DW_OP_const1s 0x09 "1"
DW_OP_const2u 0x0a "2"
DW_OP_const2s 0x0b "2"
DW_OP_const4u 0x0c "4"
DW_OP_const4s 0x0d "4"
DW_OP_const8u 0x0e "8"
DW_OP_const8s 0x0f "8"
DW_OP_constu 0x10 "u"
DW_OP_consts 0x11 "s" consts
DW_OP_dup 0x12 ""
DW_OP_drop 0x13 ""
DW_OP_over 0x14 ""
DW_OP_pick 0x15 ""
DW_OP_swap 0x16 ""
DW_OP_rot 0x17 ""
DW_OP_xderef 0x18 ""
DW_OP_abs 0x19 ""
DW_OP_and 0x1a ""
DW_OP_div 0x1b ""
DW_OP_minus 0x1c ""
DW_OP_mod 0x1d ""
DW_OP_mul 0x1e ""
DW_OP_neg 0x1f ""
DW_OP_not 0x20 ""
DW_OP_or 0x21 ""
DW_OP_plus 0x22 "" plus
DW_OP_plus_uconst 0x23 "u" plusuconsts
DW_OP_shl 0x24 ""
DW_OP_shr 0x25 ""
DW_OP_shra 0x26 ""
DW_OP_xor 0x27 ""
DW_OP_bra 0x28 "2"
DW_OP_eq 0x29 ""
DW_OP_ge 0x2a ""
DW_OP_gt 0x2b ""
DW_OP_le 0x2c ""
DW_OP_lt 0x2d ""
DW_OP_ne 0x2e ""
DW_OP_skip 0x2f "2"
DW_OP_lit0 0x30 ""
DW_OP_lit1 0x31 ""
DW_OP_lit2 0x32 ""
DW_OP_lit3 0x33 ""
DW_OP_lit4 0x34 ""
DW_OP_lit5 0x35 ""
DW_OP_lit6 0x36 ""
DW_OP_lit7 0x37 ""
DW_OP_lit8 0x38 ""
DW_OP_lit9 0x39 ""
DW_OP_lit10 0x3a ""
DW_OP_lit11 0x3b ""
DW_OP_lit12 0x3c ""
DW_OP_lit13 0x3d ""
DW_OP_lit14 0x3e ""
DW_OP_lit15 0x3f ""
DW_OP_lit16 0x40 ""
DW_OP_lit17 0x41 ""
DW_OP_lit18 0x42 ""
DW_OP_lit19 0x43 ""
DW_OP_lit20 0x44 ""
DW_OP_lit21 0x45 ""
DW_OP_lit22 0x46 ""
DW_OP_lit23 0x47 ""
DW_OP_lit24 0x48 ""
DW_OP_lit25 0x49 ""
DW_OP_lit26 0x4a ""
DW_OP_lit27 0x4b ""
DW_OP_lit28 0x4c ""
DW_OP_lit29 0x4d ""
DW_OP_lit30 0x4e ""
DW_OP_lit31 0x4f ""
DW_OP_reg0 0x50 "" register
DW_OP_reg1 0x51 "" register
DW_OP_reg2 0x52 "" register
DW_OP_reg3 0x53 "" register
DW_OP_reg4 0x54 "" register
DW_OP_reg5 0x55 "" register
DW_OP_reg6 0x56 "" register
DW_OP_reg7 0x57 "" register
DW_OP_reg8 0x58 "" register
DW_OP_reg9 0x59 "" register
DW_OP_reg10 0x5a "" register
DW_OP_reg11 0x5b "" register
DW_OP_reg12 0x5c "" register
DW_OP_reg13 0x5d "" register
DW_OP_reg14 0x5e "" register
DW_OP_reg15 0x5f "" register
DW_OP_reg16 0x60 "" register
DW_OP_reg17 0x61 "" register
DW_OP_reg18 0x62 "" register
DW_OP_reg19 0x63 "" register
DW_OP_reg20 0x64 "" register
DW_OP_reg21 0x65 "" register
DW_OP_reg22 0x66 "" register
DW_OP_reg23 0x67 "" register
DW_OP_reg24 0x68 "" register
DW_OP_reg25 0x69 "" register
DW_OP_reg26 0x6a "" register
DW_OP_reg27 0x6b "" register
DW_OP_reg28 0x6c "" register
DW_OP_reg29 0x6d "" register
DW_OP_reg30 0x6e "" register
DW_OP_reg31 0x6f "" register
DW_OP_breg0 0x70 "s"
DW_OP_breg1 0x71 "s"
DW_OP_breg2 0x72 "s"
DW_OP_breg3 0x73 "s"
DW_OP_breg4 0x74 "s"
DW_OP_breg5 0x75 "s"
DW_OP_breg6 0x76 "s"
DW_OP_breg7 0x77 "s"
DW_OP_breg8 0x78 "s"
DW_OP_breg9 0x79 "s"
DW_OP_breg10 0x7a "s"
DW_OP_breg11 0x7b "s"
DW_OP_breg12 0x7c "s"
DW_OP_breg13 0x7d "s"
DW_OP_breg14 0x7e "s"
DW_OP_breg15 0x7f "s"
DW_OP_breg16 0x80 "s"
DW_OP_breg17 0x81 "s"
DW_OP_breg18 0x82 "s"
DW_OP_breg19 0x83 "s"
DW_OP_breg20 0x84 "s"
DW_OP_breg21 0x85 "s"
DW_OP_breg22 0x86 "s"
DW_OP_breg23 0x87 "s"
DW_OP_breg24 0x88 "s"
DW_OP_breg25 0x89 "s"
DW_OP_breg26 0x8a "s"
DW_OP_breg27 0x8b "s"
DW_OP_breg28 0x8c "s"
DW_OP_breg29 0x8d "s"
DW_OP_breg30 0x8e "s"
DW_OP_breg31 0x8f "s"
DW_OP_regx 0x90 "s" register
DW_OP_fbreg 0x91 "s" framebase
DW_OP_bregx 0x92 "us"
DW_OP_piece 0x93 "u" piece
DW_OP_deref_size 0x94 "1"
DW_OP_xderef_size 0x95 "1"
DW_OP_nop 0x96 ""
DW_OP_push_object_address 0x97 ""
DW_OP_call2 0x98 "2"
DW_OP_call4 0x99 "4"
DW_OP_call_ref 0x9a "4"
DW_OP_form_tls_address 0x9b ""
DW_OP_call_frame_cfa 0x9c "" callframecfa
DW_OP_bit_piece 0x9d "uu"
DW_OP_implicit_value 0x9e "B"
DW_OP_stack_value 0x9f ""

@ -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(op.DwarfRegisters{}, 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(op.DwarfRegisters{}, append(initialInstructions, instructions...))
addr, _, err := op.ExecuteStackProgram(op.DwarfRegisters{}, append(initialInstructions, instructions...))
return uint64(addr), err
}
}

@ -5,6 +5,7 @@ import (
"debug/elf"
"debug/macho"
"debug/pe"
"encoding/binary"
"errors"
"fmt"
"io"
@ -17,6 +18,7 @@ import (
"github.com/derekparker/delve/pkg/dwarf/frame"
"github.com/derekparker/delve/pkg/dwarf/godwarf"
"github.com/derekparker/delve/pkg/dwarf/line"
"github.com/derekparker/delve/pkg/dwarf/op"
"github.com/derekparker/delve/pkg/dwarf/reader"
)
@ -32,6 +34,7 @@ type BinaryInfo struct {
Arch Arch
dwarf *dwarf.Data
frameEntries frame.FrameDescriptionEntries
loclist loclistReader
compileUnits []*compileUnit
types map[string]dwarf.Offset
packageVars map[string]dwarf.Offset
@ -116,6 +119,65 @@ func (fn *Function) BaseName() string {
return fn.Name
}
type loclistReader struct {
data []byte
cur int
ptrSz int
}
func (rdr *loclistReader) Seek(off int) {
rdr.cur = off
}
func (rdr *loclistReader) read(sz int) []byte {
r := rdr.data[rdr.cur : rdr.cur+sz]
rdr.cur += sz
return r
}
func (rdr *loclistReader) oneAddr() uint64 {
switch rdr.ptrSz {
case 4:
addr := binary.LittleEndian.Uint32(rdr.read(rdr.ptrSz))
if addr == ^uint32(0) {
return ^uint64(0)
}
return uint64(addr)
case 8:
addr := uint64(binary.LittleEndian.Uint64(rdr.read(rdr.ptrSz)))
return addr
default:
panic("bad address size")
}
}
func (rdr *loclistReader) Next(e *loclistEntry) bool {
e.lowpc = rdr.oneAddr()
e.highpc = rdr.oneAddr()
if e.lowpc == 0 && e.highpc == 0 {
return false
}
if e.BaseAddressSelection() {
e.instr = nil
return true
}
instrlen := binary.LittleEndian.Uint16(rdr.read(2))
e.instr = rdr.read(int(instrlen))
return true
}
type loclistEntry struct {
lowpc, highpc uint64
instr []byte
}
func (e *loclistEntry) BaseAddressSelection() bool {
return e.lowpc == ^uint64(0)
}
func NewBinaryInfo(goos, goarch string) BinaryInfo {
r := BinaryInfo{GOOS: goos, nameOfRuntimeType: make(map[uintptr]nameOfRuntimeTypeEntry), typeCache: make(map[dwarf.Offset]godwarf.Type)}
@ -228,8 +290,9 @@ type nilCloser struct{}
func (c *nilCloser) Close() error { return nil }
// New creates a new BinaryInfo object using the specified data. Use LoadBinary instead.
func (bi *BinaryInfo) LoadFromData(dwdata *dwarf.Data, debugFrameBytes []byte, debugLineBytes []byte) {
// LoadFromData creates a new BinaryInfo object using the specified data.
// This is used for debugging BinaryInfo, you should use LoadBinary instead.
func (bi *BinaryInfo) LoadFromData(dwdata *dwarf.Data, debugFrameBytes, debugLineBytes, debugLocBytes []byte) {
bi.closer = (*nilCloser)(nil)
bi.dwarf = dwdata
@ -237,10 +300,73 @@ func (bi *BinaryInfo) LoadFromData(dwdata *dwarf.Data, debugFrameBytes []byte, d
bi.frameEntries = frame.Parse(debugFrameBytes, frame.DwarfEndian(debugFrameBytes))
}
var wg sync.WaitGroup
wg.Add(1)
go bi.loadDebugInfoMaps(debugLineBytes, &wg)
wg.Wait()
bi.loclistInit(debugLocBytes)
bi.loadDebugInfoMaps(debugLineBytes, nil)
}
func (bi *BinaryInfo) loclistInit(data []byte) {
bi.loclist.data = data
bi.loclist.ptrSz = bi.Arch.PtrSize()
}
// Location returns the location described by attribute attr of entry.
// This will either be an int64 address or a slice of Pieces for locations
// that don't correspond to a single memory address (registers, composite
// locations).
func (bi *BinaryInfo) Location(entry *dwarf.Entry, attr dwarf.Attr, pc uint64, regs op.DwarfRegisters) (int64, []op.Piece, error) {
a := entry.Val(attr)
if a == nil {
return 0, nil, fmt.Errorf("no location attribute %s", attr)
}
if instr, ok := a.([]byte); ok {
return op.ExecuteStackProgram(regs, instr)
}
off, ok := a.(int64)
if !ok {
return 0, nil, fmt.Errorf("could not interpret location attribute %s", attr)
}
if bi.loclist.data == nil {
return 0, nil, fmt.Errorf("could not find loclist entry at %#x for address %#x (no debug_loc section found)", off, pc)
}
instr := bi.loclistEntry(off, pc)
if instr == nil {
return 0, nil, fmt.Errorf("could not find loclist entry at %#x for address %#x", off, pc)
}
return op.ExecuteStackProgram(regs, instr)
}
// loclistEntry returns the loclist entry in the loclist starting at off,
// for address pc.
func (bi *BinaryInfo) loclistEntry(off int64, pc uint64) []byte {
var base uint64
if cu := bi.findCompileUnit(pc); cu != nil {
base = cu.LowPC
}
bi.loclist.Seek(int(off))
var e loclistEntry
for bi.loclist.Next(&e) {
if e.BaseAddressSelection() {
base = e.highpc
continue
}
if pc >= e.lowpc+base && pc < e.highpc+base {
return e.instr
}
}
return nil
}
// findCompileUnit returns the compile unit containing address pc.
func (bi *BinaryInfo) findCompileUnit(pc uint64) *compileUnit {
for _, cu := range bi.compileUnits {
if pc >= cu.LowPC && pc < cu.HighPC {
return cu
}
}
return nil
}
// ELF ///////////////////////////////////////////////////////////////
@ -267,6 +393,7 @@ func (bi *BinaryInfo) LoadBinaryInfoElf(path string, wg *sync.WaitGroup) error {
if err != nil {
return err
}
bi.loclistInit(getDebugLocElf(elfFile))
wg.Add(3)
go bi.parseDebugFrameElf(elfFile, wg)
@ -310,6 +437,14 @@ func getDebugLineInfoElf(exe *elf.File) ([]byte, error) {
return nil, errors.New("could not find .debug_line section in binary")
}
func getDebugLocElf(exe *elf.File) []byte {
if sec := exe.Section(".debug_loc"); sec != nil {
debugLoc, _ := exe.Section(".debug_loc").Data()
return debugLoc
}
return nil
}
func (bi *BinaryInfo) setGStructOffsetElf(exe *elf.File, wg *sync.WaitGroup) {
defer wg.Done()
@ -368,6 +503,7 @@ func (bi *BinaryInfo) LoadBinaryInfoPE(path string, wg *sync.WaitGroup) error {
if err != nil {
return err
}
bi.loclistInit(getDebugLocPE(peFile))
wg.Add(2)
go bi.parseDebugFramePE(peFile, wg)
@ -505,6 +641,14 @@ func getDebugLineInfoPE(exe *pe.File) ([]byte, error) {
return nil, errors.New("could not find .debug_line section in binary")
}
func getDebugLocPE(exe *pe.File) []byte {
if sec := exe.Section(".debug_loc"); sec != nil {
debugLoc, _ := sec.Data()
return debugLoc
}
return nil
}
// MACH-O ////////////////////////////////////////////////////////////
func (bi *BinaryInfo) LoadBinaryInfoMacho(path string, wg *sync.WaitGroup) error {
@ -525,6 +669,7 @@ func (bi *BinaryInfo) LoadBinaryInfoMacho(path string, wg *sync.WaitGroup) error
if err != nil {
return err
}
bi.loclistInit(getDebugLocMacho(exe))
wg.Add(2)
go bi.parseDebugFrameMacho(exe, wg)
@ -567,3 +712,11 @@ func getDebugLineInfoMacho(exe *macho.File) ([]byte, error) {
}
return nil, errors.New("could not find __debug_line section in binary")
}
func getDebugLocMacho(exe *macho.File) []byte {
if sec := exe.Section("__debug_loc"); sec != nil {
debugLoc, _ := sec.Data()
return debugLoc
}
return nil
}

@ -16,18 +16,19 @@ import (
"github.com/derekparker/delve/pkg/dwarf/godwarf"
"github.com/derekparker/delve/pkg/dwarf/op"
"github.com/derekparker/delve/pkg/proc"
"github.com/derekparker/delve/pkg/proc/core"
)
const defaultCFA = 0xc420051d00
func fakeBinaryInfo(t *testing.T, dwb *dwarfbuilder.Builder) *proc.BinaryInfo {
abbrev, aranges, frame, info, line, pubnames, ranges, str, err := dwb.Build()
abbrev, aranges, frame, info, line, pubnames, ranges, str, loc, err := dwb.Build()
assertNoError(err, t, "dwarbuilder.Build")
dwdata, err := dwarf.New(abbrev, aranges, frame, info, line, pubnames, ranges, str)
assertNoError(err, t, "creating dwarf")
bi := proc.NewBinaryInfo("linux", "amd64")
bi.LoadFromData(dwdata, frame, line)
bi.LoadFromData(dwdata, frame, line, loc)
return &bi
}
@ -66,22 +67,6 @@ func (mem *fakeMemory) WriteMemory(uintptr, []byte) (int, error) {
return 0, fmt.Errorf("not implemented")
}
// fakeRegisters implements op.DwarfRegisters with arbitrary values for each
// register.
type fakeRegisters struct {
regs [32]uint64
}
func (regs *fakeRegisters) Set(i int, val uint64) {
regs.regs[i] = val
}
func (regs *fakeRegisters) Get(i int) []byte {
var out bytes.Buffer
binary.Write(&out, binary.LittleEndian, regs.regs[i])
return out.Bytes()
}
func uintExprCheck(t *testing.T, scope *proc.EvalScope, expr string, tgt uint64) {
thevar, err := scope.EvalExpression(expr, normalLoadConfig)
assertNoError(err, t, fmt.Sprintf("EvalExpression(%s)", expr))
@ -95,7 +80,7 @@ func uintExprCheck(t *testing.T, scope *proc.EvalScope, expr string, tgt uint64)
}
func dwarfExprCheck(t *testing.T, mem proc.MemoryReadWriter, regs op.DwarfRegisters, bi *proc.BinaryInfo, testCases map[string]uint16) *proc.EvalScope {
scope := &proc.EvalScope{PC: 0x40100, CFA: defaultCFA, Mem: mem, Gvar: nil, BinInfo: bi}
scope := &proc.EvalScope{PC: 0x40100, Regs: regs, Mem: mem, Gvar: nil, BinInfo: bi}
for name, value := range testCases {
uintExprCheck(t, scope, name, uint64(value))
}
@ -103,6 +88,14 @@ func dwarfExprCheck(t *testing.T, mem proc.MemoryReadWriter, regs op.DwarfRegist
return scope
}
func dwarfRegisters(regs *core.Registers) op.DwarfRegisters {
a := proc.AMD64Arch("linux")
dwarfRegs := a.RegistersToDwarfRegisters(regs)
dwarfRegs.CFA = defaultCFA
dwarfRegs.FrameBase = defaultCFA
return dwarfRegs
}
func TestDwarfExprRegisters(t *testing.T) {
testCases := map[string]uint16{
"a": 0x1234,
@ -112,23 +105,23 @@ func TestDwarfExprRegisters(t *testing.T) {
dwb := dwarfbuilder.New()
uint16off := dwb.BaseType("uint16", dwarfbuilder.DW_ATE_unsigned, 2)
uint16off := dwb.AddBaseType("uint16", dwarfbuilder.DW_ATE_unsigned, 2)
dwb.Subprogram("main.main", 0x40100, 0x41000)
dwb.AddSubprogram("main.main", 0x40100, 0x41000)
dwb.Attr(dwarf.AttrFrameBase, dwarfbuilder.LocationBlock(op.DW_OP_call_frame_cfa))
dwb.Variable("a", uint16off, dwarfbuilder.LocationBlock(op.DW_OP_reg0))
dwb.Variable("b", uint16off, dwarfbuilder.LocationBlock(op.DW_OP_fbreg, int(8)))
dwb.Variable("c", uint16off, dwarfbuilder.LocationBlock(op.DW_OP_regx, int(1)))
dwb.AddVariable("a", uint16off, dwarfbuilder.LocationBlock(op.DW_OP_reg0))
dwb.AddVariable("b", uint16off, dwarfbuilder.LocationBlock(op.DW_OP_fbreg, int(8)))
dwb.AddVariable("c", uint16off, dwarfbuilder.LocationBlock(op.DW_OP_regx, int(1)))
dwb.TagClose()
bi := fakeBinaryInfo(t, dwb)
mem := newFakeMemory(defaultCFA, uint64(0), uint64(testCases["b"]), uint16(testCases["pair.v"]))
var regs fakeRegisters
regs.Set(0, uint64(testCases["a"]))
regs.Set(1, uint64(testCases["c"]))
regs := core.Registers{LinuxCoreRegisters: &core.LinuxCoreRegisters{}}
regs.Rax = uint64(testCases["a"])
regs.Rdx = uint64(testCases["c"])
dwarfExprCheck(t, mem, &regs, bi, testCases)
dwarfExprCheck(t, mem, dwarfRegisters(&regs), bi, testCases)
}
func TestDwarfExprComposite(t *testing.T) {
@ -141,46 +134,47 @@ func TestDwarfExprComposite(t *testing.T) {
dwb := dwarfbuilder.New()
uint16off := dwb.BaseType("uint16", dwarfbuilder.DW_ATE_unsigned, 2)
intoff := dwb.BaseType("int", dwarfbuilder.DW_ATE_signed, 8)
uint16off := dwb.AddBaseType("uint16", dwarfbuilder.DW_ATE_unsigned, 2)
intoff := dwb.AddBaseType("int", dwarfbuilder.DW_ATE_signed, 8)
byteoff := dwb.BaseType("uint8", dwarfbuilder.DW_ATE_unsigned, 1)
byteoff := dwb.AddBaseType("uint8", dwarfbuilder.DW_ATE_unsigned, 1)
byteptroff := dwb.TagOpen(dwarf.TagPointerType, "*uint8")
dwb.Attr(godwarf.AttrGoKind, uint8(22))
dwb.Attr(dwarf.AttrType, byteoff)
dwb.TagClose()
pairoff := dwb.StructType("main.pair", 4)
pairoff := dwb.AddStructType("main.pair", 4)
dwb.Attr(godwarf.AttrGoKind, uint8(25))
dwb.Member("k", uint16off, dwarfbuilder.LocationBlock(op.DW_OP_plus_uconst, uint(0)))
dwb.Member("v", uint16off, dwarfbuilder.LocationBlock(op.DW_OP_plus_uconst, uint(2)))
dwb.AddMember("k", uint16off, dwarfbuilder.LocationBlock(op.DW_OP_plus_uconst, uint(0)))
dwb.AddMember("v", uint16off, dwarfbuilder.LocationBlock(op.DW_OP_plus_uconst, uint(2)))
dwb.TagClose()
stringoff := dwb.StructType("string", 16)
stringoff := dwb.AddStructType("string", 16)
dwb.Attr(godwarf.AttrGoKind, uint8(24))
dwb.Member("str", byteptroff, dwarfbuilder.LocationBlock(op.DW_OP_plus_uconst, uint(0)))
dwb.Member("len", intoff, dwarfbuilder.LocationBlock(op.DW_OP_plus_uconst, uint(8)))
dwb.AddMember("str", byteptroff, dwarfbuilder.LocationBlock(op.DW_OP_plus_uconst, uint(0)))
dwb.AddMember("len", intoff, dwarfbuilder.LocationBlock(op.DW_OP_plus_uconst, uint(8)))
dwb.TagClose()
dwb.Subprogram("main.main", 0x40100, 0x41000)
dwb.Variable("pair", pairoff, dwarfbuilder.LocationBlock(
dwb.AddSubprogram("main.main", 0x40100, 0x41000)
dwb.AddVariable("pair", pairoff, dwarfbuilder.LocationBlock(
op.DW_OP_reg2, op.DW_OP_piece, uint(2),
op.DW_OP_call_frame_cfa, op.DW_OP_consts, int(16), op.DW_OP_plus, op.DW_OP_piece, uint(2)))
dwb.Variable("s", stringoff, dwarfbuilder.LocationBlock(
dwb.AddVariable("s", stringoff, dwarfbuilder.LocationBlock(
op.DW_OP_reg1, op.DW_OP_piece, uint(8),
op.DW_OP_reg0, op.DW_OP_piece, uint(8)))
dwb.TagClose()
bi := fakeBinaryInfo(t, dwb)
mem := newFakeMemory(defaultCFA, uint64(0), uint64(0), uint16(testCases["pair.v"]), stringVal)
var regs fakeRegisters
regs.Set(0, uint64(len(stringVal)))
regs.Set(1, defaultCFA+18)
regs.Set(2, uint64(testCases["pair.k"]))
mem := newFakeMemory(defaultCFA, uint64(0), uint64(0), uint16(testCases["pair.v"]), []byte(stringVal))
var regs core.Registers
regs.LinuxCoreRegisters = &core.LinuxCoreRegisters{}
regs.Rax = uint64(len(stringVal))
regs.Rdx = defaultCFA + 18
regs.Rcx = uint64(testCases["pair.k"])
scope := dwarfExprCheck(t, mem, &regs, bi, testCases)
scope := dwarfExprCheck(t, mem, dwarfRegisters(&regs), bi, testCases)
thevar, err := scope.EvalExpression("s", normalLoadConfig)
assertNoError(err, t, fmt.Sprintf("EvalExpression(s)", "s"))
@ -199,10 +193,10 @@ func TestDwarfExprLoclist(t *testing.T) {
dwb := dwarfbuilder.New()
uint16off := dwb.BaseType("uint16", dwarfbuilder.DW_ATE_unsigned, 2)
uint16off := dwb.AddBaseType("uint16", dwarfbuilder.DW_ATE_unsigned, 2)
dwb.Subprogram("main.main", 0x40100, 0x41000)
dwb.Variable("a", uint16off, []dwarfbuilder.LocEntry{
dwb.AddSubprogram("main.main", 0x40100, 0x41000)
dwb.AddVariable("a", uint16off, []dwarfbuilder.LocEntry{
{0x40100, 0x40700, dwarfbuilder.LocationBlock(op.DW_OP_call_frame_cfa)},
{0x40700, 0x41000, dwarfbuilder.LocationBlock(op.DW_OP_call_frame_cfa, op.DW_OP_consts, int(2), op.DW_OP_plus)},
})
@ -211,8 +205,9 @@ func TestDwarfExprLoclist(t *testing.T) {
bi := fakeBinaryInfo(t, dwb)
mem := newFakeMemory(defaultCFA, uint16(before), uint16(after))
regs := core.Registers{LinuxCoreRegisters: &core.LinuxCoreRegisters{}}
scope := &proc.EvalScope{PC: 0x40100, CFA: defaultCFA, Mem: mem, Gvar: nil, BinInfo: bi}
scope := &proc.EvalScope{PC: 0x40100, Regs: dwarfRegisters(&regs), Mem: mem, Gvar: nil, BinInfo: bi}
uintExprCheck(t, scope, "a", before)
scope.PC = 0x40800

@ -87,7 +87,7 @@ func (scope *EvalScope) evalToplevelTypeCast(t ast.Expr, cfg LoadConfig) (*Varia
return nil, converr
}
for i, ch := range []byte(constant.StringVal(argv.Value)) {
e := scope.newVariable("", argv.Addr+uintptr(i), targetType.(*godwarf.SliceType).ElemType)
e := scope.newVariable("", argv.Addr+uintptr(i), targetType.(*godwarf.SliceType).ElemType, argv.mem)
e.loaded = true
e.Value = constant.MakeInt64(int64(ch))
v.Children = append(v.Children, *e)
@ -101,7 +101,7 @@ func (scope *EvalScope) evalToplevelTypeCast(t ast.Expr, cfg LoadConfig) (*Varia
return nil, converr
}
for i, ch := range constant.StringVal(argv.Value) {
e := scope.newVariable("", argv.Addr+uintptr(i), targetType.(*godwarf.SliceType).ElemType)
e := scope.newVariable("", argv.Addr+uintptr(i), targetType.(*godwarf.SliceType).ElemType, argv.mem)
e.loaded = true
e.Value = constant.MakeInt64(int64(ch))
v.Children = append(v.Children, *e)
@ -183,7 +183,7 @@ func (scope *EvalScope) evalAST(t ast.Expr) (*Variable, error) {
}
return scope.Gvar.clone(), nil
} else if maybePkg.Name == "runtime" && node.Sel.Name == "frameoff" {
return newConstant(constant.MakeInt64(scope.CFA-int64(scope.StackHi)), scope.Mem), nil
return newConstant(constant.MakeInt64(scope.Regs.CFA-int64(scope.StackHi)), scope.Mem), nil
} else if v, err := scope.packageVarAddr(maybePkg.Name + "." + node.Sel.Name); err == nil {
return v, nil
}
@ -286,7 +286,7 @@ func (scope *EvalScope) evalTypeCast(node *ast.CallExpr) (*Variable, error) {
n, _ := constant.Int64Val(argv.Value)
v.Children = []Variable{*(scope.newVariable("", uintptr(n), ttyp.Type))}
v.Children = []Variable{*(scope.newVariable("", uintptr(n), ttyp.Type, scope.Mem))}
return v, nil
case *godwarf.UintType:
@ -512,7 +512,7 @@ func complexBuiltin(args []*Variable, nodeargs []ast.Expr) (*Variable, error) {
typ := &godwarf.ComplexType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: int64(sz / 8), Name: fmt.Sprintf("complex%d", sz)}, BitSize: sz, BitOffset: 0}}
r := realev.newVariable("", 0, typ)
r := realev.newVariable("", 0, typ, nil)
r.Value = constant.BinaryOp(realev.Value, token.ADD, constant.MakeImag(imagev.Value))
return r, nil
}
@ -786,7 +786,7 @@ func (scope *EvalScope) evalAddrOf(node *ast.UnaryExpr) (*Variable, error) {
xev.OnlyAddr = true
typename := "*" + xev.DwarfType.Common().Name
rv := scope.newVariable("", 0, &godwarf.PtrType{CommonType: godwarf.CommonType{ByteSize: int64(scope.BinInfo.Arch.PtrSize()), Name: typename}, Type: xev.DwarfType})
rv := scope.newVariable("", 0, &godwarf.PtrType{CommonType: godwarf.CommonType{ByteSize: int64(scope.BinInfo.Arch.PtrSize()), Name: typename}, Type: xev.DwarfType}, scope.Mem)
rv.Children = []Variable{*xev}
rv.loaded = true
@ -851,7 +851,7 @@ func (scope *EvalScope) evalUnary(node *ast.UnaryExpr) (*Variable, error) {
return nil, err
}
if xv.DwarfType != nil {
r := xv.newVariable("", 0, xv.DwarfType)
r := xv.newVariable("", 0, xv.DwarfType, scope.Mem)
r.Value = rc
return r, nil
}
@ -994,7 +994,7 @@ func (scope *EvalScope) evalBinary(node *ast.BinaryExpr) (*Variable, error) {
return newConstant(rc, xv.mem), nil
}
r := xv.newVariable("", 0, typ)
r := xv.newVariable("", 0, typ, scope.Mem)
r.Value = rc
if r.Kind == reflect.String {
r.Len = xv.Len + yv.Len
@ -1207,7 +1207,11 @@ func (v *Variable) sliceAccess(idx int) (*Variable, error) {
if idx < 0 || int64(idx) >= v.Len {
return nil, fmt.Errorf("index out of bounds")
}
return v.newVariable("", v.Base+uintptr(int64(idx)*v.stride), v.fieldType), nil
mem := v.mem
if v.Kind != reflect.Array {
mem = DereferenceMemory(mem)
}
return v.newVariable("", v.Base+uintptr(int64(idx)*v.stride), v.fieldType, mem), nil
}
func (v *Variable) mapAccess(idx *Variable) (*Variable, error) {
@ -1261,7 +1265,12 @@ func (v *Variable) reslice(low int64, high int64) (*Variable, error) {
typ = fakeSliceType(v.fieldType)
}
r := v.newVariable("", 0, typ)
mem := v.mem
if v.Kind != reflect.Array {
mem = DereferenceMemory(mem)
}
r := v.newVariable("", 0, typ, mem)
r.Cap = len
r.Len = len
r.Base = base

@ -1,5 +1,11 @@
package proc
import (
"errors"
"github.com/derekparker/delve/pkg/dwarf/op"
)
const cacheEnabled = true
// MemoryReader is like io.ReaderAt, but the offset is a uintptr so that it
@ -46,7 +52,8 @@ func cacheMemory(mem MemoryReadWriter, addr uintptr, size int) MemoryReadWriter
if size <= 0 {
return mem
}
if cacheMem, isCache := mem.(*memCache); isCache {
switch cacheMem := mem.(type) {
case *memCache:
if cacheMem.contains(addr, size) {
return mem
} else {
@ -57,6 +64,8 @@ func cacheMemory(mem MemoryReadWriter, addr uintptr, size int) MemoryReadWriter
}
return &memCache{addr, cache, mem}
}
case *compositeMemory:
return mem
}
cache := make([]byte, size)
_, err := mem.ReadMemory(cache, addr)
@ -65,3 +74,62 @@ func cacheMemory(mem MemoryReadWriter, addr uintptr, size int) MemoryReadWriter
}
return &memCache{addr, cache, mem}
}
// fakeAddress used by extractVarInfoFromEntry for variables that do not
// have a memory address, we can't use 0 because a lot of code (likely
// including client code) assumes that addr == 0 is nil
const fakeAddress = 0xbeef0000
type compositeMemory struct {
realmem MemoryReadWriter
regs op.DwarfRegisters
pieces []op.Piece
data []byte
}
func newCompositeMemory(mem MemoryReadWriter, regs op.DwarfRegisters, pieces []op.Piece) *compositeMemory {
cmem := &compositeMemory{realmem: mem, regs: regs, pieces: pieces, data: []byte{}}
for _, piece := range pieces {
if piece.IsRegister {
reg := regs.Bytes(piece.RegNum)
sz := piece.Size
if sz == 0 && len(pieces) == 1 {
sz = len(reg)
}
cmem.data = append(cmem.data, reg[:sz]...)
} else {
buf := make([]byte, piece.Size)
mem.ReadMemory(buf, uintptr(piece.Addr))
cmem.data = append(cmem.data, buf...)
}
}
return cmem
}
func (mem *compositeMemory) ReadMemory(data []byte, addr uintptr) (int, error) {
addr -= fakeAddress
if addr >= uintptr(len(mem.data)) || addr+uintptr(len(data)) > uintptr(len(mem.data)) {
return 0, errors.New("read out of bounds")
}
copy(data, mem.data[addr:addr+uintptr(len(data))])
return len(data), nil
}
func (mem *compositeMemory) WriteMemory(addr uintptr, data []byte) (int, error) {
//TODO(aarzilli): implement
return 0, errors.New("can't write composite memory")
}
// DereferenceMemory returns a MemoryReadWriter that can read and write the
// memory pointed to by pointers in this memory.
// Normally mem and mem.Dereference are the same object, they are different
// only if this MemoryReadWriter is used to access memory outside of the
// normal address space of the inferior process (such as data contained in
// registers, or composite memory).
func DereferenceMemory(mem MemoryReadWriter) MemoryReadWriter {
switch mem := mem.(type) {
case *compositeMemory:
return mem.realmem
}
return mem
}

@ -3,6 +3,8 @@ package proc
import (
"go/constant"
"unsafe"
"github.com/derekparker/delve/pkg/dwarf/op"
)
// delve counterpart to runtime.moduledata
@ -13,7 +15,7 @@ type moduleData struct {
func loadModuleData(bi *BinaryInfo, mem MemoryReadWriter) (err error) {
bi.loadModuleDataOnce.Do(func() {
scope := &EvalScope{0, 0, mem, nil, bi, 0}
scope := &EvalScope{0, op.DwarfRegisters{}, mem, nil, bi, 0}
var md *Variable
md, err = scope.packageVarAddr("runtime.firstmoduledata")
if err != nil {
@ -81,7 +83,7 @@ func resolveTypeOff(bi *BinaryInfo, typeAddr uintptr, off uintptr, mem MemoryRea
}
v.loadValue(LoadConfig{false, 1, 0, 0, -1})
addr, _ := constant.Int64Val(v.Value)
return v.newVariable(v.Name, uintptr(addr), rtyp), nil
return v.newVariable(v.Name, uintptr(addr), rtyp, mem), nil
}
if t, _ := md.typemapVar.mapAccess(newConstant(constant.MakeUint64(uint64(off)), mem)); t != nil {
@ -119,7 +121,7 @@ func resolveNameOff(bi *BinaryInfo, typeAddr uintptr, off uintptr, mem MemoryRea
}
func reflectOffsMapAccess(bi *BinaryInfo, off uintptr, mem MemoryReadWriter) (*Variable, error) {
scope := &EvalScope{0, 0, mem, nil, bi, 0}
scope := &EvalScope{0, op.DwarfRegisters{}, mem, nil, bi, 0}
reflectOffs, err := scope.packageVarAddr("runtime.reflectOffs")
if err != nil {
return nil, err

@ -481,12 +481,10 @@ 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].Regs.CFA
return &EvalScope{PC, CFA, thread, g.variable, dbp.BinInfo(), g.stackhi}, nil
return &EvalScope{locs[frame].Current.PC, locs[frame].Regs, thread, g.variable, dbp.BinInfo(), locs[frame].StackHi}, nil
}
// FrameToScope returns a new EvalScope for this frame
func FrameToScope(p Process, frame Stackframe) *EvalScope {
return &EvalScope{frame.Current.PC, frame.Regs.CFA, p.CurrentThread(), nil, p.BinInfo(), frame.StackHi}
return &EvalScope{frame.Current.PC, frame.Regs, p.CurrentThread(), nil, p.BinInfo(), frame.StackHi}
}

@ -0,0 +1,90 @@
package proc
import (
"bytes"
"encoding/binary"
"golang.org/x/arch/x86/x86asm"
)
var dwarfToAsm = map[int]x86asm.Reg{
0: x86asm.RAX,
1: x86asm.RDX,
2: x86asm.RCX,
3: x86asm.RBX,
4: x86asm.RSI,
5: x86asm.RDI,
6: x86asm.RBP,
7: x86asm.RSP,
8: x86asm.R8,
9: x86asm.R9,
10: x86asm.R10,
11: x86asm.R11,
12: x86asm.R12,
13: x86asm.R13,
14: x86asm.R14,
15: x86asm.R15,
16: x86asm.RIP,
}
var dwarfToName = 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",
}
// getDwarfRegister maps between DWARF register numbers and architecture
// registers.
// The mapping 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 GetDwarfRegister(regs Registers, i int) []byte {
if asmreg, ok := dwarfToAsm[i]; ok {
x, _ := regs.Get(int(asmreg))
var buf bytes.Buffer
binary.Write(&buf, binary.LittleEndian, x)
return buf.Bytes()
}
if regname, ok := dwarfToName[i]; ok {
regslice := regs.Slice()
for _, reg := range regslice {
if reg.Name == regname {
return reg.Bytes
}
}
}
return []byte{}
}

@ -1,6 +1,7 @@
package proc
import (
"debug/dwarf"
"errors"
"fmt"
@ -173,6 +174,19 @@ func (it *stackIterator) Err() error {
return it.err
}
// frameBase calculates the frame base pseudo-register for DWARF for fn and
// the current frame.
func (it *stackIterator) frameBase(fn *Function) int64 {
rdr := it.bi.dwarf.Reader()
rdr.Seek(fn.offset)
e, err := rdr.Next()
if err != nil {
return 0
}
fb, _, _ := it.bi.Location(e, dwarf.AttrFrameBase, it.pc, it.regs)
return fb
}
func (it *stackIterator) newStackframe(ret, retaddr uint64) Stackframe {
if retaddr == 0 {
it.err = NullAddrError{}
@ -182,6 +196,8 @@ func (it *stackIterator) newStackframe(ret, retaddr uint64) Stackframe {
if fn == nil {
f = "?"
l = -1
} else {
it.regs.FrameBase = it.frameBase(fn)
}
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 {
@ -281,13 +297,13 @@ func (it *stackIterator) executeFrameRegRule(regnum uint64, rule frame.DWRule, c
case frame.RuleRegister:
return it.regs.Reg(rule.Reg), nil
case frame.RuleExpression:
v, err := op.ExecuteStackProgram(it.regs, rule.Expression)
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)
v, _, err := op.ExecuteStackProgram(it.regs, rule.Expression)
if err != nil {
return nil, err
}

@ -379,7 +379,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].Regs.CFA, thread, nil, thread.BinInfo(), 0}, nil
return &EvalScope{locations[0].Current.PC, locations[0].Regs, thread, nil, thread.BinInfo(), 0}, nil
}
// GoroutineScope returns an EvalScope for the goroutine running on this thread.
@ -395,7 +395,7 @@ func GoroutineScope(thread Thread) (*EvalScope, error) {
if err != nil {
return nil, err
}
return &EvalScope{locations[0].Current.PC, locations[0].Regs.CFA, thread, g.variable, thread.BinInfo(), g.stackhi}, nil
return &EvalScope{locations[0].Current.PC, locations[0].Regs, thread, g.variable, thread.BinInfo(), g.stackhi}, nil
}
func onRuntimeBreakpoint(thread Thread) bool {

@ -170,7 +170,9 @@ func (v compileUnitsByLowpc) Less(i int, j int) bool { return v[i].LowPC < v[j].
func (v compileUnitsByLowpc) Swap(i int, j int) { v[i], v[j] = v[j], v[i] }
func (bi *BinaryInfo) loadDebugInfoMaps(debugLineBytes []byte, wg *sync.WaitGroup) {
defer wg.Done()
if wg != nil {
defer wg.Done()
}
bi.types = make(map[string]dwarf.Offset)
bi.packageVars = make(map[string]dwarf.Offset)
bi.Functions = []Function{}
@ -502,7 +504,7 @@ func nameOfFuncRuntimeType(_type *Variable, tflag int64, anonymous bool) (string
outCount = outCount & (1<<15 - 1)
}
cursortyp := _type.newVariable("", _type.Addr+uintptr(uadd), prtyp)
cursortyp := _type.newVariable("", _type.Addr+uintptr(uadd), prtyp, _type.mem)
var buf bytes.Buffer
if anonymous {
buf.WriteString("func(")
@ -710,7 +712,7 @@ func specificRuntimeType(_type *Variable, kind int64) (*Variable, error) {
return _type, nil
}
return _type.newVariable(_type.Name, _type.Addr, typ), nil
return _type.newVariable(_type.Name, _type.Addr, typ, _type.mem), nil
}
// See reflect.(*rtype).uncommon in $GOROOT/src/reflect/type.go
@ -724,7 +726,7 @@ func uncommon(_type *Variable, tflag int64) *Variable {
return nil
}
return _type.newVariable(_type.Name, _type.Addr+uintptr(_type.RealType.Size()), typ)
return _type.newVariable(_type.Name, _type.Addr+uintptr(_type.RealType.Size()), typ, _type.mem)
}
// typeForKind returns a *dwarf.StructType describing the specialization of

@ -150,8 +150,8 @@ type G struct {
// EvalScope is the scope for variable evaluation. Contains the thread,
// current location (PC), and canonical frame address.
type EvalScope struct {
PC uint64 // Current instruction of the evaluation frame
CFA int64 // Stack address of the evaluation frame
PC uint64 // Current instruction of the evaluation frame
Regs op.DwarfRegisters
Mem MemoryReadWriter // Target's memory
Gvar *Variable
BinInfo *BinaryInfo
@ -167,16 +167,16 @@ func (err *IsNilErr) Error() string {
return fmt.Sprintf("%s is nil", err.name)
}
func (scope *EvalScope) newVariable(name string, addr uintptr, dwarfType godwarf.Type) *Variable {
return newVariable(name, addr, dwarfType, scope.BinInfo, scope.Mem)
func (scope *EvalScope) newVariable(name string, addr uintptr, dwarfType godwarf.Type, mem MemoryReadWriter) *Variable {
return newVariable(name, addr, dwarfType, scope.BinInfo, mem)
}
func newVariableFromThread(t Thread, name string, addr uintptr, dwarfType godwarf.Type) *Variable {
return newVariable(name, addr, dwarfType, t.BinInfo(), t)
}
func (v *Variable) newVariable(name string, addr uintptr, dwarfType godwarf.Type) *Variable {
return newVariable(name, addr, dwarfType, v.bi, v.mem)
func (v *Variable) newVariable(name string, addr uintptr, dwarfType godwarf.Type, mem MemoryReadWriter) *Variable {
return newVariable(name, addr, dwarfType, v.bi, mem)
}
func newVariable(name string, addr uintptr, dwarfType godwarf.Type, bi *BinaryInfo, mem MemoryReadWriter) *Variable {
@ -333,7 +333,7 @@ func (v *Variable) toField(field *godwarf.StructField) (*Variable, error) {
name = fmt.Sprintf("%s.%s", v.Name, field.Name)
}
}
return v.newVariable(name, uintptr(int64(v.Addr)+field.ByteOffset), field.Type), nil
return v.newVariable(name, uintptr(int64(v.Addr)+field.ByteOffset), field.Type, v.mem), nil
}
// DwarfReader returns the DwarfReader containing the
@ -597,14 +597,6 @@ func (scope *EvalScope) SetVariable(name, value string) error {
return xv.setValue(yv)
}
func (scope *EvalScope) extractVariableFromEntry(entry *dwarf.Entry) (*Variable, error) {
v, err := scope.extractVarInfoFromEntry(entry)
if err != nil {
return nil, err
}
return v, nil
}
// LocalVariables returns all local variables from the current function scope.
func (scope *EvalScope) LocalVariables(cfg LoadConfig) ([]*Variable, error) {
return scope.variablesByTag(dwarf.TagVariable, &cfg)
@ -636,7 +628,7 @@ func (scope *EvalScope) PackageVariables(cfg LoadConfig) ([]*Variable, error) {
}
// Ignore errors trying to extract values
val, err := scope.extractVariableFromEntry(entry)
val, err := scope.extractVarInfoFromEntry(entry)
if err != nil {
continue
}
@ -757,14 +749,14 @@ func (scope *EvalScope) extractVarInfoFromEntry(entry *dwarf.Entry) (*Variable,
return nil, err
}
instructions, ok := entry.Val(dwarf.AttrLocation).([]byte)
if !ok {
return nil, fmt.Errorf("type assertion failed")
addr, pieces, err := scope.BinInfo.Location(entry, dwarf.AttrLocation, scope.PC, scope.Regs)
mem := scope.Mem
if pieces != nil {
addr = fakeAddress
mem = newCompositeMemory(scope.Mem, scope.Regs, pieces)
}
addr, err := op.ExecuteStackProgram(op.DwarfRegisters{CFA: scope.CFA}, instructions)
v := scope.newVariable(n, uintptr(addr), t)
v := scope.newVariable(n, uintptr(addr), t, mem)
if err != nil {
v.Unreadable = err
}
@ -780,7 +772,7 @@ func (v *Variable) maybeDereference() *Variable {
switch t := v.RealType.(type) {
case *godwarf.PtrType:
ptrval, err := readUintRaw(v.mem, uintptr(v.Addr), t.ByteSize)
r := v.newVariable("", uintptr(ptrval), t.Type)
r := v.newVariable("", uintptr(ptrval), t.Type, DereferenceMemory(v.mem))
if err != nil {
r.Unreadable = err
}
@ -837,7 +829,7 @@ func (v *Variable) loadValueInternal(recurseLevel int, cfg LoadConfig) {
case reflect.String:
var val string
val, v.Unreadable = readStringValue(v.mem, v.Base, v.Len, cfg)
val, v.Unreadable = readStringValue(DereferenceMemory(v.mem), v.Base, v.Len, cfg)
v.Value = constant.MakeString(val)
case reflect.Slice, reflect.Array:
@ -1105,8 +1097,13 @@ func (v *Variable) loadArrayValues(recurseLevel int, cfg LoadConfig) {
errcount := 0
mem := v.mem
if v.Kind != reflect.Array {
mem = DereferenceMemory(mem)
}
for i := int64(0); i < count; i++ {
fieldvar := v.newVariable("", uintptr(int64(v.Base)+(i*v.stride)), v.fieldType)
fieldvar := v.newVariable("", uintptr(int64(v.Base)+(i*v.stride)), v.fieldType, mem)
fieldvar.loadValueInternal(recurseLevel+1, cfg)
if fieldvar.Unreadable != nil {
@ -1134,8 +1131,8 @@ func (v *Variable) readComplex(size int64) {
ftyp := &godwarf.FloatType{BasicType: godwarf.BasicType{CommonType: godwarf.CommonType{ByteSize: fs, Name: fmt.Sprintf("float%d", fs)}, BitSize: fs * 8, BitOffset: 0}}
realvar := v.newVariable("real", v.Addr, ftyp)
imagvar := v.newVariable("imaginary", v.Addr+uintptr(fs), ftyp)
realvar := v.newVariable("real", v.Addr, ftyp, v.mem)
imagvar := v.newVariable("imaginary", v.Addr+uintptr(fs), ftyp, v.mem)
realvar.loadValue(loadSingleValue)
imagvar.loadValue(loadSingleValue)
v.Value = constant.BinaryOp(realvar.Value, token.ADD, constant.MakeImag(imagvar.Value))
@ -1316,7 +1313,7 @@ func (v *Variable) loadMap(recurseLevel int, cfg LoadConfig) {
if it.values.fieldType.Size() > 0 {
val = it.value()
} else {
val = v.newVariable("", it.values.Addr, it.values.fieldType)
val = v.newVariable("", it.values.Addr, it.values.fieldType, DereferenceMemory(v.mem))
}
key.loadValueInternal(recurseLevel+1, cfg)
val.loadValueInternal(recurseLevel+1, cfg)
@ -1710,7 +1707,7 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool, cfg LoadConfig
}
}
data = data.newVariable("data", data.Addr, typ)
data = data.newVariable("data", data.Addr, typ, data.mem)
if deref {
data = data.maybeDereference()
data.Name = "data"
@ -1754,7 +1751,7 @@ func (scope *EvalScope) variablesByTag(tag dwarf.Tag, cfg *LoadConfig) ([]*Varia
if entry.Tag != tag {
continue
}
val, err := scope.extractVariableFromEntry(entry)
val, err := scope.extractVarInfoFromEntry(entry)
if err != nil {
// skip variables that we can't parse yet
continue

95
scripts/gen-opcodes.go Normal file

@ -0,0 +1,95 @@
// This script generates pkg/dwarf/op/opcodes.go from pkg/dwarf/op/opcodes.txt
package main
import (
"bufio"
"bytes"
"fmt"
"go/format"
"log"
"os"
"strings"
)
type Opcode struct {
Name string
Code string
Args string
Func string
}
func main() {
fh, err := os.Open(os.Args[1])
if err != nil {
log.Fatal(err)
}
defer fh.Close()
outfh := os.Stdout
if os.Args[2] != "-" {
outfh, err = os.Create(os.Args[2])
if err != nil {
log.Fatal(err)
}
defer outfh.Close()
}
opcodes := []Opcode{}
s := bufio.NewScanner(fh)
for s.Scan() {
line := strings.TrimSpace(s.Text())
if line == "" || strings.HasPrefix(line, "//") {
continue
}
fields := strings.Split(line, "\t")
opcode := Opcode{Name: fields[0], Code: fields[1], Args: fields[2]}
if len(fields) > 3 {
opcode.Func = fields[3]
}
opcodes = append(opcodes, opcode)
}
var buf bytes.Buffer
fmt.Fprintf(&buf, `// THIS FILE IS AUTOGENERATED, EDIT opcodes.table INSTEAD
package op
`)
// constants
fmt.Fprintf(&buf, "const (\n")
for _, opcode := range opcodes {
fmt.Fprintf(&buf, "%s Opcode = %s\n", opcode.Name, opcode.Code)
}
fmt.Fprintf(&buf, ")\n\n")
// name map
fmt.Fprintf(&buf, "var opcodeName = map[Opcode]string{\n")
for _, opcode := range opcodes {
fmt.Fprintf(&buf, "%s: %q,\n", opcode.Name, opcode.Name)
}
fmt.Fprintf(&buf, "}\n")
// arguments map
fmt.Fprintf(&buf, "var opcodeArgs = map[Opcode]string{\n")
for _, opcode := range opcodes {
fmt.Fprintf(&buf, "%s: %s,\n", opcode.Name, opcode.Args)
}
fmt.Fprintf(&buf, "}\n")
// function map
fmt.Fprintf(&buf, "var oplut = map[Opcode]stackfn{\n")
for _, opcode := range opcodes {
if opcode.Func != "" {
fmt.Fprintf(&buf, "%s: %s,\n", opcode.Name, opcode.Func)
}
}
fmt.Fprintf(&buf, "}\n")
src, err := format.Source(buf.Bytes())
if err != nil {
log.Fatal(err)
}
outfh.Write(src)
}