diff --git a/pkg/dwarf/dwarfbuilder/builder.go b/pkg/dwarf/dwarfbuilder/builder.go new file mode 100644 index 00000000..24469186 --- /dev/null +++ b/pkg/dwarf/dwarfbuilder/builder.go @@ -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 +} diff --git a/pkg/dwarf/dwarfbuilder/info.go b/pkg/dwarf/dwarfbuilder/info.go new file mode 100644 index 00000000..9716a505 --- /dev/null +++ b/pkg/dwarf/dwarfbuilder/info.go @@ -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 +} diff --git a/pkg/dwarf/dwarfbuilder/loc.go b/pkg/dwarf/dwarfbuilder/loc.go new file mode 100644 index 00000000..47ef7112 --- /dev/null +++ b/pkg/dwarf/dwarfbuilder/loc.go @@ -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() +} diff --git a/pkg/dwarf/godwarf/type.go b/pkg/dwarf/godwarf/type.go index 7b96c3d1..29dec44e 100644 --- a/pkg/dwarf/godwarf/type.go +++ b/pkg/dwarf/godwarf/type.go @@ -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 ] f.ByteOffset = int64(b.Uint()) b.AssertEmpty() case op.DW_OP_consts: // Handle opcode sequence [DW_OP_consts 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 diff --git a/pkg/dwarf/line/state_machine.go b/pkg/dwarf/line/state_machine.go index 188d94b8..e9dc4937 100644 --- a/pkg/dwarf/line/state_machine.go +++ b/pkg/dwarf/line/state_machine.go @@ -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 ( diff --git a/pkg/dwarf/op/op.go b/pkg/dwarf/op/op.go index b8c465bc..5b32a3b8 100644 --- a/pkg/dwarf/op/op.go +++ b/pkg/dwarf/op/op.go @@ -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) { diff --git a/pkg/dwarf/op/op_const.go b/pkg/dwarf/op/op_const.go new file mode 100644 index 00000000..3017bc54 --- /dev/null +++ b/pkg/dwarf/op/op_const.go @@ -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 +) diff --git a/pkg/dwarf/util/util.go b/pkg/dwarf/util/util.go index f60c203a..012f5308 100644 --- a/pkg/dwarf/util/util.go +++ b/pkg/dwarf/util/util.go @@ -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 { diff --git a/pkg/dwarf/util/util_test.go b/pkg/dwarf/util/util_test.go index d385b5cb..63bb4190 100644 --- a/pkg/dwarf/util/util_test.go +++ b/pkg/dwarf/util/util_test.go @@ -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) diff --git a/pkg/proc/bininfo.go b/pkg/proc/bininfo.go index 42aac9df..12a03ca0 100644 --- a/pkg/proc/bininfo.go +++ b/pkg/proc/bininfo.go @@ -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 { diff --git a/pkg/proc/dwarf_expr_test.go b/pkg/proc/dwarf_expr_test.go new file mode 100644 index 00000000..24f56149 --- /dev/null +++ b/pkg/proc/dwarf_expr_test.go @@ -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) +} diff --git a/pkg/proc/variables.go b/pkg/proc/variables.go index a30a4838..6b4ce5da 100644 --- a/pkg/proc/variables.go +++ b/pkg/proc/variables.go @@ -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.