blob: 8c6ca7259ea6060020820f7f19f44e0502f01a41 [file] [log] [blame]
Austin Clements2cbd7072015-09-13 12:49:05 -04001// 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
5package dwarf_test
6
7import (
8 . "debug/dwarf"
Austin Clementsbc402942019-08-21 15:05:26 -04009 "encoding/binary"
Dmitry Vyukovf92c6402020-09-21 12:19:47 +020010 "path/filepath"
Ian Lance Taylord1b88712016-03-16 14:15:54 -070011 "reflect"
Austin Clements2cbd7072015-09-13 12:49:05 -040012 "testing"
13)
14
15func 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 Taylord1b88712016-03-16 14:15:54 -070040
41// wantRange maps from a PC to the ranges of the compilation unit
42// containing that PC.
43type wantRange struct {
44 pc uint64
45 ranges [][2]uint64
46}
47
48func 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 Taylor828746e2020-12-11 15:15:43 -080058
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 Taylord1b88712016-03-16 14:15:54 -070072}
73
74func 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 Taylora826f7d2021-03-16 13:31:20 -070087func 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 Taylord1b88712016-03-16 14:15:54 -0700100func 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
126func TestReaderRanges(t *testing.T) {
Ian Lance Taylor828746e2020-12-11 15:15:43 -0800127 type subprograms []struct {
Ian Lance Taylord1b88712016-03-16 14:15:54 -0700128 name string
129 ranges [][2]uint64
Ian Lance Taylor828746e2020-12-11 15:15:43 -0800130 }
131 tests := []struct {
132 filename string
133 subprograms subprograms
Ian Lance Taylord1b88712016-03-16 14:15:54 -0700134 }{
Ian Lance Taylor828746e2020-12-11 15:15:43 -0800135 {
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 Taylord1b88712016-03-16 14:15:54 -0700159 }
160
Ian Lance Taylor828746e2020-12-11 15:15:43 -0800161 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 Taylord1b88712016-03-16 14:15:54 -0700188 }
189
Ian Lance Taylor828746e2020-12-11 15:15:43 -0800190 if i < len(subprograms) {
191 t.Errorf("%s: saw only %d subprograms, expected %d", test.filename, i, len(subprograms))
Ian Lance Taylord1b88712016-03-16 14:15:54 -0700192 }
Ian Lance Taylord1b88712016-03-16 14:15:54 -0700193 }
194}
Ian Lance Taylor151c66b2017-10-16 11:11:34 -0700195
196func 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 Clementsbc402942019-08-21 15:05:26 -0400201 name string
202 info []byte
203 addrSize int
204 byteOrder binary.ByteOrder
Ian Lance Taylor151c66b2017-10-16 11:11:34 -0700205 }{
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 Clementsbc402942019-08-21 15:05:26 -0400219 8, binary.LittleEndian,
Ian Lance Taylor151c66b2017-10-16 11:11:34 -0700220 },
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 Clementsbc402942019-08-21 15:05:26 -0400234 8, binary.LittleEndian,
Ian Lance Taylor151c66b2017-10-16 11:11:34 -0700235 },
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 Clementsbc402942019-08-21 15:05:26 -0400249 8, binary.BigEndian,
Ian Lance Taylor151c66b2017-10-16 11:11:34 -0700250 },
251 }
252
253 for _, test := range tests {
Austin Clementsbc402942019-08-21 15:05:26 -0400254 data, err := New(nil, nil, nil, test.info, nil, nil, nil, nil)
Ian Lance Taylor151c66b2017-10-16 11:11:34 -0700255 if err != nil {
256 t.Errorf("%s: %v", test.name, err)
257 }
Austin Clementsbc402942019-08-21 15:05:26 -0400258
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 Taylor151c66b2017-10-16 11:11:34 -0700266 }
267}
Dmitry Vyukovf92c6402020-09-21 12:19:47 +0200268
269func 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 Cox2580d0e2021-12-01 12:15:45 -0500280 var units [2][]any
Dmitry Vyukovf92c6402020-09-21 12:19:47 +0200281 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}