Austin Clements | 2cbd707 | 2015-09-13 12:49:05 -0400 | [diff] [blame] | 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" |
Austin Clements | bc40294 | 2019-08-21 15:05:26 -0400 | [diff] [blame] | 9 | "encoding/binary" |
Dmitry Vyukov | f92c640 | 2020-09-21 12:19:47 +0200 | [diff] [blame] | 10 | "path/filepath" |
Ian Lance Taylor | d1b8871 | 2016-03-16 14:15:54 -0700 | [diff] [blame] | 11 | "reflect" |
Austin Clements | 2cbd707 | 2015-09-13 12:49:05 -0400 | [diff] [blame] | 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 | } |
Ian Lance Taylor | d1b8871 | 2016-03-16 14:15:54 -0700 | [diff] [blame] | 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) |
Ian Lance Taylor | 828746e | 2020-12-11 15:15:43 -0800 | [diff] [blame] | 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) |
Ian Lance Taylor | d1b8871 | 2016-03-16 14:15:54 -0700 | [diff] [blame] | 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 | |
Ian Lance Taylor | a826f7d | 2021-03-16 13:31:20 -0700 | [diff] [blame] | 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 | |
Ian Lance Taylor | d1b8871 | 2016-03-16 14:15:54 -0700 | [diff] [blame] | 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) { |
Ian Lance Taylor | 828746e | 2020-12-11 15:15:43 -0800 | [diff] [blame] | 127 | type subprograms []struct { |
Ian Lance Taylor | d1b8871 | 2016-03-16 14:15:54 -0700 | [diff] [blame] | 128 | name string |
| 129 | ranges [][2]uint64 |
Ian Lance Taylor | 828746e | 2020-12-11 15:15:43 -0800 | [diff] [blame] | 130 | } |
| 131 | tests := []struct { |
| 132 | filename string |
| 133 | subprograms subprograms |
Ian Lance Taylor | d1b8871 | 2016-03-16 14:15:54 -0700 | [diff] [blame] | 134 | }{ |
Ian Lance Taylor | 828746e | 2020-12-11 15:15:43 -0800 | [diff] [blame] | 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 | }, |
Ian Lance Taylor | d1b8871 | 2016-03-16 14:15:54 -0700 | [diff] [blame] | 159 | } |
| 160 | |
Ian Lance Taylor | 828746e | 2020-12-11 15:15:43 -0800 | [diff] [blame] | 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++ |
Ian Lance Taylor | d1b8871 | 2016-03-16 14:15:54 -0700 | [diff] [blame] | 188 | } |
| 189 | |
Ian Lance Taylor | 828746e | 2020-12-11 15:15:43 -0800 | [diff] [blame] | 190 | if i < len(subprograms) { |
| 191 | t.Errorf("%s: saw only %d subprograms, expected %d", test.filename, i, len(subprograms)) |
Ian Lance Taylor | d1b8871 | 2016-03-16 14:15:54 -0700 | [diff] [blame] | 192 | } |
Ian Lance Taylor | d1b8871 | 2016-03-16 14:15:54 -0700 | [diff] [blame] | 193 | } |
| 194 | } |
Ian Lance Taylor | 151c66b | 2017-10-16 11:11:34 -0700 | [diff] [blame] | 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 { |
Austin Clements | bc40294 | 2019-08-21 15:05:26 -0400 | [diff] [blame] | 201 | name string |
| 202 | info []byte |
| 203 | addrSize int |
| 204 | byteOrder binary.ByteOrder |
Ian Lance Taylor | 151c66b | 2017-10-16 11:11:34 -0700 | [diff] [blame] | 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 | }, |
Austin Clements | bc40294 | 2019-08-21 15:05:26 -0400 | [diff] [blame] | 219 | 8, binary.LittleEndian, |
Ian Lance Taylor | 151c66b | 2017-10-16 11:11:34 -0700 | [diff] [blame] | 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 | }, |
Austin Clements | bc40294 | 2019-08-21 15:05:26 -0400 | [diff] [blame] | 234 | 8, binary.LittleEndian, |
Ian Lance Taylor | 151c66b | 2017-10-16 11:11:34 -0700 | [diff] [blame] | 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 | }, |
Austin Clements | bc40294 | 2019-08-21 15:05:26 -0400 | [diff] [blame] | 249 | 8, binary.BigEndian, |
Ian Lance Taylor | 151c66b | 2017-10-16 11:11:34 -0700 | [diff] [blame] | 250 | }, |
| 251 | } |
| 252 | |
| 253 | for _, test := range tests { |
Austin Clements | bc40294 | 2019-08-21 15:05:26 -0400 | [diff] [blame] | 254 | data, err := New(nil, nil, nil, test.info, nil, nil, nil, nil) |
Ian Lance Taylor | 151c66b | 2017-10-16 11:11:34 -0700 | [diff] [blame] | 255 | if err != nil { |
| 256 | t.Errorf("%s: %v", test.name, err) |
| 257 | } |
Austin Clements | bc40294 | 2019-08-21 15:05:26 -0400 | [diff] [blame] | 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 | } |
Ian Lance Taylor | 151c66b | 2017-10-16 11:11:34 -0700 | [diff] [blame] | 266 | } |
| 267 | } |
Dmitry Vyukov | f92c640 | 2020-09-21 12:19:47 +0200 | [diff] [blame] | 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) |
Russ Cox | 2580d0e | 2021-12-01 12:15:45 -0500 | [diff] [blame^] | 280 | var units [2][]any |
Dmitry Vyukov | f92c640 | 2020-09-21 12:19:47 +0200 | [diff] [blame] | 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 | } |