mirror of https://github.com/golang/go.git
debug/dwarf: add support for DWARFv5 to (*Data).Ranges
Updates the (*Data).Ranges method to work with DWARFv5 which uses the new debug_rnglists section instead of debug_ranges. This does not include supporting DW_FORM_rnglistx. General support for DWARFv5 was added by CL 175138. Change-Id: I01f919a865616a3ff12f5bf649c2c9abf89fcf52 Reviewed-on: https://go-review.googlesource.com/c/go/+/236657 Run-TryBot: Ian Lance Taylor <iant@golang.org> TryBot-Result: Go Bot <gobot@golang.org> Reviewed-by: Ian Lance Taylor <iant@golang.org> Trust: Emmanuel Odeke <emm.odeke@gmail.com>
This commit is contained in:
parent
bc0b198bd7
commit
05b6118139
|
|
@ -461,3 +461,15 @@ const (
|
|||
utSplitCompile = 0x05
|
||||
utSplitType = 0x06
|
||||
)
|
||||
|
||||
// Opcodes for DWARFv5 debug_rnglists section.
|
||||
const (
|
||||
rleEndOfList = 0x0
|
||||
rleBaseAddressx = 0x1
|
||||
rleStartxEndx = 0x2
|
||||
rleStartxLength = 0x3
|
||||
rleOffsetPair = 0x4
|
||||
rleBaseAddress = 0x5
|
||||
rleStartEnd = 0x6
|
||||
rleStartLength = 0x7
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
// Copyright 2020 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package dwarf
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDwarf5Ranges(t *testing.T) {
|
||||
rngLists, err := ioutil.ReadFile("testdata/debug_rnglists")
|
||||
if err != nil {
|
||||
t.Fatalf("could not read test data: %v", err)
|
||||
}
|
||||
|
||||
d := &Data{}
|
||||
d.order = binary.LittleEndian
|
||||
if err := d.AddSection(".debug_rnglists", rngLists); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ret, err := d.dwarf5Ranges(nil, 0x5fbd, 0xc, [][2]uint64{})
|
||||
if err != nil {
|
||||
t.Fatalf("could not read rnglist: %v", err)
|
||||
}
|
||||
t.Logf("%#v", ret)
|
||||
|
||||
tgt := [][2]uint64{{0x0000000000006712, 0x000000000000679f}, {0x00000000000067af}, {0x00000000000067b3}}
|
||||
|
||||
if reflect.DeepEqual(ret, tgt) {
|
||||
t.Errorf("expected %#v got %#x", tgt, ret)
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
||||
var err error
|
||||
val, err = b.dwarf.debugAddr(uint64(addrBase), off)
|
||||
if err != nil {
|
||||
if b.err == nil {
|
||||
b.err = err
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -935,53 +925,187 @@ func (d *Data) Ranges(e *Entry) ([][2]uint64, error) {
|
|||
ret = append(ret, [2]uint64{low, high})
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// DWARF version 2 through 4
|
||||
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
|
||||
_, base, err := d.baseAddressForEntry(e)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return d.dwarf2Ranges(u, base, ranges, ret)
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
i := d.offsetToUnit(e.Offset)
|
||||
if i == -1 {
|
||||
return nil, 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, b.err
|
||||
}
|
||||
}
|
||||
|
||||
var base uint64
|
||||
if cuEntry, cuEntryOK := cu.Val(AttrEntrypc).(uint64); cuEntryOK {
|
||||
base = cuEntry
|
||||
} else if cuLow, cuLowOK := cu.Val(AttrLowpc).(uint64); cuLowOK {
|
||||
base = cuLow
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
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})
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
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})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,10 @@
|
|||
// 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).
|
||||
|
|
@ -23,9 +26,10 @@ type Data struct {
|
|||
str []byte
|
||||
|
||||
// New sections added in DWARF 5.
|
||||
addr []byte
|
||||
addr *debugAddr
|
||||
lineStr []byte
|
||||
strOffsets []byte
|
||||
rngLists *rngLists
|
||||
|
||||
// parsed data
|
||||
abbrevCache map[uint64]abbrevTable
|
||||
|
|
@ -36,6 +40,23 @@ type Data struct {
|
|||
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,
|
||||
|
|
@ -108,14 +129,79 @@ func (d *Data) AddTypes(name string, types []byte) error {
|
|||
// 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)
|
||||
}
|
||||
|
|
|
|||
Binary file not shown.
Loading…
Reference in New Issue