Source file src/debug/dwarf/entry_test.go

     1  // Copyright 2009 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package dwarf_test
     6  
     7  import (
     8  	. "debug/dwarf"
     9  	"encoding/binary"
    10  	"path/filepath"
    11  	"reflect"
    12  	"testing"
    13  )
    14  
    15  func TestSplit(t *testing.T) {
    16  	// debug/dwarf doesn't (currently) support split DWARF, but
    17  	// the attributes that pointed to the split DWARF used to
    18  	// cause loading the DWARF data to fail entirely (issue
    19  	// #12592). Test that we can at least read the DWARF data.
    20  	d := elfData(t, "testdata/split.elf")
    21  	r := d.Reader()
    22  	e, err := r.Next()
    23  	if err != nil {
    24  		t.Fatal(err)
    25  	}
    26  	if e.Tag != TagCompileUnit {
    27  		t.Fatalf("bad tag: have %s, want %s", e.Tag, TagCompileUnit)
    28  	}
    29  	// Check that we were able to parse the unknown section offset
    30  	// field, even if we can't figure out its DWARF class.
    31  	const AttrGNUAddrBase Attr = 0x2133
    32  	f := e.AttrField(AttrGNUAddrBase)
    33  	if _, ok := f.Val.(int64); !ok {
    34  		t.Fatalf("bad attribute value type: have %T, want int64", f.Val)
    35  	}
    36  	if f.Class != ClassUnknown {
    37  		t.Fatalf("bad class: have %s, want %s", f.Class, ClassUnknown)
    38  	}
    39  }
    40  
    41  // wantRange maps from a PC to the ranges of the compilation unit
    42  // containing that PC.
    43  type wantRange struct {
    44  	pc     uint64
    45  	ranges [][2]uint64
    46  }
    47  
    48  func TestReaderSeek(t *testing.T) {
    49  	want := []wantRange{
    50  		{0x40059d, [][2]uint64{{0x40059d, 0x400601}}},
    51  		{0x400600, [][2]uint64{{0x40059d, 0x400601}}},
    52  		{0x400601, [][2]uint64{{0x400601, 0x400611}}},
    53  		{0x4005f0, [][2]uint64{{0x40059d, 0x400601}}}, // loop test
    54  		{0x10, nil},
    55  		{0x400611, nil},
    56  	}
    57  	testRanges(t, "testdata/line-gcc.elf", want)
    58  
    59  	want = []wantRange{
    60  		{0x401122, [][2]uint64{{0x401122, 0x401166}}},
    61  		{0x401165, [][2]uint64{{0x401122, 0x401166}}},
    62  		{0x401166, [][2]uint64{{0x401166, 0x401179}}},
    63  	}
    64  	testRanges(t, "testdata/line-gcc-dwarf5.elf", want)
    65  
    66  	want = []wantRange{
    67  		{0x401130, [][2]uint64{{0x401130, 0x40117e}}},
    68  		{0x40117d, [][2]uint64{{0x401130, 0x40117e}}},
    69  		{0x40117e, nil},
    70  	}
    71  	testRanges(t, "testdata/line-clang-dwarf5.elf", want)
    72  }
    73  
    74  func TestRangesSection(t *testing.T) {
    75  	want := []wantRange{
    76  		{0x400500, [][2]uint64{{0x400500, 0x400549}, {0x400400, 0x400408}}},
    77  		{0x400400, [][2]uint64{{0x400500, 0x400549}, {0x400400, 0x400408}}},
    78  		{0x400548, [][2]uint64{{0x400500, 0x400549}, {0x400400, 0x400408}}},
    79  		{0x400407, [][2]uint64{{0x400500, 0x400549}, {0x400400, 0x400408}}},
    80  		{0x400408, nil},
    81  		{0x400449, nil},
    82  		{0x4003ff, nil},
    83  	}
    84  	testRanges(t, "testdata/ranges.elf", want)
    85  }
    86  
    87  func TestRangesRnglistx(t *testing.T) {
    88  	want := []wantRange{
    89  		{0x401000, [][2]uint64{{0x401020, 0x40102c}, {0x401000, 0x40101d}}},
    90  		{0x40101c, [][2]uint64{{0x401020, 0x40102c}, {0x401000, 0x40101d}}},
    91  		{0x40101d, nil},
    92  		{0x40101f, nil},
    93  		{0x401020, [][2]uint64{{0x401020, 0x40102c}, {0x401000, 0x40101d}}},
    94  		{0x40102b, [][2]uint64{{0x401020, 0x40102c}, {0x401000, 0x40101d}}},
    95  		{0x40102c, nil},
    96  	}
    97  	testRanges(t, "testdata/rnglistx.elf", want)
    98  }
    99  
   100  func testRanges(t *testing.T, name string, want []wantRange) {
   101  	d := elfData(t, name)
   102  	r := d.Reader()
   103  	for _, w := range want {
   104  		entry, err := r.SeekPC(w.pc)
   105  		if err != nil {
   106  			if w.ranges != nil {
   107  				t.Errorf("%s: missing Entry for %#x", name, w.pc)
   108  			}
   109  			if err != ErrUnknownPC {
   110  				t.Errorf("%s: expected ErrUnknownPC for %#x, got %v", name, w.pc, err)
   111  			}
   112  			continue
   113  		}
   114  
   115  		ranges, err := d.Ranges(entry)
   116  		if err != nil {
   117  			t.Errorf("%s: %v", name, err)
   118  			continue
   119  		}
   120  		if !reflect.DeepEqual(ranges, w.ranges) {
   121  			t.Errorf("%s: for %#x got %x, expected %x", name, w.pc, ranges, w.ranges)
   122  		}
   123  	}
   124  }
   125  
   126  func TestReaderRanges(t *testing.T) {
   127  	type subprograms []struct {
   128  		name   string
   129  		ranges [][2]uint64
   130  	}
   131  	tests := []struct {
   132  		filename    string
   133  		subprograms subprograms
   134  	}{
   135  		{
   136  			"testdata/line-gcc.elf",
   137  			subprograms{
   138  				{"f1", [][2]uint64{{0x40059d, 0x4005e7}}},
   139  				{"main", [][2]uint64{{0x4005e7, 0x400601}}},
   140  				{"f2", [][2]uint64{{0x400601, 0x400611}}},
   141  			},
   142  		},
   143  		{
   144  			"testdata/line-gcc-dwarf5.elf",
   145  			subprograms{
   146  				{"main", [][2]uint64{{0x401147, 0x401166}}},
   147  				{"f1", [][2]uint64{{0x401122, 0x401147}}},
   148  				{"f2", [][2]uint64{{0x401166, 0x401179}}},
   149  			},
   150  		},
   151  		{
   152  			"testdata/line-clang-dwarf5.elf",
   153  			subprograms{
   154  				{"main", [][2]uint64{{0x401130, 0x401144}}},
   155  				{"f1", [][2]uint64{{0x401150, 0x40117e}}},
   156  				{"f2", [][2]uint64{{0x401180, 0x401197}}},
   157  			},
   158  		},
   159  	}
   160  
   161  	for _, test := range tests {
   162  		d := elfData(t, test.filename)
   163  		subprograms := test.subprograms
   164  
   165  		r := d.Reader()
   166  		i := 0
   167  		for entry, err := r.Next(); entry != nil && err == nil; entry, err = r.Next() {
   168  			if entry.Tag != TagSubprogram {
   169  				continue
   170  			}
   171  
   172  			if i > len(subprograms) {
   173  				t.Fatalf("%s: too many subprograms (expected at most %d)", test.filename, i)
   174  			}
   175  
   176  			if got := entry.Val(AttrName).(string); got != subprograms[i].name {
   177  				t.Errorf("%s: subprogram %d name is %s, expected %s", test.filename, i, got, subprograms[i].name)
   178  			}
   179  			ranges, err := d.Ranges(entry)
   180  			if err != nil {
   181  				t.Errorf("%s: subprogram %d: %v", test.filename, i, err)
   182  				continue
   183  			}
   184  			if !reflect.DeepEqual(ranges, subprograms[i].ranges) {
   185  				t.Errorf("%s: subprogram %d ranges are %x, expected %x", test.filename, i, ranges, subprograms[i].ranges)
   186  			}
   187  			i++
   188  		}
   189  
   190  		if i < len(subprograms) {
   191  			t.Errorf("%s: saw only %d subprograms, expected %d", test.filename, i, len(subprograms))
   192  		}
   193  	}
   194  }
   195  
   196  func Test64Bit(t *testing.T) {
   197  	// I don't know how to generate a 64-bit DWARF debug
   198  	// compilation unit except by using XCOFF, so this is
   199  	// hand-written.
   200  	tests := []struct {
   201  		name      string
   202  		info      []byte
   203  		addrSize  int
   204  		byteOrder binary.ByteOrder
   205  	}{
   206  		{
   207  			"32-bit little",
   208  			[]byte{0x30, 0, 0, 0, // comp unit length
   209  				4, 0, // DWARF version 4
   210  				0, 0, 0, 0, // abbrev offset
   211  				8, // address size
   212  				0,
   213  				0, 0, 0, 0, 0, 0, 0, 0,
   214  				0, 0, 0, 0, 0, 0, 0, 0,
   215  				0, 0, 0, 0, 0, 0, 0, 0,
   216  				0, 0, 0, 0, 0, 0, 0, 0,
   217  				0, 0, 0, 0, 0, 0, 0, 0,
   218  			},
   219  			8, binary.LittleEndian,
   220  		},
   221  		{
   222  			"64-bit little",
   223  			[]byte{0xff, 0xff, 0xff, 0xff, // 64-bit DWARF
   224  				0x30, 0, 0, 0, 0, 0, 0, 0, // comp unit length
   225  				4, 0, // DWARF version 4
   226  				0, 0, 0, 0, 0, 0, 0, 0, // abbrev offset
   227  				8, // address size
   228  				0, 0, 0, 0, 0,
   229  				0, 0, 0, 0, 0, 0, 0, 0,
   230  				0, 0, 0, 0, 0, 0, 0, 0,
   231  				0, 0, 0, 0, 0, 0, 0, 0,
   232  				0, 0, 0, 0, 0, 0, 0, 0,
   233  			},
   234  			8, binary.LittleEndian,
   235  		},
   236  		{
   237  			"64-bit big",
   238  			[]byte{0xff, 0xff, 0xff, 0xff, // 64-bit DWARF
   239  				0, 0, 0, 0, 0, 0, 0, 0x30, // comp unit length
   240  				0, 4, // DWARF version 4
   241  				0, 0, 0, 0, 0, 0, 0, 0, // abbrev offset
   242  				8, // address size
   243  				0, 0, 0, 0, 0,
   244  				0, 0, 0, 0, 0, 0, 0, 0,
   245  				0, 0, 0, 0, 0, 0, 0, 0,
   246  				0, 0, 0, 0, 0, 0, 0, 0,
   247  				0, 0, 0, 0, 0, 0, 0, 0,
   248  			},
   249  			8, binary.BigEndian,
   250  		},
   251  	}
   252  
   253  	for _, test := range tests {
   254  		data, err := New(nil, nil, nil, test.info, nil, nil, nil, nil)
   255  		if err != nil {
   256  			t.Errorf("%s: %v", test.name, err)
   257  		}
   258  
   259  		r := data.Reader()
   260  		if r.AddressSize() != test.addrSize {
   261  			t.Errorf("%s: got address size %d, want %d", test.name, r.AddressSize(), test.addrSize)
   262  		}
   263  		if r.ByteOrder() != test.byteOrder {
   264  			t.Errorf("%s: got byte order %s, want %s", test.name, r.ByteOrder(), test.byteOrder)
   265  		}
   266  	}
   267  }
   268  
   269  func TestUnitIteration(t *testing.T) {
   270  	// Iterate over all ELF test files we have and ensure that
   271  	// we get the same set of compilation units skipping (method 0)
   272  	// and not skipping (method 1) CU children.
   273  	files, err := filepath.Glob(filepath.Join("testdata", "*.elf"))
   274  	if err != nil {
   275  		t.Fatal(err)
   276  	}
   277  	for _, file := range files {
   278  		t.Run(file, func(t *testing.T) {
   279  			d := elfData(t, file)
   280  			var units [2][]any
   281  			for method := range units {
   282  				for r := d.Reader(); ; {
   283  					ent, err := r.Next()
   284  					if err != nil {
   285  						t.Fatal(err)
   286  					}
   287  					if ent == nil {
   288  						break
   289  					}
   290  					if ent.Tag == TagCompileUnit {
   291  						units[method] = append(units[method], ent.Val(AttrName))
   292  					}
   293  					if method == 0 {
   294  						if ent.Tag != TagCompileUnit {
   295  							t.Fatalf("found unexpected tag %v on top level", ent.Tag)
   296  						}
   297  						r.SkipChildren()
   298  					}
   299  				}
   300  			}
   301  			t.Logf("skipping CUs:     %v", units[0])
   302  			t.Logf("not-skipping CUs: %v", units[1])
   303  			if !reflect.DeepEqual(units[0], units[1]) {
   304  				t.Fatal("set of CUs differ")
   305  			}
   306  		})
   307  	}
   308  }
   309  

View as plain text