212 lines
5.5 KiB
Go
212 lines
5.5 KiB
Go
// Copyright 2009 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_test
|
|
|
|
import (
|
|
. "debug/dwarf"
|
|
"encoding/binary"
|
|
"reflect"
|
|
"testing"
|
|
)
|
|
|
|
func TestSplit(t *testing.T) {
|
|
// debug/dwarf doesn't (currently) support split DWARF, but
|
|
// the attributes that pointed to the split DWARF used to
|
|
// cause loading the DWARF data to fail entirely (issue
|
|
// #12592). Test that we can at least read the DWARF data.
|
|
d := elfData(t, "testdata/split.elf")
|
|
r := d.Reader()
|
|
e, err := r.Next()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if e.Tag != TagCompileUnit {
|
|
t.Fatalf("bad tag: have %s, want %s", e.Tag, TagCompileUnit)
|
|
}
|
|
// Check that we were able to parse the unknown section offset
|
|
// field, even if we can't figure out its DWARF class.
|
|
const AttrGNUAddrBase Attr = 0x2133
|
|
f := e.AttrField(AttrGNUAddrBase)
|
|
if _, ok := f.Val.(int64); !ok {
|
|
t.Fatalf("bad attribute value type: have %T, want int64", f.Val)
|
|
}
|
|
if f.Class != ClassUnknown {
|
|
t.Fatalf("bad class: have %s, want %s", f.Class, ClassUnknown)
|
|
}
|
|
}
|
|
|
|
// wantRange maps from a PC to the ranges of the compilation unit
|
|
// containing that PC.
|
|
type wantRange struct {
|
|
pc uint64
|
|
ranges [][2]uint64
|
|
}
|
|
|
|
func TestReaderSeek(t *testing.T) {
|
|
want := []wantRange{
|
|
{0x40059d, [][2]uint64{{0x40059d, 0x400601}}},
|
|
{0x400600, [][2]uint64{{0x40059d, 0x400601}}},
|
|
{0x400601, [][2]uint64{{0x400601, 0x400611}}},
|
|
{0x4005f0, [][2]uint64{{0x40059d, 0x400601}}}, // loop test
|
|
{0x10, nil},
|
|
{0x400611, nil},
|
|
}
|
|
testRanges(t, "testdata/line-gcc.elf", want)
|
|
}
|
|
|
|
func TestRangesSection(t *testing.T) {
|
|
want := []wantRange{
|
|
{0x400500, [][2]uint64{{0x400500, 0x400549}, {0x400400, 0x400408}}},
|
|
{0x400400, [][2]uint64{{0x400500, 0x400549}, {0x400400, 0x400408}}},
|
|
{0x400548, [][2]uint64{{0x400500, 0x400549}, {0x400400, 0x400408}}},
|
|
{0x400407, [][2]uint64{{0x400500, 0x400549}, {0x400400, 0x400408}}},
|
|
{0x400408, nil},
|
|
{0x400449, nil},
|
|
{0x4003ff, nil},
|
|
}
|
|
testRanges(t, "testdata/ranges.elf", want)
|
|
}
|
|
|
|
func testRanges(t *testing.T, name string, want []wantRange) {
|
|
d := elfData(t, name)
|
|
r := d.Reader()
|
|
for _, w := range want {
|
|
entry, err := r.SeekPC(w.pc)
|
|
if err != nil {
|
|
if w.ranges != nil {
|
|
t.Errorf("%s: missing Entry for %#x", name, w.pc)
|
|
}
|
|
if err != ErrUnknownPC {
|
|
t.Errorf("%s: expected ErrUnknownPC for %#x, got %v", name, w.pc, err)
|
|
}
|
|
continue
|
|
}
|
|
|
|
ranges, err := d.Ranges(entry)
|
|
if err != nil {
|
|
t.Errorf("%s: %v", name, err)
|
|
continue
|
|
}
|
|
if !reflect.DeepEqual(ranges, w.ranges) {
|
|
t.Errorf("%s: for %#x got %x, expected %x", name, w.pc, ranges, w.ranges)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestReaderRanges(t *testing.T) {
|
|
d := elfData(t, "testdata/line-gcc.elf")
|
|
|
|
subprograms := []struct {
|
|
name string
|
|
ranges [][2]uint64
|
|
}{
|
|
{"f1", [][2]uint64{{0x40059d, 0x4005e7}}},
|
|
{"main", [][2]uint64{{0x4005e7, 0x400601}}},
|
|
{"f2", [][2]uint64{{0x400601, 0x400611}}},
|
|
}
|
|
|
|
r := d.Reader()
|
|
i := 0
|
|
for entry, err := r.Next(); entry != nil && err == nil; entry, err = r.Next() {
|
|
if entry.Tag != TagSubprogram {
|
|
continue
|
|
}
|
|
|
|
if i > len(subprograms) {
|
|
t.Fatalf("too many subprograms (expected at most %d)", i)
|
|
}
|
|
|
|
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)
|
|
}
|
|
i++
|
|
}
|
|
|
|
if i < len(subprograms) {
|
|
t.Errorf("saw only %d subprograms, expected %d", i, len(subprograms))
|
|
}
|
|
}
|
|
|
|
func Test64Bit(t *testing.T) {
|
|
// I don't know how to generate a 64-bit DWARF debug
|
|
// compilation unit except by using XCOFF, so this is
|
|
// hand-written.
|
|
tests := []struct {
|
|
name string
|
|
info []byte
|
|
addrSize int
|
|
byteOrder binary.ByteOrder
|
|
}{
|
|
{
|
|
"32-bit little",
|
|
[]byte{0x30, 0, 0, 0, // comp unit length
|
|
4, 0, // DWARF version 4
|
|
0, 0, 0, 0, // abbrev offset
|
|
8, // address size
|
|
0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
},
|
|
8, binary.LittleEndian,
|
|
},
|
|
{
|
|
"64-bit little",
|
|
[]byte{0xff, 0xff, 0xff, 0xff, // 64-bit DWARF
|
|
0x30, 0, 0, 0, 0, 0, 0, 0, // comp unit length
|
|
4, 0, // DWARF version 4
|
|
0, 0, 0, 0, 0, 0, 0, 0, // abbrev offset
|
|
8, // address size
|
|
0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
},
|
|
8, binary.LittleEndian,
|
|
},
|
|
{
|
|
"64-bit big",
|
|
[]byte{0xff, 0xff, 0xff, 0xff, // 64-bit DWARF
|
|
0, 0, 0, 0, 0, 0, 0, 0x30, // comp unit length
|
|
0, 4, // DWARF version 4
|
|
0, 0, 0, 0, 0, 0, 0, 0, // abbrev offset
|
|
8, // address size
|
|
0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
},
|
|
8, binary.BigEndian,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
data, err := New(nil, nil, nil, test.info, nil, nil, nil, nil)
|
|
if err != nil {
|
|
t.Errorf("%s: %v", test.name, err)
|
|
}
|
|
|
|
r := data.Reader()
|
|
if r.AddressSize() != test.addrSize {
|
|
t.Errorf("%s: got address size %d, want %d", test.name, r.AddressSize(), test.addrSize)
|
|
}
|
|
if r.ByteOrder() != test.byteOrder {
|
|
t.Errorf("%s: got byte order %s, want %s", test.name, r.ByteOrder(), test.byteOrder)
|
|
}
|
|
}
|
|
}
|