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
5package dwarf_test
6
7import (
8	. "debug/dwarf"
9	"debug/elf"
10	"debug/macho"
11	"debug/pe"
12	"fmt"
13	"strconv"
14	"testing"
15)
16
17var typedefTests = map[string]string{
18	"t_ptr_volatile_int":                    "*volatile int",
19	"t_ptr_const_char":                      "*const char",
20	"t_long":                                "long int",
21	"t_ushort":                              "short unsigned int",
22	"t_func_int_of_float_double":            "func(float, double) int",
23	"t_ptr_func_int_of_float_double":        "*func(float, double) int",
24	"t_ptr_func_int_of_float_complex":       "*func(complex float) int",
25	"t_ptr_func_int_of_double_complex":      "*func(complex double) int",
26	"t_ptr_func_int_of_long_double_complex": "*func(complex long double) int",
27	"t_func_ptr_int_of_char_schar_uchar":    "func(char, signed char, unsigned char) *int",
28	"t_func_void_of_char":                   "func(char) void",
29	"t_func_void_of_void":                   "func() void",
30	"t_func_void_of_ptr_char_dots":          "func(*char, ...) void",
31	"t_my_struct":                           "struct my_struct {vi volatile int@0; x char@4 : 1@7; y int@4 : 4@27; z [0]int@8; array [40]long long int@8; zz [0]int@328}",
32	"t_my_struct1":                          "struct my_struct1 {zz [1]int@0}",
33	"t_my_union":                            "union my_union {vi volatile int@0; x char@0 : 1@7; y int@0 : 4@28; array [40]long long int@0}",
34	"t_my_enum":                             "enum my_enum {e1=1; e2=2; e3=-5; e4=1000000000000000}",
35	"t_my_list":                             "struct list {val short int@0; next *t_my_list@8}",
36	"t_my_tree":                             "struct tree {left *struct tree@0; right *struct tree@8; val long long unsigned int@16}",
37}
38
39// As Apple converts gcc to a clang-based front end
40// they keep breaking the DWARF output. This map lists the
41// conversion from real answer to Apple answer.
42var machoBug = map[string]string{
43	"func(*char, ...) void":                                 "func(*char) void",
44	"enum my_enum {e1=1; e2=2; e3=-5; e4=1000000000000000}": "enum my_enum {e1=1; e2=2; e3=-5; e4=-1530494976}",
45}
46
47func elfData(t *testing.T, name string) *Data {
48	f, err := elf.Open(name)
49	if err != nil {
50		t.Fatal(err)
51	}
52
53	d, err := f.DWARF()
54	if err != nil {
55		t.Fatal(err)
56	}
57	return d
58}
59
60func machoData(t *testing.T, name string) *Data {
61	f, err := macho.Open(name)
62	if err != nil {
63		t.Fatal(err)
64	}
65
66	d, err := f.DWARF()
67	if err != nil {
68		t.Fatal(err)
69	}
70	return d
71}
72
73func peData(t *testing.T, name string) *Data {
74	f, err := pe.Open(name)
75	if err != nil {
76		t.Fatal(err)
77	}
78
79	d, err := f.DWARF()
80	if err != nil {
81		t.Fatal(err)
82	}
83	return d
84}
85
86func TestTypedefsELF(t *testing.T) {
87	testTypedefs(t, elfData(t, "testdata/typedef.elf"), "elf", typedefTests)
88}
89
90func TestTypedefsMachO(t *testing.T) {
91	testTypedefs(t, machoData(t, "testdata/typedef.macho"), "macho", typedefTests)
92}
93
94func TestTypedefsELFDwarf4(t *testing.T) {
95	testTypedefs(t, elfData(t, "testdata/typedef.elf4"), "elf", typedefTests)
96}
97
98func testTypedefs(t *testing.T, d *Data, kind string, testcases map[string]string) {
99	r := d.Reader()
100	seen := make(map[string]bool)
101	for {
102		e, err := r.Next()
103		if err != nil {
104			t.Fatal("r.Next:", err)
105		}
106		if e == nil {
107			break
108		}
109		if e.Tag == TagTypedef {
110			typ, err := d.Type(e.Offset)
111			if err != nil {
112				t.Fatal("d.Type:", err)
113			}
114			t1 := typ.(*TypedefType)
115			var typstr string
116			if ts, ok := t1.Type.(*StructType); ok {
117				typstr = ts.Defn()
118			} else {
119				typstr = t1.Type.String()
120			}
121
122			if want, ok := testcases[t1.Name]; ok {
123				if seen[t1.Name] {
124					t.Errorf("multiple definitions for %s", t1.Name)
125				}
126				seen[t1.Name] = true
127				if typstr != want && (kind != "macho" || typstr != machoBug[want]) {
128					t.Errorf("%s:\n\thave %s\n\twant %s", t1.Name, typstr, want)
129				}
130			}
131		}
132		if e.Tag != TagCompileUnit {
133			r.SkipChildren()
134		}
135	}
136
137	for k := range testcases {
138		if !seen[k] {
139			t.Errorf("missing %s", k)
140		}
141	}
142}
143
144func TestTypedefCycle(t *testing.T) {
145	// See issue #13039: reading a typedef cycle starting from a
146	// different place than the size needed to be computed from
147	// used to crash.
148	//
149	// cycle.elf built with GCC 4.8.4:
150	//    gcc -g -c -o cycle.elf cycle.c
151	d := elfData(t, "testdata/cycle.elf")
152	r := d.Reader()
153	offsets := []Offset{}
154	for {
155		e, err := r.Next()
156		if err != nil {
157			t.Fatal("r.Next:", err)
158		}
159		if e == nil {
160			break
161		}
162		switch e.Tag {
163		case TagBaseType, TagTypedef, TagPointerType, TagStructType:
164			offsets = append(offsets, e.Offset)
165		}
166	}
167
168	// Parse each type with a fresh type cache.
169	for _, offset := range offsets {
170		d := elfData(t, "testdata/cycle.elf")
171		_, err := d.Type(offset)
172		if err != nil {
173			t.Fatalf("d.Type(0x%x): %s", offset, err)
174		}
175	}
176}
177
178var unsupportedTypeTests = []string{
179	// varname:typename:string:size
180	"culprit::(unsupported type ReferenceType):8",
181	"pdm::(unsupported type PtrToMemberType):-1",
182}
183
184func TestUnsupportedTypes(t *testing.T) {
185	// Issue 29601:
186	// When reading DWARF from C++ load modules, we can encounter
187	// oddball type DIEs. These will be returned as "UnsupportedType"
188	// objects; check to make sure this works properly.
189	d := elfData(t, "testdata/cppunsuptypes.elf")
190	r := d.Reader()
191	seen := make(map[string]bool)
192	for {
193		e, err := r.Next()
194		if err != nil {
195			t.Fatal("r.Next:", err)
196		}
197		if e == nil {
198			break
199		}
200		if e.Tag == TagVariable {
201			vname, _ := e.Val(AttrName).(string)
202			tAttr := e.Val(AttrType)
203			typOff, ok := tAttr.(Offset)
204			if !ok {
205				t.Errorf("variable at offset %v has no type", e.Offset)
206				continue
207			}
208			typ, err := d.Type(typOff)
209			if err != nil {
210				t.Errorf("err in type decode: %v\n", err)
211				continue
212			}
213			unsup, isok := typ.(*UnsupportedType)
214			if !isok {
215				continue
216			}
217			tag := vname + ":" + unsup.Name + ":" + unsup.String() +
218				":" + strconv.FormatInt(unsup.Size(), 10)
219			seen[tag] = true
220		}
221	}
222	dumpseen := false
223	for _, v := range unsupportedTypeTests {
224		if !seen[v] {
225			t.Errorf("missing %s", v)
226			dumpseen = true
227		}
228	}
229	if dumpseen {
230		for k := range seen {
231			fmt.Printf("seen: %s\n", k)
232		}
233	}
234}
235
236var expectedBitOffsets1 = map[string]string{
237	"x": "S:1 DBO:32",
238	"y": "S:4 DBO:33",
239}
240
241var expectedBitOffsets2 = map[string]string{
242	"x": "S:1 BO:7",
243	"y": "S:4 BO:27",
244}
245
246func TestBitOffsetsELF(t *testing.T) {
247	f := "testdata/typedef.elf"
248	testBitOffsets(t, elfData(t, f), f, expectedBitOffsets2)
249}
250
251func TestBitOffsetsMachO(t *testing.T) {
252	f := "testdata/typedef.macho"
253	testBitOffsets(t, machoData(t, f), f, expectedBitOffsets2)
254}
255
256func TestBitOffsetsMachO4(t *testing.T) {
257	f := "testdata/typedef.macho4"
258	testBitOffsets(t, machoData(t, f), f, expectedBitOffsets1)
259}
260
261func TestBitOffsetsELFDwarf4(t *testing.T) {
262	f := "testdata/typedef.elf4"
263	testBitOffsets(t, elfData(t, f), f, expectedBitOffsets1)
264}
265
266func TestBitOffsetsELFDwarf5(t *testing.T) {
267	f := "testdata/typedef.elf5"
268	testBitOffsets(t, elfData(t, f), f, expectedBitOffsets1)
269}
270
271func testBitOffsets(t *testing.T, d *Data, tag string, expectedBitOffsets map[string]string) {
272	r := d.Reader()
273	for {
274		e, err := r.Next()
275		if err != nil {
276			t.Fatal("r.Next:", err)
277		}
278		if e == nil {
279			break
280		}
281
282		if e.Tag == TagStructType {
283			typ, err := d.Type(e.Offset)
284			if err != nil {
285				t.Fatal("d.Type:", err)
286			}
287
288			t1 := typ.(*StructType)
289
290			bitInfoDump := func(f *StructField) string {
291				res := fmt.Sprintf("S:%d", f.BitSize)
292				if f.BitOffset != 0 {
293					res += fmt.Sprintf(" BO:%d", f.BitOffset)
294				}
295				if f.DataBitOffset != 0 {
296					res += fmt.Sprintf(" DBO:%d", f.DataBitOffset)
297				}
298				return res
299			}
300
301			for _, field := range t1.Field {
302				// We're only testing for bitfields
303				if field.BitSize == 0 {
304					continue
305				}
306				got := bitInfoDump(field)
307				want := expectedBitOffsets[field.Name]
308				if got != want {
309					t.Errorf("%s: field %s in %s: got info %q want %q", tag, field.Name, t1.StructName, got, want)
310				}
311			}
312		}
313		if e.Tag != TagCompileUnit {
314			r.SkipChildren()
315		}
316	}
317}
318
319var bitfieldTests = map[string]string{
320	"t_another_struct": "struct another_struct {quix short unsigned int@0; xyz [0]int@4; x unsigned int@4 : 1@31; array [40]long long int@8}",
321}
322
323// TestBitFieldZeroArrayIssue50685 checks to make sure that the DWARF
324// type reading code doesn't get confused by the presence of a
325// specifically-sized bitfield member immediately following a field
326// whose type is a zero-length array. Prior to the fix for issue
327// 50685, we would get this type for the case in testdata/bitfields.c:
328//
329// another_struct {quix short unsigned int@0; xyz [-1]int@4; x unsigned int@4 : 1@31; array [40]long long int@8}
330//
331// Note the "-1" for the xyz field, which should be zero.
332func TestBitFieldZeroArrayIssue50685(t *testing.T) {
333	f := "testdata/bitfields.elf4"
334	testTypedefs(t, elfData(t, f), "elf", bitfieldTests)
335}
336