delve/pkg/dwarf/dwarfbuilder/info.go
Alessandro Arzilli 29eae8f617
*: clean up staticcheck problems (#2723)
Fix problems that can be fixed, ignore the ones that don't make sense
2021-09-28 12:07:42 -07:00

316 lines
9.1 KiB
Go

package dwarfbuilder
import (
"bytes"
"debug/dwarf"
"encoding/binary"
"github.com/go-delve/delve/pkg/dwarf/godwarf"
"github.com/go-delve/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)
if name != "" {
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]
}
// Attr adds an attribute to the current DIE.
func (b *Builder) Attr(attr dwarf.Attr, val interface{}) dwarf.Offset {
if len(b.tagStack) == 0 {
panic("Attr with no open tags")
}
off := dwarf.Offset(b.info.Len())
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 uint64:
tag.form = append(tag.form, DW_FORM_data8)
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")
}
return off
}
// PatchOffset writes the offset 'patch' at offset patchedOffset.
func (b *Builder) PatchOffset(patchedOffset, patch dwarf.Offset) {
infoBytes := b.info.Bytes()
buf := new(bytes.Buffer)
binary.Write(buf, binary.LittleEndian, patch)
copy(infoBytes[patchedOffset:], buf.Bytes())
}
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()
}
func (b *Builder) AddCompileUnit(name string, lowPC uint64) dwarf.Offset {
r := b.TagOpen(dwarf.TagCompileUnit, name)
b.Attr(dwarf.AttrLowpc, lowPC)
return r
}
// 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
}
// AddPointerType adds a new pointer type to debug_info.
func (b *Builder) AddPointerType(typename string, typ dwarf.Offset) dwarf.Offset {
r := b.TagOpen(dwarf.TagPointerType, typename)
b.Attr(dwarf.AttrType, typ)
b.Attr(godwarf.AttrGoKind, uint8(22))
b.TagClose()
return r
}