case formAddrx4:
off = uint64(b.uint32())
}
- if len(b.dwarf.addr) == 0 {
+ if b.dwarf.addr == nil {
b.error("DW_FORM_addrx with no .debug_addr section")
}
if b.err != nil {
return nil
}
- addrsize := b.format.addrsize()
- if addrsize == 0 {
- b.error("unknown address size for DW_FORM_addrx")
- }
- off *= uint64(addrsize)
// We have to adjust by the offset of the
// compilation unit. This won't work if the
// program uses Reader.Seek to skip over the
// unit. Not much we can do about that.
+ var addrBase int64
if cu != nil {
- cuOff, ok := cu.Val(AttrAddrBase).(int64)
- if ok {
- off += uint64(cuOff)
- }
+ addrBase, _ = cu.Val(AttrAddrBase).(int64)
}
- if uint64(int(off)) != off {
- b.error("DW_FORM_addrx offset out of range")
- }
-
- b1 := makeBuf(b.dwarf, b.format, "addr", 0, b.dwarf.addr)
- b1.skip(int(off))
- val = b1.addr()
- if b1.err != nil {
- b.err = b1.err
+ var err error
+ val, err = b.dwarf.debugAddr(uint64(addrBase), off)
+ if err != nil {
+ if b.err == nil {
+ b.err = err
+ }
return nil
}
ret = append(ret, [2]uint64{low, high})
}
- ranges, rangesOK := e.Val(AttrRanges).(int64)
- if rangesOK && d.ranges != nil {
- // The initial base address is the lowpc attribute
- // of the enclosing compilation unit.
- // Although DWARF specifies the lowpc attribute,
- // comments in gdb/dwarf2read.c say that some versions
- // of GCC use the entrypc attribute, so we check that too.
- var cu *Entry
- if e.Tag == TagCompileUnit {
- cu = e
- } else {
- i := d.offsetToUnit(e.Offset)
- if i == -1 {
- return nil, errors.New("no unit for entry")
+ var u *unit
+ if uidx := d.offsetToUnit(e.Offset); uidx >= 0 && uidx < len(d.unit) {
+ u = &d.unit[uidx]
+ }
+
+ if u != nil && u.vers >= 5 && d.rngLists != nil {
+ // DWARF version 5 and later
+ field := e.AttrField(AttrRanges)
+ if field == nil {
+ return ret, nil
+ }
+ switch field.Class {
+ case ClassRangeListPtr:
+ ranges, rangesOK := field.Val.(int64)
+ if !rangesOK {
+ return ret, nil
}
- u := &d.unit[i]
- b := makeBuf(d, u, "info", u.off, u.data)
- cu = b.entry(nil, u.atable, u.base, u.vers)
- if b.err != nil {
- return nil, b.err
+ cu, base, err := d.baseAddressForEntry(e)
+ if err != nil {
+ return nil, err
}
+ return d.dwarf5Ranges(cu, base, ranges, ret)
+
+ case ClassRngList:
+ // TODO: support DW_FORM_rnglistx
+ return ret, nil
+
+ default:
+ return ret, nil
}
+ }
- var base uint64
- if cuEntry, cuEntryOK := cu.Val(AttrEntrypc).(uint64); cuEntryOK {
- base = cuEntry
- } else if cuLow, cuLowOK := cu.Val(AttrLowpc).(uint64); cuLowOK {
- base = cuLow
+ // DWARF version 2 through 4
+ ranges, rangesOK := e.Val(AttrRanges).(int64)
+ if rangesOK && d.ranges != nil {
+ _, base, err := d.baseAddressForEntry(e)
+ if err != nil {
+ return nil, err
}
+ return d.dwarf2Ranges(u, base, ranges, ret)
+ }
- u := &d.unit[d.offsetToUnit(e.Offset)]
- buf := makeBuf(d, u, "ranges", Offset(ranges), d.ranges[ranges:])
- for len(buf.data) > 0 {
- low = buf.addr()
- high = buf.addr()
+ return ret, nil
+}
- if low == 0 && high == 0 {
- break
+// baseAddressForEntry returns the initial base address to be used when
+// looking up the range list of entry e.
+// DWARF specifies that this should be the lowpc attribute of the enclosing
+// compilation unit, however comments in gdb/dwarf2read.c say that some
+// versions of GCC use the entrypc attribute, so we check that too.
+func (d *Data) baseAddressForEntry(e *Entry) (*Entry, uint64, error) {
+ var cu *Entry
+ if e.Tag == TagCompileUnit {
+ cu = e
+ } else {
+ i := d.offsetToUnit(e.Offset)
+ if i == -1 {
+ return nil, 0, errors.New("no unit for entry")
+ }
+ u := &d.unit[i]
+ b := makeBuf(d, u, "info", u.off, u.data)
+ cu = b.entry(nil, u.atable, u.base, u.vers)
+ if b.err != nil {
+ return nil, 0, b.err
+ }
+ }
+
+ if cuEntry, cuEntryOK := cu.Val(AttrEntrypc).(uint64); cuEntryOK {
+ return cu, cuEntry, nil
+ } else if cuLow, cuLowOK := cu.Val(AttrLowpc).(uint64); cuLowOK {
+ return cu, cuLow, nil
+ }
+
+ return cu, 0, nil
+}
+
+func (d *Data) dwarf2Ranges(u *unit, base uint64, ranges int64, ret [][2]uint64) ([][2]uint64, error) {
+ buf := makeBuf(d, u, "ranges", Offset(ranges), d.ranges[ranges:])
+ for len(buf.data) > 0 {
+ low := buf.addr()
+ high := buf.addr()
+
+ if low == 0 && high == 0 {
+ break
+ }
+
+ if low == ^uint64(0)>>uint((8-u.addrsize())*8) {
+ base = high
+ } else {
+ ret = append(ret, [2]uint64{base + low, base + high})
+ }
+ }
+
+ return ret, nil
+}
+
+// dwarf5Ranges interpets a debug_rnglists sequence, see DWARFv5 section
+// 2.17.3 (page 53).
+func (d *Data) dwarf5Ranges(cu *Entry, base uint64, ranges int64, ret [][2]uint64) ([][2]uint64, error) {
+ var addrBase int64
+ if cu != nil {
+ addrBase, _ = cu.Val(AttrAddrBase).(int64)
+ }
+
+ buf := makeBuf(d, d.rngLists, "rnglists", 0, d.rngLists.data)
+ buf.skip(int(ranges))
+ for {
+ opcode := buf.uint8()
+ switch opcode {
+ case rleEndOfList:
+ if buf.err != nil {
+ return nil, buf.err
+ }
+ return ret, nil
+
+ case rleBaseAddressx:
+ baseIdx := buf.uint()
+ var err error
+ base, err = d.debugAddr(uint64(addrBase), baseIdx)
+ if err != nil {
+ return nil, err
}
- if low == ^uint64(0)>>uint((8-u.addrsize())*8) {
- base = high
- } else {
- ret = append(ret, [2]uint64{base + low, base + high})
+ case rleStartxEndx:
+ startIdx := buf.uint()
+ endIdx := buf.uint()
+
+ start, err := d.debugAddr(uint64(addrBase), startIdx)
+ if err != nil {
+ return nil, err
+ }
+ end, err := d.debugAddr(uint64(addrBase), endIdx)
+ if err != nil {
+ return nil, err
+ }
+ ret = append(ret, [2]uint64{start, end})
+
+ case rleStartxLength:
+ startIdx := buf.uint()
+ len := buf.uint()
+ start, err := d.debugAddr(uint64(addrBase), startIdx)
+ if err != nil {
+ return nil, err
}
+ ret = append(ret, [2]uint64{start, start + len})
+
+ case rleOffsetPair:
+ off1 := buf.uint()
+ off2 := buf.uint()
+ ret = append(ret, [2]uint64{base + off1, base + off2})
+
+ case rleBaseAddress:
+ base = buf.addr()
+
+ case rleStartEnd:
+ start := buf.addr()
+ end := buf.addr()
+ ret = append(ret, [2]uint64{start, end})
+
+ case rleStartLength:
+ start := buf.addr()
+ len := buf.uint()
+ ret = append(ret, [2]uint64{start, start + len})
}
}
+}
- return ret, nil
+// debugAddr returns the address at idx in debug_addr
+func (d *Data) debugAddr(addrBase, idx uint64) (uint64, error) {
+ off := idx*uint64(d.addr.addrsize()) + addrBase
+
+ if uint64(int(off)) != off {
+ return 0, errors.New("offset out of range")
+ }
+
+ b := makeBuf(d, d.addr, "addr", 0, d.addr.data)
+ b.skip(int(off))
+ val := b.addr()
+ if b.err != nil {
+ return 0, b.err
+ }
+
+ return val, nil
}
// http://dwarfstd.org/doc/dwarf-2.0.0.pdf
package dwarf
-import "encoding/binary"
+import (
+ "encoding/binary"
+ "errors"
+)
// Data represents the DWARF debugging information
// loaded from an executable file (for example, an ELF or Mach-O executable).
str []byte
// New sections added in DWARF 5.
- addr []byte
+ addr *debugAddr
lineStr []byte
strOffsets []byte
+ rngLists *rngLists
// parsed data
abbrevCache map[uint64]abbrevTable
unit []unit
}
+// rngLists represents the contents of a debug_rnglists section (DWARFv5).
+type rngLists struct {
+ is64 bool
+ asize uint8
+ data []byte
+ ver uint16
+}
+
+// debugAddr represents the contents of a debug_addr section (DWARFv5).
+type debugAddr struct {
+ is64 bool
+ asize uint8
+ data []byte
+}
+
+var errSegmentSelector = errors.New("non-zero segment_selector size not supported")
+
// New returns a new Data object initialized from the given parameters.
// Rather than calling this function directly, clients should typically use
// the DWARF method of the File type of the appropriate package debug/elf,
// so forth. This approach is used for new DWARF sections added in
// DWARF 5 and later.
func (d *Data) AddSection(name string, contents []byte) error {
+ var err error
switch name {
case ".debug_addr":
- d.addr = contents
+ d.addr, err = d.parseAddrHeader(contents)
case ".debug_line_str":
d.lineStr = contents
case ".debug_str_offsets":
d.strOffsets = contents
+ case ".debug_rnglists":
+ d.rngLists, err = d.parseRngListsHeader(contents)
}
// Just ignore names that we don't yet support.
- return nil
+ return err
+}
+
+// parseRngListsHeader reads the header of a debug_rnglists section, see
+// DWARFv5 section 7.28 (page 242).
+func (d *Data) parseRngListsHeader(bytes []byte) (*rngLists, error) {
+ rngLists := &rngLists{data: bytes}
+
+ buf := makeBuf(d, unknownFormat{}, "rnglists", 0, bytes)
+ _, rngLists.is64 = buf.unitLength()
+
+ rngLists.ver = buf.uint16() // version
+
+ rngLists.asize = buf.uint8()
+ segsize := buf.uint8()
+ if segsize != 0 {
+ return nil, errSegmentSelector
+ }
+
+ // Header fields not read: offset_entry_count, offset table
+
+ return rngLists, nil
+}
+
+func (rngLists *rngLists) version() int {
+ return int(rngLists.ver)
+}
+
+func (rngLists *rngLists) dwarf64() (bool, bool) {
+ return rngLists.is64, true
+}
+
+func (rngLists *rngLists) addrsize() int {
+ return int(rngLists.asize)
+}
+
+// parseAddrHeader reads the header of a debug_addr section, see DWARFv5
+// section 7.27 (page 241).
+func (d *Data) parseAddrHeader(bytes []byte) (*debugAddr, error) {
+ addr := &debugAddr{data: bytes}
+
+ buf := makeBuf(d, unknownFormat{}, "addr", 0, bytes)
+ _, addr.is64 = buf.unitLength()
+
+ addr.asize = buf.uint8()
+ segsize := buf.uint8()
+ if segsize != 0 {
+ return nil, errSegmentSelector
+ }
+
+ return addr, nil
+}
+
+func (addr *debugAddr) version() int {
+ return 5
+}
+
+func (addr *debugAddr) dwarf64() (bool, bool) {
+ return addr.is64, true
+}
+
+func (addr *debugAddr) addrsize() int {
+ return int(addr.asize)
}