mirror of
https://github.com/go-delve/delve.git
synced 2025-10-27 20:23:41 +08:00
proc/tests: testing apparatus for complex location expressions
This commit is contained in:
50
pkg/dwarf/dwarfbuilder/builder.go
Normal file
50
pkg/dwarf/dwarfbuilder/builder.go
Normal file
@ -0,0 +1,50 @@
|
||||
// Package dwarfbuilder provides a way to build DWARF sections with
|
||||
// arbitrary contents.
|
||||
package dwarfbuilder
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"debug/dwarf"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Builder struct {
|
||||
info bytes.Buffer
|
||||
loc bytes.Buffer
|
||||
abbrevs []tagDescr
|
||||
tagStack []*tagState
|
||||
}
|
||||
|
||||
// New creates a new DWARF builder.
|
||||
func New() *Builder {
|
||||
b := &Builder{}
|
||||
|
||||
b.info.Write([]byte{
|
||||
0x0, 0x0, 0x0, 0x0, // length
|
||||
0x4, 0x0, // version
|
||||
0x0, 0x0, 0x0, 0x0, // debug_abbrev_offset
|
||||
0x8, // address_size
|
||||
})
|
||||
|
||||
b.TagOpen(dwarf.TagCompileUnit, "go")
|
||||
b.Attr(dwarf.AttrLanguage, uint8(22))
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// Build closes b and returns all the dwarf sections.
|
||||
func (b *Builder) Build() (abbrev, aranges, frame, info, line, pubnames, ranges, str []byte, err error) {
|
||||
b.TagClose()
|
||||
|
||||
if len(b.tagStack) > 0 {
|
||||
err = fmt.Errorf("unbalanced TagOpen/TagClose %d", len(b.tagStack))
|
||||
return
|
||||
}
|
||||
|
||||
abbrev = b.makeAbbrevTable()
|
||||
info = b.info.Bytes()
|
||||
binary.LittleEndian.PutUint32(info, uint32(len(info)-4))
|
||||
|
||||
return
|
||||
}
|
||||
284
pkg/dwarf/dwarfbuilder/info.go
Normal file
284
pkg/dwarf/dwarfbuilder/info.go
Normal file
@ -0,0 +1,284 @@
|
||||
package dwarfbuilder
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"debug/dwarf"
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/derekparker/delve/pkg/dwarf/util"
|
||||
)
|
||||
|
||||
// Form represents a DWARF form kind (see Figure 20, page 160 and following,
|
||||
// DWARF v4)
|
||||
type Form uint16
|
||||
|
||||
const (
|
||||
DW_FORM_addr Form = 0x01 // address
|
||||
DW_FORM_block2 Form = 0x03 // block
|
||||
DW_FORM_block4 Form = 0x04 // block
|
||||
DW_FORM_data2 Form = 0x05 // constant
|
||||
DW_FORM_data4 Form = 0x06 // constant
|
||||
DW_FORM_data8 Form = 0x07 // constant
|
||||
DW_FORM_string Form = 0x08 // string
|
||||
DW_FORM_block Form = 0x09 // block
|
||||
DW_FORM_block1 Form = 0x0a // block
|
||||
DW_FORM_data1 Form = 0x0b // constant
|
||||
DW_FORM_flag Form = 0x0c // flag
|
||||
DW_FORM_sdata Form = 0x0d // constant
|
||||
DW_FORM_strp Form = 0x0e // string
|
||||
DW_FORM_udata Form = 0x0f // constant
|
||||
DW_FORM_ref_addr Form = 0x10 // reference
|
||||
DW_FORM_ref1 Form = 0x11 // reference
|
||||
DW_FORM_ref2 Form = 0x12 // reference
|
||||
DW_FORM_ref4 Form = 0x13 // reference
|
||||
DW_FORM_ref8 Form = 0x14 // reference
|
||||
DW_FORM_ref_udata Form = 0x15 // reference
|
||||
DW_FORM_indirect Form = 0x16 // (see Section 7.5.3)
|
||||
DW_FORM_sec_offset Form = 0x17 // lineptr, loclistptr, macptr, rangelistptr
|
||||
DW_FORM_exprloc Form = 0x18 // exprloc
|
||||
DW_FORM_flag_present Form = 0x19 // flag
|
||||
DW_FORM_ref_sig8 Form = 0x20 // reference
|
||||
)
|
||||
|
||||
// Encoding represents a DWARF base type encoding (see section 7.8, page 168
|
||||
// and following, DWARF v4).
|
||||
type Encoding uint16
|
||||
|
||||
const (
|
||||
DW_ATE_address Encoding = 0x01
|
||||
DW_ATE_boolean Encoding = 0x02
|
||||
DW_ATE_complex_float Encoding = 0x03
|
||||
DW_ATE_float Encoding = 0x04
|
||||
DW_ATE_signed Encoding = 0x05
|
||||
DW_ATE_signed_char Encoding = 0x06
|
||||
DW_ATE_unsigned Encoding = 0x07
|
||||
DW_ATE_unsigned_char Encoding = 0x08
|
||||
DW_ATE_imaginary_float Encoding = 0x09
|
||||
DW_ATE_packed_decimal Encoding = 0x0a
|
||||
DW_ATE_numeric_string Encoding = 0x0b
|
||||
DW_ATE_edited Encoding = 0x0c
|
||||
DW_ATE_signed_fixed Encoding = 0x0d
|
||||
DW_ATE_unsigned_fixed Encoding = 0x0e
|
||||
DW_ATE_decimal_float Encoding = 0x0f
|
||||
DW_ATE_UTF Encoding = 0x10
|
||||
DW_ATE_lo_user Encoding = 0x80
|
||||
DW_ATE_hi_user Encoding = 0xff
|
||||
)
|
||||
|
||||
// Address represents a machine address.
|
||||
type Address uint64
|
||||
|
||||
type tagDescr struct {
|
||||
tag dwarf.Tag
|
||||
|
||||
attr []dwarf.Attr
|
||||
form []Form
|
||||
children bool
|
||||
}
|
||||
|
||||
type tagState struct {
|
||||
off dwarf.Offset
|
||||
tagDescr
|
||||
}
|
||||
|
||||
// TagOpen starts a new DIE, call TagClose after adding all attributes and
|
||||
// children elements.
|
||||
func (b *Builder) TagOpen(tag dwarf.Tag, name string) dwarf.Offset {
|
||||
if len(b.tagStack) > 0 {
|
||||
b.tagStack[len(b.tagStack)-1].children = true
|
||||
}
|
||||
ts := &tagState{off: dwarf.Offset(b.info.Len())}
|
||||
ts.tag = tag
|
||||
b.info.WriteByte(0)
|
||||
b.tagStack = append(b.tagStack, ts)
|
||||
b.Attr(dwarf.AttrName, name)
|
||||
|
||||
return ts.off
|
||||
}
|
||||
|
||||
// SetHasChildren sets the current DIE as having children (even if none are added).
|
||||
func (b *Builder) SetHasChildren() {
|
||||
if len(b.tagStack) <= 0 {
|
||||
panic("NoChildren with no open tags")
|
||||
}
|
||||
b.tagStack[len(b.tagStack)-1].children = true
|
||||
}
|
||||
|
||||
// TagClose closes the current DIE.
|
||||
func (b *Builder) TagClose() {
|
||||
if len(b.tagStack) <= 0 {
|
||||
panic("TagClose with no open tags")
|
||||
}
|
||||
tag := b.tagStack[len(b.tagStack)-1]
|
||||
abbrev := b.abbrevFor(tag.tagDescr)
|
||||
b.info.Bytes()[tag.off] = abbrev
|
||||
if tag.children {
|
||||
b.info.WriteByte(0)
|
||||
}
|
||||
b.tagStack = b.tagStack[:len(b.tagStack)-1]
|
||||
return
|
||||
}
|
||||
|
||||
// Attr adds an attribute to the current DIE.
|
||||
func (b *Builder) Attr(attr dwarf.Attr, val interface{}) {
|
||||
if len(b.tagStack) < 0 {
|
||||
panic("Attr with no open tags")
|
||||
}
|
||||
tag := b.tagStack[len(b.tagStack)-1]
|
||||
if tag.children {
|
||||
panic("Can't add attributes after adding children")
|
||||
}
|
||||
|
||||
tag.attr = append(tag.attr, attr)
|
||||
|
||||
switch x := val.(type) {
|
||||
case string:
|
||||
tag.form = append(tag.form, DW_FORM_string)
|
||||
b.info.Write([]byte(x))
|
||||
b.info.WriteByte(0)
|
||||
case uint8:
|
||||
tag.form = append(tag.form, DW_FORM_data1)
|
||||
binary.Write(&b.info, binary.LittleEndian, x)
|
||||
case uint16:
|
||||
tag.form = append(tag.form, DW_FORM_data2)
|
||||
binary.Write(&b.info, binary.LittleEndian, x)
|
||||
case Address:
|
||||
tag.form = append(tag.form, DW_FORM_addr)
|
||||
binary.Write(&b.info, binary.LittleEndian, x)
|
||||
case dwarf.Offset:
|
||||
tag.form = append(tag.form, DW_FORM_ref_addr)
|
||||
binary.Write(&b.info, binary.LittleEndian, x)
|
||||
case []byte:
|
||||
tag.form = append(tag.form, DW_FORM_block4)
|
||||
binary.Write(&b.info, binary.LittleEndian, uint32(len(x)))
|
||||
b.info.Write(x)
|
||||
case []LocEntry:
|
||||
tag.form = append(tag.form, DW_FORM_sec_offset)
|
||||
binary.Write(&b.info, binary.LittleEndian, uint32(b.loc.Len()))
|
||||
|
||||
// base address
|
||||
binary.Write(&b.loc, binary.LittleEndian, ^uint64(0))
|
||||
binary.Write(&b.loc, binary.LittleEndian, uint64(0))
|
||||
|
||||
for _, locentry := range x {
|
||||
binary.Write(&b.loc, binary.LittleEndian, uint64(locentry.Lowpc))
|
||||
binary.Write(&b.loc, binary.LittleEndian, uint64(locentry.Highpc))
|
||||
binary.Write(&b.loc, binary.LittleEndian, uint16(len(locentry.Loc)))
|
||||
b.loc.Write(locentry.Loc)
|
||||
}
|
||||
|
||||
// end of loclist
|
||||
binary.Write(&b.loc, binary.LittleEndian, uint64(0))
|
||||
binary.Write(&b.loc, binary.LittleEndian, uint64(0))
|
||||
default:
|
||||
panic("unknown value type")
|
||||
}
|
||||
}
|
||||
|
||||
func sameTagDescr(a, b tagDescr) bool {
|
||||
if a.tag != b.tag {
|
||||
return false
|
||||
}
|
||||
if len(a.attr) != len(b.attr) {
|
||||
return false
|
||||
}
|
||||
if a.children != b.children {
|
||||
return false
|
||||
}
|
||||
for i := range a.attr {
|
||||
if a.attr[i] != b.attr[i] {
|
||||
return false
|
||||
}
|
||||
if a.form[i] != b.form[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// abbrevFor returns an abbrev for the given entry description. If no abbrev
|
||||
// for tag already exist a new one is created.
|
||||
func (b *Builder) abbrevFor(tag tagDescr) byte {
|
||||
for abbrev, descr := range b.abbrevs {
|
||||
if sameTagDescr(descr, tag) {
|
||||
return byte(abbrev + 1)
|
||||
}
|
||||
}
|
||||
|
||||
b.abbrevs = append(b.abbrevs, tag)
|
||||
return byte(len(b.abbrevs))
|
||||
}
|
||||
|
||||
func (b *Builder) makeAbbrevTable() []byte {
|
||||
var abbrev bytes.Buffer
|
||||
|
||||
for i := range b.abbrevs {
|
||||
util.EncodeULEB128(&abbrev, uint64(i+1))
|
||||
util.EncodeULEB128(&abbrev, uint64(b.abbrevs[i].tag))
|
||||
if b.abbrevs[i].children {
|
||||
abbrev.WriteByte(0x01)
|
||||
} else {
|
||||
abbrev.WriteByte(0x00)
|
||||
}
|
||||
for j := range b.abbrevs[i].attr {
|
||||
util.EncodeULEB128(&abbrev, uint64(b.abbrevs[i].attr[j]))
|
||||
util.EncodeULEB128(&abbrev, uint64(b.abbrevs[i].form[j]))
|
||||
}
|
||||
util.EncodeULEB128(&abbrev, 0)
|
||||
util.EncodeULEB128(&abbrev, 0)
|
||||
}
|
||||
|
||||
return abbrev.Bytes()
|
||||
}
|
||||
|
||||
// AddSubprogram adds a subprogram declaration to debug_info, must call
|
||||
// TagClose after adding all local variables and parameters.
|
||||
// Will write an abbrev corresponding to a DW_TAG_subprogram, followed by a
|
||||
// DW_AT_lowpc and a DW_AT_highpc.
|
||||
func (b *Builder) AddSubprogram(fnname string, lowpc, highpc uint64) dwarf.Offset {
|
||||
r := b.TagOpen(dwarf.TagSubprogram, fnname)
|
||||
b.Attr(dwarf.AttrLowpc, Address(lowpc))
|
||||
b.Attr(dwarf.AttrHighpc, Address(highpc))
|
||||
return r
|
||||
}
|
||||
|
||||
// AddVariable adds a new variable entry to debug_info.
|
||||
// Will write a DW_TAG_variable, followed by a DW_AT_type and a
|
||||
// DW_AT_location.
|
||||
func (b *Builder) AddVariable(varname string, typ dwarf.Offset, loc interface{}) dwarf.Offset {
|
||||
r := b.TagOpen(dwarf.TagVariable, varname)
|
||||
b.Attr(dwarf.AttrType, typ)
|
||||
b.Attr(dwarf.AttrLocation, loc)
|
||||
b.TagClose()
|
||||
return r
|
||||
}
|
||||
|
||||
// AddBaseType adds a new base type entry to debug_info.
|
||||
// Will write a DW_TAG_base_type, followed by a DW_AT_encoding and a
|
||||
// DW_AT_byte_size.
|
||||
func (b *Builder) AddBaseType(typename string, encoding Encoding, byteSz uint16) dwarf.Offset {
|
||||
r := b.TagOpen(dwarf.TagBaseType, typename)
|
||||
b.Attr(dwarf.AttrEncoding, uint16(encoding))
|
||||
b.Attr(dwarf.AttrByteSize, byteSz)
|
||||
b.TagClose()
|
||||
return r
|
||||
}
|
||||
|
||||
// AddStructType adds a new structure type to debug_info. Call TagClose to
|
||||
// finish adding fields.
|
||||
// Will write a DW_TAG_struct_type, followed by a DW_AT_byte_size.
|
||||
func (b *Builder) AddStructType(typename string, byteSz uint16) dwarf.Offset {
|
||||
r := b.TagOpen(dwarf.TagStructType, typename)
|
||||
b.Attr(dwarf.AttrByteSize, byteSz)
|
||||
return r
|
||||
}
|
||||
|
||||
// AddMember adds a new member entry to debug_info.
|
||||
// Writes a DW_TAG_member followed by DW_AT_type and DW_AT_data_member_loc.
|
||||
func (b *Builder) AddMember(fieldname string, typ dwarf.Offset, memberLoc []byte) dwarf.Offset {
|
||||
r := b.TagOpen(dwarf.TagMember, fieldname)
|
||||
b.Attr(dwarf.AttrType, typ)
|
||||
b.Attr(dwarf.AttrDataMemberLoc, memberLoc)
|
||||
b.TagClose()
|
||||
return r
|
||||
}
|
||||
34
pkg/dwarf/dwarfbuilder/loc.go
Normal file
34
pkg/dwarf/dwarfbuilder/loc.go
Normal file
@ -0,0 +1,34 @@
|
||||
package dwarfbuilder
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/derekparker/delve/pkg/dwarf/op"
|
||||
"github.com/derekparker/delve/pkg/dwarf/util"
|
||||
)
|
||||
|
||||
// LocEntry represents one entry of debug_loc.
|
||||
type LocEntry struct {
|
||||
Lowpc uint64
|
||||
Highpc uint64
|
||||
Loc []byte
|
||||
}
|
||||
|
||||
// LocationBlock returns a DWARF expression corresponding to the list of
|
||||
// arguments.
|
||||
func LocationBlock(args ...interface{}) []byte {
|
||||
var buf bytes.Buffer
|
||||
for _, arg := range args {
|
||||
switch x := arg.(type) {
|
||||
case op.Opcode:
|
||||
buf.WriteByte(byte(x))
|
||||
case int:
|
||||
util.EncodeSLEB128(&buf, int64(x))
|
||||
case uint:
|
||||
util.EncodeULEB128(&buf, uint64(x))
|
||||
default:
|
||||
panic("unsupported value type")
|
||||
}
|
||||
}
|
||||
return buf.Bytes()
|
||||
}
|
||||
@ -625,16 +625,16 @@ func readType(d *dwarf.Data, name string, r *dwarf.Reader, off dwarf.Offset, typ
|
||||
break
|
||||
}
|
||||
b := util.MakeBuf(d, util.UnknownFormat{}, "location", 0, loc)
|
||||
op_ := b.Uint8()
|
||||
op_ := op.Opcode(b.Uint8())
|
||||
switch op_ {
|
||||
case op.DW_OP_plus_uconsts:
|
||||
case op.DW_OP_plus_uconst:
|
||||
// Handle opcode sequence [DW_OP_plus_uconst <uleb128>]
|
||||
f.ByteOffset = int64(b.Uint())
|
||||
b.AssertEmpty()
|
||||
case op.DW_OP_consts:
|
||||
// Handle opcode sequence [DW_OP_consts <sleb128> DW_OP_plus]
|
||||
f.ByteOffset = b.Int()
|
||||
op_ = b.Uint8()
|
||||
op_ = op.Opcode(b.Uint8())
|
||||
if op_ != op.DW_OP_plus {
|
||||
err = dwarf.DecodeError{name, kid.Offset, fmt.Sprintf("unexpected opcode 0x%x", op_)}
|
||||
goto Error
|
||||
|
||||
@ -140,7 +140,7 @@ var NoSourceError = errors.New("no source available")
|
||||
|
||||
func (lineInfo *DebugLineInfo) AllPCsBetween(begin, end uint64) ([]uint64, error) {
|
||||
if lineInfo == nil {
|
||||
return nil, nil
|
||||
return nil, NoSourceError
|
||||
}
|
||||
|
||||
var (
|
||||
|
||||
@ -26,12 +26,14 @@ type context struct {
|
||||
DwarfRegisters
|
||||
}
|
||||
|
||||
var oplut = map[byte]stackfn{
|
||||
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_uconsts: plusuconsts,
|
||||
DW_OP_plus_uconst: plusuconsts,
|
||||
}
|
||||
|
||||
func ExecuteStackProgram(regs DwarfRegisters, instructions []byte) (int64, error) {
|
||||
|
||||
161
pkg/dwarf/op/op_const.go
Normal file
161
pkg/dwarf/op/op_const.go
Normal file
@ -0,0 +1,161 @@
|
||||
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
|
||||
)
|
||||
@ -1,6 +1,12 @@
|
||||
package util
|
||||
|
||||
import "bytes"
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
)
|
||||
|
||||
// The Little Endian Base 128 format is defined in the DWARF v4 standard,
|
||||
// section 7.6, page 161 and following.
|
||||
|
||||
// DecodeULEB128 decodes an unsigned Little Endian Base 128
|
||||
// represented number.
|
||||
@ -71,6 +77,45 @@ func DecodeSLEB128(buf *bytes.Buffer) (int64, uint32) {
|
||||
return result, length
|
||||
}
|
||||
|
||||
// EncodeULEB128 encodes x to the unsigned Little Endian Base 128 format
|
||||
// into out.
|
||||
func EncodeULEB128(out io.ByteWriter, x uint64) {
|
||||
for {
|
||||
b := byte(x & 0x7f)
|
||||
x = x >> 7
|
||||
if x != 0 {
|
||||
b = b | 0x80
|
||||
}
|
||||
out.WriteByte(b)
|
||||
if x == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// EncodeSLEB128 encodes x to the signed Little Endian Base 128 format
|
||||
// into out.
|
||||
func EncodeSLEB128(out io.ByteWriter, x int64) {
|
||||
for {
|
||||
b := byte(x & 0x7f)
|
||||
x >>= 7
|
||||
|
||||
signb := b & 0x40
|
||||
|
||||
last := false
|
||||
if (x == 0 && signb == 0) || (x == -1 && signb != 0) {
|
||||
last = true
|
||||
} else {
|
||||
b = b | 0x80
|
||||
}
|
||||
out.WriteByte(b)
|
||||
|
||||
if last {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ParseString(data *bytes.Buffer) (string, uint32) {
|
||||
str, err := data.ReadString(0x0)
|
||||
if err != nil {
|
||||
|
||||
@ -27,6 +27,42 @@ func TestDecodeSLEB128(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeULEB128(t *testing.T) {
|
||||
tc := []uint64{0x00, 0x7f, 0x80, 0x8f, 0xffff, 0xfffffff7}
|
||||
for i := range tc {
|
||||
var buf bytes.Buffer
|
||||
EncodeULEB128(&buf, tc[i])
|
||||
enc := append([]byte{}, buf.Bytes()...)
|
||||
buf.Write([]byte{0x1, 0x2, 0x3})
|
||||
out, c := DecodeULEB128(&buf)
|
||||
t.Logf("input %x output %x encoded %x", tc[i], out, enc)
|
||||
if c != uint32(len(enc)) {
|
||||
t.Errorf("wrong encode")
|
||||
}
|
||||
if out != tc[i] {
|
||||
t.Errorf("wrong encode")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeSLEB128(t *testing.T) {
|
||||
tc := []int64{2, -2, 127, -127, 128, -128, 129, -129}
|
||||
for i := range tc {
|
||||
var buf bytes.Buffer
|
||||
EncodeSLEB128(&buf, tc[i])
|
||||
enc := append([]byte{}, buf.Bytes()...)
|
||||
buf.Write([]byte{0x1, 0x2, 0x3})
|
||||
out, c := DecodeSLEB128(&buf)
|
||||
t.Logf("input %x output %x encoded %x", tc[i], out, enc)
|
||||
if c != uint32(len(enc)) {
|
||||
t.Errorf("wrong encode")
|
||||
}
|
||||
if out != tc[i] {
|
||||
t.Errorf("wrong encode")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseString(t *testing.T) {
|
||||
bstr := bytes.NewBuffer([]byte{'h', 'i', 0x0, 0xFF, 0xCC})
|
||||
str, _ := ParseString(bstr)
|
||||
|
||||
@ -224,6 +224,25 @@ func (bi *BinaryInfo) LoadError() error {
|
||||
return bi.loadErr
|
||||
}
|
||||
|
||||
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) {
|
||||
bi.closer = (*nilCloser)(nil)
|
||||
bi.dwarf = dwdata
|
||||
|
||||
if debugFrameBytes != nil {
|
||||
bi.frameEntries = frame.Parse(debugFrameBytes, frame.DwarfEndian(debugFrameBytes))
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go bi.loadDebugInfoMaps(debugLineBytes, &wg)
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// ELF ///////////////////////////////////////////////////////////////
|
||||
|
||||
func (bi *BinaryInfo) LoadBinaryInfoElf(path string, wg *sync.WaitGroup) error {
|
||||
|
||||
220
pkg/proc/dwarf_expr_test.go
Normal file
220
pkg/proc/dwarf_expr_test.go
Normal file
@ -0,0 +1,220 @@
|
||||
// Tests for loading variables that have complex location expressions. They
|
||||
// are only produced for optimized code (for both Go and C) therefore we can
|
||||
// not get the compiler to produce them reliably enough for tests.
|
||||
|
||||
package proc_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"debug/dwarf"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"testing"
|
||||
|
||||
"github.com/derekparker/delve/pkg/dwarf/dwarfbuilder"
|
||||
"github.com/derekparker/delve/pkg/dwarf/godwarf"
|
||||
"github.com/derekparker/delve/pkg/dwarf/op"
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
)
|
||||
|
||||
const defaultCFA = 0xc420051d00
|
||||
|
||||
func fakeBinaryInfo(t *testing.T, dwb *dwarfbuilder.Builder) *proc.BinaryInfo {
|
||||
abbrev, aranges, frame, info, line, pubnames, ranges, str, 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)
|
||||
|
||||
return &bi
|
||||
}
|
||||
|
||||
// fakeMemory implements proc.MemoryReadWriter by reading from a byte slice.
|
||||
// Byte 0 of "data" is at address "base".
|
||||
type fakeMemory struct {
|
||||
base uint64
|
||||
data []byte
|
||||
}
|
||||
|
||||
func newFakeMemory(base uint64, contents ...interface{}) *fakeMemory {
|
||||
mem := &fakeMemory{base: base}
|
||||
var buf bytes.Buffer
|
||||
for _, x := range contents {
|
||||
binary.Write(&buf, binary.LittleEndian, x)
|
||||
}
|
||||
mem.data = buf.Bytes()
|
||||
return mem
|
||||
}
|
||||
|
||||
func (mem *fakeMemory) ReadMemory(data []byte, addr uintptr) (int, error) {
|
||||
if uint64(addr) < mem.base {
|
||||
return 0, fmt.Errorf("read out of bounds %d %#x", len(data), addr)
|
||||
}
|
||||
start := uint64(addr) - mem.base
|
||||
end := uint64(len(data)) + start
|
||||
if end > uint64(len(mem.data)) {
|
||||
panic(fmt.Errorf("read out of bounds %d %#x", len(data), addr))
|
||||
}
|
||||
copy(data, mem.data[start:end])
|
||||
return len(data), nil
|
||||
}
|
||||
|
||||
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))
|
||||
if thevar.Unreadable != nil {
|
||||
t.Errorf("variable %q unreadable: %v", expr, thevar.Unreadable)
|
||||
} else {
|
||||
if v, _ := constant.Uint64Val(thevar.Value); v != tgt {
|
||||
t.Errorf("expected value %x got %x for %q", tgt, v, expr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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}
|
||||
for name, value := range testCases {
|
||||
uintExprCheck(t, scope, name, uint64(value))
|
||||
}
|
||||
|
||||
return scope
|
||||
}
|
||||
|
||||
func TestDwarfExprRegisters(t *testing.T) {
|
||||
testCases := map[string]uint16{
|
||||
"a": 0x1234,
|
||||
"b": 0x4321,
|
||||
"c": 0x2143,
|
||||
}
|
||||
|
||||
dwb := dwarfbuilder.New()
|
||||
|
||||
uint16off := dwb.BaseType("uint16", dwarfbuilder.DW_ATE_unsigned, 2)
|
||||
|
||||
dwb.Subprogram("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.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"]))
|
||||
|
||||
dwarfExprCheck(t, mem, ®s, bi, testCases)
|
||||
}
|
||||
|
||||
func TestDwarfExprComposite(t *testing.T) {
|
||||
testCases := map[string]uint16{
|
||||
"pair.k": 0x8765,
|
||||
"pair.v": 0x5678,
|
||||
}
|
||||
|
||||
const stringVal = "this is a string"
|
||||
|
||||
dwb := dwarfbuilder.New()
|
||||
|
||||
uint16off := dwb.BaseType("uint16", dwarfbuilder.DW_ATE_unsigned, 2)
|
||||
intoff := dwb.BaseType("int", dwarfbuilder.DW_ATE_signed, 8)
|
||||
|
||||
byteoff := dwb.BaseType("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)
|
||||
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.TagClose()
|
||||
|
||||
stringoff := dwb.StructType("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.TagClose()
|
||||
|
||||
dwb.Subprogram("main.main", 0x40100, 0x41000)
|
||||
dwb.Variable("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(
|
||||
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"]))
|
||||
|
||||
scope := dwarfExprCheck(t, mem, ®s, bi, testCases)
|
||||
|
||||
thevar, err := scope.EvalExpression("s", normalLoadConfig)
|
||||
assertNoError(err, t, fmt.Sprintf("EvalExpression(s)", "s"))
|
||||
if thevar.Unreadable != nil {
|
||||
t.Errorf("variable \"s\" unreadable: %v", thevar.Unreadable)
|
||||
} else {
|
||||
if v := constant.StringVal(thevar.Value); v != stringVal {
|
||||
t.Errorf("expected value %q got %q", stringVal, v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDwarfExprLoclist(t *testing.T) {
|
||||
const before = 0x1234
|
||||
const after = 0x4321
|
||||
|
||||
dwb := dwarfbuilder.New()
|
||||
|
||||
uint16off := dwb.BaseType("uint16", dwarfbuilder.DW_ATE_unsigned, 2)
|
||||
|
||||
dwb.Subprogram("main.main", 0x40100, 0x41000)
|
||||
dwb.Variable("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)},
|
||||
})
|
||||
dwb.TagClose()
|
||||
|
||||
bi := fakeBinaryInfo(t, dwb)
|
||||
|
||||
mem := newFakeMemory(defaultCFA, uint16(before), uint16(after))
|
||||
|
||||
scope := &proc.EvalScope{PC: 0x40100, CFA: defaultCFA, Mem: mem, Gvar: nil, BinInfo: bi}
|
||||
|
||||
uintExprCheck(t, scope, "a", before)
|
||||
scope.PC = 0x40800
|
||||
uintExprCheck(t, scope, "a", after)
|
||||
}
|
||||
@ -763,11 +763,12 @@ func (scope *EvalScope) extractVarInfoFromEntry(entry *dwarf.Entry) (*Variable,
|
||||
}
|
||||
|
||||
addr, err := op.ExecuteStackProgram(op.DwarfRegisters{CFA: scope.CFA}, instructions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return scope.newVariable(n, uintptr(addr), t), nil
|
||||
v := scope.newVariable(n, uintptr(addr), t)
|
||||
if err != nil {
|
||||
v.Unreadable = err
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// If v is a pointer a new variable is returned containing the value pointed by v.
|
||||
|
||||
Reference in New Issue
Block a user