Skip to content

Commit

Permalink
debug/dwarf: don't try to parse addr/rnglists header
Browse files Browse the repository at this point in the history
In an executable, the debug_addr and debug_rnglists sections are
assembled by concatenating the input sections, and each input section
has a header, and each header may have different attributes. So just
parsing the single header isn't right.  Parsing the header is not
necessary to handle offsets into these sections which is all we do.

Looking at the header is also problematic because GCC with
-gsplit-dwarf when using DWARF versions 2 through 4 emits a
.debug_addr section, but it has no header.  The header was only added
for DWARF 5. So we can't parse the header at all for that case, and we
can't even detect that case in general.

This CL also fixes SeekPC with addrx and strx formats, by not using
the wrong compilation unit to find the address or string base.
To make that work when parsing the compilation unit itself, we add
support for delay the resolution of those values until we know the base.

New test binaries built with

gcc -gdwarf-5 -no-pie debug/dwarf/testdata/line[12].c
(gcc (Debian 10.2.0-15) 10.2.0)

clang -gdwarf-5 -no-pie debug/dwarf/testdata/line[12].c
(clang version 9.0.1-14)

Change-Id: I66783e0eded629bf80c467767f781164d344a54d
Reviewed-on: https://go-review.googlesource.com/c/go/+/277233
Trust: Ian Lance Taylor <[email protected]>
Reviewed-by: Than McIntosh <[email protected]>
  • Loading branch information
ianlancetaylor committed Dec 14, 2020
1 parent be10af7 commit 828746e
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 151 deletions.
7 changes: 6 additions & 1 deletion src/debug/dwarf/dwarf5ranges_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,12 @@ func TestDwarf5Ranges(t *testing.T) {
if err := d.AddSection(".debug_rnglists", rngLists); err != nil {
t.Fatal(err)
}
ret, err := d.dwarf5Ranges(nil, 0x5fbd, 0xc, [][2]uint64{})
u := &unit{
asize: 8,
vers: 5,
is64: true,
}
ret, err := d.dwarf5Ranges(u, nil, 0x5fbd, 0xc, [][2]uint64{})
if err != nil {
t.Fatalf("could not read rnglist: %v", err)
}
Expand Down
123 changes: 83 additions & 40 deletions src/debug/dwarf/entry.go
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,47 @@ func (b *buf) entry(cu *Entry, atab abbrevTable, ubase Offset, vers int) *Entry
Children: a.children,
Field: make([]Field, len(a.field)),
}

// If we are currently parsing the compilation unit,
// we can't evaluate Addrx or Strx until we've seen the
// relevant base entry.
type delayed struct {
idx int
off uint64
fmt format
}
var delay []delayed

resolveStrx := func(strBase, off uint64) string {
off += strBase
if uint64(int(off)) != off {
b.error("DW_FORM_strx offset out of range")
}

b1 := makeBuf(b.dwarf, b.format, "str_offsets", 0, b.dwarf.strOffsets)
b1.skip(int(off))
is64, _ := b.format.dwarf64()
if is64 {
off = b1.uint64()
} else {
off = uint64(b1.uint32())
}
if b1.err != nil {
b.err = b1.err
return ""
}
if uint64(int(off)) != off {
b.error("DW_FORM_strx indirect offset out of range")
}
b1 = makeBuf(b.dwarf, b.format, "str", 0, b.dwarf.str)
b1.skip(int(off))
val := b1.string()
if b1.err != nil {
b.err = b1.err
}
return val
}

for i := range e.Field {
e.Field[i].Attr = a.field[i].attr
e.Field[i].Class = a.field[i].class
Expand Down Expand Up @@ -467,10 +508,13 @@ func (b *buf) entry(cu *Entry, atab abbrevTable, ubase Offset, vers int) *Entry
var addrBase int64
if cu != nil {
addrBase, _ = cu.Val(AttrAddrBase).(int64)
} else if a.tag == TagCompileUnit {
delay = append(delay, delayed{i, off, formAddrx})
break
}

var err error
val, err = b.dwarf.debugAddr(uint64(addrBase), off)
val, err = b.dwarf.debugAddr(b.format, uint64(addrBase), off)
if err != nil {
if b.err == nil {
b.err = err
Expand Down Expand Up @@ -611,38 +655,16 @@ func (b *buf) entry(cu *Entry, atab abbrevTable, ubase Offset, vers int) *Entry
// 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 strBase int64
if cu != nil {
cuOff, ok := cu.Val(AttrStrOffsetsBase).(int64)
if ok {
off += uint64(cuOff)
}
strBase, _ = cu.Val(AttrStrOffsetsBase).(int64)
} else if a.tag == TagCompileUnit {
delay = append(delay, delayed{i, off, formStrx})
break
}

if uint64(int(off)) != off {
b.error("DW_FORM_strx offset out of range")
}
val = resolveStrx(uint64(strBase), off)

b1 := makeBuf(b.dwarf, b.format, "str_offsets", 0, b.dwarf.strOffsets)
b1.skip(int(off))
if is64 {
off = b1.uint64()
} else {
off = uint64(b1.uint32())
}
if b1.err != nil {
b.err = b1.err
return nil
}
if uint64(int(off)) != off {
b.error("DW_FORM_strx indirect offset out of range")
}
b1 = makeBuf(b.dwarf, b.format, "str", 0, b.dwarf.str)
b1.skip(int(off))
val = b1.string()
if b1.err != nil {
b.err = b1.err
return nil
}
case formStrpSup:
is64, known := b.format.dwarf64()
if !known {
Expand Down Expand Up @@ -689,11 +711,32 @@ func (b *buf) entry(cu *Entry, atab abbrevTable, ubase Offset, vers int) *Entry
case formRnglistx:
val = b.uint()
}

e.Field[i].Val = val
}
if b.err != nil {
return nil
}

for _, del := range delay {
switch del.fmt {
case formAddrx:
addrBase, _ := e.Val(AttrAddrBase).(int64)
val, err := b.dwarf.debugAddr(b.format, uint64(addrBase), del.off)
if err != nil {
b.err = err
return nil
}
e.Field[del.idx].Val = val
case formStrx:
strBase, _ := e.Val(AttrStrOffsetsBase).(int64)
e.Field[del.idx].Val = resolveStrx(uint64(strBase), del.off)
if b.err != nil {
return nil
}
}
}

return e
}

Expand Down Expand Up @@ -877,6 +920,7 @@ func (r *Reader) SeekPC(pc uint64) (*Entry, error) {
r.err = nil
r.lastChildren = false
r.unit = unit
r.cu = nil
u := &r.d.unit[unit]
r.b = makeBuf(r.d, u, "info", u.off, u.data)
e, err := r.Next()
Expand Down Expand Up @@ -946,7 +990,7 @@ func (d *Data) Ranges(e *Entry) ([][2]uint64, error) {
if err != nil {
return nil, err
}
return d.dwarf5Ranges(cu, base, ranges, ret)
return d.dwarf5Ranges(u, cu, base, ranges, ret)

case ClassRngList:
// TODO: support DW_FORM_rnglistx
Expand Down Expand Up @@ -1023,13 +1067,13 @@ func (d *Data) dwarf2Ranges(u *unit, base uint64, ranges int64, ret [][2]uint64)

// 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) {
func (d *Data) dwarf5Ranges(u *unit, 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 := makeBuf(d, u, "rnglists", 0, d.rngLists)
buf.skip(int(ranges))
for {
opcode := buf.uint8()
Expand All @@ -1043,7 +1087,7 @@ func (d *Data) dwarf5Ranges(cu *Entry, base uint64, ranges int64, ret [][2]uint6
case rleBaseAddressx:
baseIdx := buf.uint()
var err error
base, err = d.debugAddr(uint64(addrBase), baseIdx)
base, err = d.debugAddr(u, uint64(addrBase), baseIdx)
if err != nil {
return nil, err
}
Expand All @@ -1052,11 +1096,11 @@ func (d *Data) dwarf5Ranges(cu *Entry, base uint64, ranges int64, ret [][2]uint6
startIdx := buf.uint()
endIdx := buf.uint()

start, err := d.debugAddr(uint64(addrBase), startIdx)
start, err := d.debugAddr(u, uint64(addrBase), startIdx)
if err != nil {
return nil, err
}
end, err := d.debugAddr(uint64(addrBase), endIdx)
end, err := d.debugAddr(u, uint64(addrBase), endIdx)
if err != nil {
return nil, err
}
Expand All @@ -1065,7 +1109,7 @@ func (d *Data) dwarf5Ranges(cu *Entry, base uint64, ranges int64, ret [][2]uint6
case rleStartxLength:
startIdx := buf.uint()
len := buf.uint()
start, err := d.debugAddr(uint64(addrBase), startIdx)
start, err := d.debugAddr(u, uint64(addrBase), startIdx)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -1093,19 +1137,18 @@ func (d *Data) dwarf5Ranges(cu *Entry, base uint64, ranges int64, ret [][2]uint6
}

// 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
func (d *Data) debugAddr(format dataFormat, addrBase, idx uint64) (uint64, error) {
off := idx*uint64(format.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 := makeBuf(d, format, "addr", 0, d.addr)
b.skip(int(off))
val := b.addr()
if b.err != nil {
return 0, b.err
}

return val, nil
}
100 changes: 71 additions & 29 deletions src/debug/dwarf/entry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,20 @@ func TestReaderSeek(t *testing.T) {
{0x400611, nil},
}
testRanges(t, "testdata/line-gcc.elf", want)

want = []wantRange{
{0x401122, [][2]uint64{{0x401122, 0x401166}}},
{0x401165, [][2]uint64{{0x401122, 0x401166}}},
{0x401166, [][2]uint64{{0x401166, 0x401179}}},
}
testRanges(t, "testdata/line-gcc-dwarf5.elf", want)

want = []wantRange{
{0x401130, [][2]uint64{{0x401130, 0x40117e}}},
{0x40117d, [][2]uint64{{0x401130, 0x40117e}}},
{0x40117e, nil},
}
testRanges(t, "testdata/line-clang-dwarf5.elf", want)
}

func TestRangesSection(t *testing.T) {
Expand Down Expand Up @@ -97,44 +111,72 @@ func testRanges(t *testing.T, name string, want []wantRange) {
}

func TestReaderRanges(t *testing.T) {
d := elfData(t, "testdata/line-gcc.elf")

subprograms := []struct {
type subprograms []struct {
name string
ranges [][2]uint64
}
tests := []struct {
filename string
subprograms subprograms
}{
{"f1", [][2]uint64{{0x40059d, 0x4005e7}}},
{"main", [][2]uint64{{0x4005e7, 0x400601}}},
{"f2", [][2]uint64{{0x400601, 0x400611}}},
{
"testdata/line-gcc.elf",
subprograms{
{"f1", [][2]uint64{{0x40059d, 0x4005e7}}},
{"main", [][2]uint64{{0x4005e7, 0x400601}}},
{"f2", [][2]uint64{{0x400601, 0x400611}}},
},
},
{
"testdata/line-gcc-dwarf5.elf",
subprograms{
{"main", [][2]uint64{{0x401147, 0x401166}}},
{"f1", [][2]uint64{{0x401122, 0x401147}}},
{"f2", [][2]uint64{{0x401166, 0x401179}}},
},
},
{
"testdata/line-clang-dwarf5.elf",
subprograms{
{"main", [][2]uint64{{0x401130, 0x401144}}},
{"f1", [][2]uint64{{0x401150, 0x40117e}}},
{"f2", [][2]uint64{{0x401180, 0x401197}}},
},
},
}

r := d.Reader()
i := 0
for entry, err := r.Next(); entry != nil && err == nil; entry, err = r.Next() {
if entry.Tag != TagSubprogram {
continue
}
for _, test := range tests {
d := elfData(t, test.filename)
subprograms := test.subprograms

if i > len(subprograms) {
t.Fatalf("too many subprograms (expected at most %d)", i)
}
r := d.Reader()
i := 0
for entry, err := r.Next(); entry != nil && err == nil; entry, err = r.Next() {
if entry.Tag != TagSubprogram {
continue
}

if got := entry.Val(AttrName).(string); got != subprograms[i].name {
t.Errorf("subprogram %d name is %s, expected %s", i, got, subprograms[i].name)
}
ranges, err := d.Ranges(entry)
if err != nil {
t.Errorf("subprogram %d: %v", i, err)
continue
}
if !reflect.DeepEqual(ranges, subprograms[i].ranges) {
t.Errorf("subprogram %d ranges are %x, expected %x", i, ranges, subprograms[i].ranges)
if i > len(subprograms) {
t.Fatalf("%s: too many subprograms (expected at most %d)", test.filename, i)
}

if got := entry.Val(AttrName).(string); got != subprograms[i].name {
t.Errorf("%s: subprogram %d name is %s, expected %s", test.filename, i, got, subprograms[i].name)
}
ranges, err := d.Ranges(entry)
if err != nil {
t.Errorf("%s: subprogram %d: %v", test.filename, i, err)
continue
}
if !reflect.DeepEqual(ranges, subprograms[i].ranges) {
t.Errorf("%s: subprogram %d ranges are %x, expected %x", test.filename, i, ranges, subprograms[i].ranges)
}
i++
}
i++
}

if i < len(subprograms) {
t.Errorf("saw only %d subprograms, expected %d", i, len(subprograms))
if i < len(subprograms) {
t.Errorf("%s: saw only %d subprograms, expected %d", test.filename, i, len(subprograms))
}
}
}

Expand Down
Loading

0 comments on commit 828746e

Please sign in to comment.