aboutsummaryrefslogtreecommitdiff
path: root/src/debug/dwarf/entry.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/debug/dwarf/entry.go')
-rw-r--r--src/debug/dwarf/entry.go234
1 files changed, 179 insertions, 55 deletions
diff --git a/src/debug/dwarf/entry.go b/src/debug/dwarf/entry.go
index 88eb56936b..bc05d7ef31 100644
--- a/src/debug/dwarf/entry.go
+++ b/src/debug/dwarf/entry.go
@@ -453,38 +453,28 @@ func (b *buf) entry(cu *Entry, atab abbrevTable, ubase Offset, vers int) *Entry
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
}
@@ -935,53 +925,187 @@ func (d *Data) Ranges(e *Entry) ([][2]uint64, error) {
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
}