1
2
3
4
5 package ld
6
7 import (
8 "debug/dwarf"
9 "debug/pe"
10 "fmt"
11 "internal/platform"
12 "internal/testenv"
13 "io"
14 "os"
15 "path/filepath"
16 "reflect"
17 "runtime"
18 "sort"
19 "strconv"
20 "strings"
21 "testing"
22
23 intdwarf "cmd/internal/dwarf"
24 objfilepkg "cmd/internal/objfile"
25 "cmd/link/internal/dwtest"
26 )
27
28 func mustHaveDWARF(t testing.TB) {
29 if !platform.ExecutableHasDWARF(runtime.GOOS, runtime.GOARCH) {
30 t.Helper()
31 t.Skipf("skipping on %s/%s: no DWARF symbol table in executables", runtime.GOOS, runtime.GOARCH)
32 }
33 }
34
35 const (
36 DefaultOpt = "-gcflags="
37 NoOpt = "-gcflags=-l -N"
38 OptInl4 = "-gcflags=-l=4"
39 OptAllInl4 = "-gcflags=all=-l=4"
40 )
41
42 func TestRuntimeTypesPresent(t *testing.T) {
43 t.Parallel()
44 testenv.MustHaveGoBuild(t)
45
46 mustHaveDWARF(t)
47
48 dir := t.TempDir()
49
50 f := gobuild(t, dir, `package main; func main() { }`, NoOpt)
51 defer f.Close()
52
53 dwarf, err := f.DWARF()
54 if err != nil {
55 t.Fatalf("error reading DWARF: %v", err)
56 }
57
58 want := map[string]bool{
59 "internal/abi.Type": true,
60 "internal/abi.ArrayType": true,
61 "internal/abi.ChanType": true,
62 "internal/abi.FuncType": true,
63 "internal/abi.PtrType": true,
64 "internal/abi.SliceType": true,
65 "internal/abi.StructType": true,
66 "internal/abi.InterfaceType": true,
67 "internal/abi.ITab": true,
68 }
69
70 found := findTypes(t, dwarf, want)
71 if len(found) != len(want) {
72 t.Errorf("found %v, want %v", found, want)
73 }
74
75
76 want = map[string]bool{
77 "internal/abi.OldMapType": true,
78 "internal/abi.SwissMapType": true,
79 }
80 found = findTypes(t, dwarf, want)
81 if len(found) != 1 {
82 t.Errorf("map type want one of %v found %v", want, found)
83 }
84 }
85
86 func findTypes(t *testing.T, dw *dwarf.Data, want map[string]bool) (found map[string]bool) {
87 found = make(map[string]bool)
88 rdr := dw.Reader()
89 for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
90 if err != nil {
91 t.Fatalf("error reading DWARF: %v", err)
92 }
93 switch entry.Tag {
94 case dwarf.TagTypedef:
95 if name, ok := entry.Val(dwarf.AttrName).(string); ok && want[name] {
96 found[name] = true
97 }
98 }
99 }
100 return
101 }
102
103 type builtFile struct {
104 *objfilepkg.File
105 path string
106 }
107
108 func gobuild(t *testing.T, dir string, testfile string, gcflags string) *builtFile {
109 src := filepath.Join(dir, "test.go")
110 dst := filepath.Join(dir, "out.exe")
111
112 if err := os.WriteFile(src, []byte(testfile), 0666); err != nil {
113 t.Fatal(err)
114 }
115
116 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", gcflags, "-o", dst, src)
117 b, err := cmd.CombinedOutput()
118 if len(b) != 0 {
119 t.Logf("## build output:\n%s", b)
120 }
121 if err != nil {
122 t.Fatalf("build error: %v", err)
123 }
124
125 f, err := objfilepkg.Open(dst)
126 if err != nil {
127 t.Fatal(err)
128 }
129 return &builtFile{f, dst}
130 }
131
132
133
134 func gobuildTestdata(t *testing.T, pkgDir string, gcflags string) *builtFile {
135 dst := filepath.Join(t.TempDir(), "out.exe")
136
137
138 cmd := testenv.Command(t, testenv.GoToolPath(t), "build", gcflags, "-o", dst)
139 cmd.Dir = pkgDir
140 if b, err := cmd.CombinedOutput(); err != nil {
141 t.Logf("build: %s\n", b)
142 t.Fatalf("build error: %v", err)
143 }
144
145 f, err := objfilepkg.Open(dst)
146 if err != nil {
147 t.Fatal(err)
148 }
149 return &builtFile{f, dst}
150 }
151
152
153 func gobuildAndExamine(t *testing.T, source string, gcflags string) (*dwarf.Data, *dwtest.Examiner) {
154 dir := t.TempDir()
155
156 f := gobuild(t, dir, source, gcflags)
157 defer f.Close()
158
159 d, err := f.DWARF()
160 if err != nil {
161 t.Fatalf("error reading DWARF in program %q: %v", source, err)
162 }
163
164 rdr := d.Reader()
165 ex := &dwtest.Examiner{}
166 if err := ex.Populate(rdr); err != nil {
167 t.Fatalf("error populating DWARF examiner for program %q: %v", source, err)
168 }
169
170 return d, ex
171 }
172
173 func findSubprogramDIE(t *testing.T, ex *dwtest.Examiner, sym string) *dwarf.Entry {
174 dies := ex.Named(sym)
175 if len(dies) == 0 {
176 t.Fatalf("unable to locate DIE for %s", sym)
177 }
178 if len(dies) != 1 {
179 t.Fatalf("more than one %s DIE: %+v", sym, dies)
180 }
181 die := dies[0]
182
183
184 if die.Tag != dwarf.TagSubprogram {
185 t.Fatalf("unexpected tag %v on %s DIE", die.Tag, sym)
186 }
187
188 return die
189 }
190
191 func TestEmbeddedStructMarker(t *testing.T) {
192 t.Parallel()
193 testenv.MustHaveGoBuild(t)
194
195 mustHaveDWARF(t)
196
197 const prog = `
198 package main
199
200 import "fmt"
201
202 type Foo struct { v int }
203 type Bar struct {
204 Foo
205 name string
206 }
207 type Baz struct {
208 *Foo
209 name string
210 }
211
212 func main() {
213 bar := Bar{ Foo: Foo{v: 123}, name: "onetwothree"}
214 baz := Baz{ Foo: &bar.Foo, name: "123" }
215 fmt.Println(bar, baz)
216 }`
217
218 want := map[string]map[string]bool{
219 "main.Foo": {"v": false},
220 "main.Bar": {"Foo": true, "name": false},
221 "main.Baz": {"Foo": true, "name": false},
222 }
223
224 dir := t.TempDir()
225
226 f := gobuild(t, dir, prog, NoOpt)
227
228 defer f.Close()
229
230 d, err := f.DWARF()
231 if err != nil {
232 t.Fatalf("error reading DWARF: %v", err)
233 }
234
235 rdr := d.Reader()
236 for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
237 if err != nil {
238 t.Fatalf("error reading DWARF: %v", err)
239 }
240 switch entry.Tag {
241 case dwarf.TagStructType:
242 name, ok := entry.Val(dwarf.AttrName).(string)
243 if !ok {
244 continue
245 }
246 wantMembers := want[name]
247 if wantMembers == nil {
248 continue
249 }
250 gotMembers, err := findMembers(rdr)
251 if err != nil {
252 t.Fatalf("error reading DWARF: %v", err)
253 }
254
255 if !reflect.DeepEqual(gotMembers, wantMembers) {
256 t.Errorf("type %v: got map[member]embedded = %+v, want %+v", name, wantMembers, gotMembers)
257 }
258 delete(want, name)
259 }
260 }
261 if len(want) != 0 {
262 t.Errorf("failed to check all expected types: missing types = %+v", want)
263 }
264 }
265
266 func findMembers(rdr *dwarf.Reader) (map[string]bool, error) {
267 memberEmbedded := map[string]bool{}
268
269 const goEmbeddedStruct = dwarf.Attr(intdwarf.DW_AT_go_embedded_field)
270 for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
271 if err != nil {
272 return nil, err
273 }
274 switch entry.Tag {
275 case dwarf.TagMember:
276 name := entry.Val(dwarf.AttrName).(string)
277 embedded := entry.Val(goEmbeddedStruct).(bool)
278 memberEmbedded[name] = embedded
279 case 0:
280 return memberEmbedded, nil
281 }
282 }
283 return memberEmbedded, nil
284 }
285
286 func TestSizes(t *testing.T) {
287 mustHaveDWARF(t)
288
289
290
291
292
293 testenv.MustInternalLink(t, testenv.NoSpecialBuildTypes)
294
295 t.Parallel()
296
297
298
299 const prog = `
300 package main
301 var x func()
302 var y [4]func()
303 func main() {
304 x = nil
305 y[0] = nil
306 }
307 `
308 dir := t.TempDir()
309
310 f := gobuild(t, dir, prog, NoOpt)
311 defer f.Close()
312 d, err := f.DWARF()
313 if err != nil {
314 t.Fatalf("error reading DWARF: %v", err)
315 }
316 rdr := d.Reader()
317 for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
318 if err != nil {
319 t.Fatalf("error reading DWARF: %v", err)
320 }
321 switch entry.Tag {
322 case dwarf.TagArrayType, dwarf.TagPointerType, dwarf.TagStructType, dwarf.TagBaseType, dwarf.TagSubroutineType, dwarf.TagTypedef:
323 default:
324 continue
325 }
326 typ, err := d.Type(entry.Offset)
327 if err != nil {
328 t.Fatalf("can't read type: %v", err)
329 }
330 if typ.Size() < 0 {
331 t.Errorf("subzero size %s %s %T", typ, entry.Tag, typ)
332 }
333 }
334 }
335
336 func TestFieldOverlap(t *testing.T) {
337 mustHaveDWARF(t)
338 t.Parallel()
339
340
341
342 const prog = `
343 package main
344
345 var c chan string
346
347 func main() {
348 c <- "foo"
349 }
350 `
351 dir := t.TempDir()
352
353 f := gobuild(t, dir, prog, NoOpt)
354 defer f.Close()
355
356 d, err := f.DWARF()
357 if err != nil {
358 t.Fatalf("error reading DWARF: %v", err)
359 }
360
361 rdr := d.Reader()
362 for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
363 if err != nil {
364 t.Fatalf("error reading DWARF: %v", err)
365 }
366 if entry.Tag != dwarf.TagStructType {
367 continue
368 }
369 typ, err := d.Type(entry.Offset)
370 if err != nil {
371 t.Fatalf("can't read type: %v", err)
372 }
373 s := typ.(*dwarf.StructType)
374 for i := 0; i < len(s.Field); i++ {
375 end := s.Field[i].ByteOffset + s.Field[i].Type.Size()
376 var limit int64
377 if i == len(s.Field)-1 {
378 limit = s.Size()
379 } else {
380 limit = s.Field[i+1].ByteOffset
381 }
382 if end > limit {
383 name := entry.Val(dwarf.AttrName).(string)
384 t.Fatalf("field %s.%s overlaps next field", name, s.Field[i].Name)
385 }
386 }
387 }
388 }
389
390 func TestSubprogramDeclFileLine(t *testing.T) {
391 testenv.MustHaveGoBuild(t)
392 t.Parallel()
393
394 mustHaveDWARF(t)
395
396 const prog = `package main
397 %s
398 func main() {}
399 `
400 tests := []struct {
401 name string
402 prog string
403 file string
404 line int64
405 }{
406 {
407 name: "normal",
408 prog: fmt.Sprintf(prog, ""),
409 file: "test.go",
410 line: 3,
411 },
412 {
413 name: "line-directive",
414 prog: fmt.Sprintf(prog, "//line /foobar.go:200"),
415 file: "foobar.go",
416 line: 200,
417 },
418 }
419 for _, tc := range tests {
420 tc := tc
421 t.Run(tc.name, func(t *testing.T) {
422 t.Parallel()
423
424 d, ex := gobuildAndExamine(t, tc.prog, NoOpt)
425
426 maindie := findSubprogramDIE(t, ex, "main.main")
427
428 mainIdx := ex.IdxFromOffset(maindie.Offset)
429
430 fileIdx, fileIdxOK := maindie.Val(dwarf.AttrDeclFile).(int64)
431 if !fileIdxOK {
432 t.Errorf("missing or invalid DW_AT_decl_file for main")
433 }
434 file, err := ex.FileRef(d, mainIdx, fileIdx)
435 if err != nil {
436 t.Fatalf("FileRef: %v", err)
437 }
438 base := filepath.Base(file)
439 if base != tc.file {
440 t.Errorf("DW_AT_decl_file for main is %v, want %v", base, tc.file)
441 }
442
443 line, lineOK := maindie.Val(dwarf.AttrDeclLine).(int64)
444 if !lineOK {
445 t.Errorf("missing or invalid DW_AT_decl_line for main")
446 }
447 if line != tc.line {
448 t.Errorf("DW_AT_decl_line for main is %v, want %d", line, tc.line)
449 }
450 })
451 }
452 }
453
454 func TestVarDeclLine(t *testing.T) {
455 testenv.MustHaveGoBuild(t)
456 t.Parallel()
457
458 mustHaveDWARF(t)
459
460 const prog = `package main
461 %s
462 func main() {
463
464 var i int
465 i = i
466 }
467 `
468 tests := []struct {
469 name string
470 prog string
471 line int64
472 }{
473 {
474 name: "normal",
475 prog: fmt.Sprintf(prog, ""),
476 line: 5,
477 },
478 {
479 name: "line-directive",
480 prog: fmt.Sprintf(prog, "//line /foobar.go:200"),
481 line: 202,
482 },
483 }
484 for _, tc := range tests {
485 tc := tc
486 t.Run(tc.name, func(t *testing.T) {
487 t.Parallel()
488
489 _, ex := gobuildAndExamine(t, tc.prog, NoOpt)
490
491 maindie := findSubprogramDIE(t, ex, "main.main")
492
493 mainIdx := ex.IdxFromOffset(maindie.Offset)
494 childDies := ex.Children(mainIdx)
495 var iEntry *dwarf.Entry
496 for _, child := range childDies {
497 if child.Tag == dwarf.TagVariable && child.Val(dwarf.AttrName).(string) == "i" {
498 iEntry = child
499 break
500 }
501 }
502 if iEntry == nil {
503 t.Fatalf("didn't find DW_TAG_variable for i in main.main")
504 }
505
506
507 line, lineOK := iEntry.Val(dwarf.AttrDeclLine).(int64)
508 if !lineOK {
509 t.Errorf("missing or invalid DW_AT_decl_line for i")
510 }
511 if line != tc.line {
512 t.Errorf("DW_AT_decl_line for i is %v, want %d", line, tc.line)
513 }
514 })
515 }
516 }
517
518
519
520 func TestInlinedRoutineCallFileLine(t *testing.T) {
521 testenv.MustHaveGoBuild(t)
522
523 mustHaveDWARF(t)
524
525 t.Parallel()
526
527 const prog = `
528 package main
529
530 var G int
531
532 //go:noinline
533 func notinlined() int {
534 return 42
535 }
536
537 func inlined() int {
538 return notinlined()
539 }
540
541 %s
542 func main() {
543 x := inlined()
544 G = x
545 }
546 `
547 tests := []struct {
548 name string
549 prog string
550 file string
551 line int64
552 }{
553 {
554 name: "normal",
555 prog: fmt.Sprintf(prog, ""),
556 file: "test.go",
557 line: 17,
558 },
559 {
560 name: "line-directive",
561 prog: fmt.Sprintf(prog, "//line /foobar.go:200"),
562 file: "foobar.go",
563 line: 201,
564 },
565 }
566 for _, tc := range tests {
567 tc := tc
568 t.Run(tc.name, func(t *testing.T) {
569 t.Parallel()
570
571
572
573
574
575
576 d, ex := gobuildAndExamine(t, tc.prog, OptInl4)
577
578 maindie := findSubprogramDIE(t, ex, "main.main")
579
580
581 mainIdx := ex.IdxFromOffset(maindie.Offset)
582 childDies := ex.Children(mainIdx)
583 found := false
584 for _, child := range childDies {
585 if child.Tag != dwarf.TagInlinedSubroutine {
586 continue
587 }
588
589
590 if found {
591 t.Fatalf("Found multiple inlined subroutines, expect only one")
592 }
593 found = true
594
595
596 ooff, originOK := child.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
597 if !originOK {
598 t.Fatalf("no abstract origin attr for inlined subroutine at offset %v", child.Offset)
599 }
600 originDIE := ex.EntryFromOffset(ooff)
601 if originDIE == nil {
602 t.Fatalf("can't locate origin DIE at off %v", ooff)
603 }
604
605
606 name, ok := originDIE.Val(dwarf.AttrName).(string)
607 if !ok {
608 t.Fatalf("no name attr for inlined subroutine at offset %v", child.Offset)
609 }
610 if name != "main.inlined" {
611 t.Fatalf("expected inlined routine %s got %s", "main.cand", name)
612 }
613
614
615
616
617
618
619 cf, cfOK := child.Val(dwarf.AttrCallFile).(int64)
620 if !cfOK {
621 t.Fatalf("no call_file attr for inlined subroutine at offset %v", child.Offset)
622 }
623 file, err := ex.FileRef(d, mainIdx, cf)
624 if err != nil {
625 t.Errorf("FileRef: %v", err)
626 continue
627 }
628 base := filepath.Base(file)
629 if base != tc.file {
630 t.Errorf("bad call_file attribute, found '%s', want '%s'",
631 file, tc.file)
632 }
633
634
635
636 cl, clOK := child.Val(dwarf.AttrCallLine).(int64)
637 if !clOK {
638 t.Fatalf("no call_line attr for inlined subroutine at offset %v", child.Offset)
639 }
640 if cl != tc.line {
641 t.Errorf("bad call_line attribute, found %d, want %d", cl, tc.line)
642 }
643 }
644 if !found {
645 t.Fatalf("not enough inlined subroutines found in main.main")
646 }
647 })
648 }
649 }
650
651
652 func TestInlinedRoutineArgsVars(t *testing.T) {
653 testenv.MustHaveGoBuild(t)
654
655 mustHaveDWARF(t)
656
657 t.Parallel()
658
659 const prog = `
660 package main
661
662 var G int
663
664 func noinline(x int) int {
665 defer func() { G += x }()
666 return x
667 }
668
669 func cand(x, y int) int {
670 return noinline(x+y) ^ (y - x)
671 }
672
673 func main() {
674 x := cand(G*G,G|7%G)
675 G = x
676 }
677 `
678
679
680
681
682
683 _, ex := gobuildAndExamine(t, prog, OptInl4)
684
685 maindie := findSubprogramDIE(t, ex, "main.main")
686
687
688 mainIdx := ex.IdxFromOffset(maindie.Offset)
689 childDies := ex.Children(mainIdx)
690 found := false
691 for _, child := range childDies {
692 if child.Tag != dwarf.TagInlinedSubroutine {
693 continue
694 }
695
696
697 if found {
698 t.Fatalf("Found multiple inlined subroutines, expect only one")
699 }
700 found = true
701
702
703 ooff, originOK := child.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
704 if !originOK {
705 t.Fatalf("no abstract origin attr for inlined subroutine at offset %v", child.Offset)
706 }
707 originDIE := ex.EntryFromOffset(ooff)
708 if originDIE == nil {
709 t.Fatalf("can't locate origin DIE at off %v", ooff)
710 }
711
712
713 name, ok := originDIE.Val(dwarf.AttrName).(string)
714 if !ok {
715 t.Fatalf("no name attr for inlined subroutine at offset %v", child.Offset)
716 }
717 if name != "main.cand" {
718 t.Fatalf("expected inlined routine %s got %s", "main.cand", name)
719 }
720
721
722
723
724
725 absFcnIdx := ex.IdxFromOffset(ooff)
726 absFcnChildDies := ex.Children(absFcnIdx)
727 if len(absFcnChildDies) != 2 {
728 t.Fatalf("expected abstract function: expected 2 children, got %d children", len(absFcnChildDies))
729 }
730 formalCount := 0
731 for _, absChild := range absFcnChildDies {
732 if absChild.Tag == dwarf.TagFormalParameter {
733 formalCount += 1
734 continue
735 }
736 t.Fatalf("abstract function child DIE: expected formal, got %v", absChild.Tag)
737 }
738 if formalCount != 2 {
739 t.Fatalf("abstract function DIE: expected 2 formals, got %d", formalCount)
740 }
741
742 omap := make(map[dwarf.Offset]bool)
743
744
745
746
747 inlIdx := ex.IdxFromOffset(child.Offset)
748 inlChildDies := ex.Children(inlIdx)
749 for _, k := range inlChildDies {
750 ooff, originOK := k.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
751 if !originOK {
752 t.Fatalf("no abstract origin attr for child of inlined subroutine at offset %v", k.Offset)
753 }
754 if _, found := omap[ooff]; found {
755 t.Fatalf("duplicate abstract origin at child of inlined subroutine at offset %v", k.Offset)
756 }
757 omap[ooff] = true
758 }
759 }
760 if !found {
761 t.Fatalf("not enough inlined subroutines found in main.main")
762 }
763 }
764
765 func abstractOriginSanity(t *testing.T, pkgDir string, flags string) {
766 t.Parallel()
767
768
769 f := gobuildTestdata(t, filepath.Join(pkgDir, "main"), flags)
770 defer f.Close()
771
772 d, err := f.DWARF()
773 if err != nil {
774 t.Fatalf("error reading DWARF: %v", err)
775 }
776 rdr := d.Reader()
777 ex := dwtest.Examiner{}
778 if err := ex.Populate(rdr); err != nil {
779 t.Fatalf("error reading DWARF: %v", err)
780 }
781
782
783
784 abscount := 0
785 for i, die := range ex.DIEs() {
786
787 ooff, originOK := die.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
788 if !originOK {
789 continue
790 }
791
792
793 abscount += 1
794 originDIE := ex.EntryFromOffset(ooff)
795 if originDIE == nil {
796 ex.DumpEntry(i, false, 0)
797 t.Fatalf("unresolved abstract origin ref in DIE at offset 0x%x\n", die.Offset)
798 }
799
800
801
802
803
804 pidx := ex.IdxFromOffset(die.Offset)
805 if pidx < 0 {
806 t.Fatalf("can't locate DIE id")
807 }
808 kids := ex.Children(pidx)
809 for _, kid := range kids {
810 if kid.Tag != dwarf.TagVariable &&
811 kid.Tag != dwarf.TagFormalParameter {
812 continue
813 }
814 kooff, originOK := kid.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
815 if !originOK {
816 continue
817 }
818 childOriginDIE := ex.EntryFromOffset(kooff)
819 if childOriginDIE == nil {
820 ex.DumpEntry(i, false, 0)
821 t.Fatalf("unresolved abstract origin ref in DIE at offset %x", kid.Offset)
822 }
823 coidx := ex.IdxFromOffset(childOriginDIE.Offset)
824 childOriginParent := ex.Parent(coidx)
825 if childOriginParent != originDIE {
826 ex.DumpEntry(i, false, 0)
827 t.Fatalf("unexpected parent of abstract origin DIE at offset %v", childOriginDIE.Offset)
828 }
829 }
830 }
831 if abscount == 0 {
832 t.Fatalf("no abstract origin refs found, something is wrong")
833 }
834 }
835
836 func TestAbstractOriginSanity(t *testing.T) {
837 testenv.MustHaveGoBuild(t)
838
839 if testing.Short() {
840 t.Skip("skipping test in short mode.")
841 }
842
843 mustHaveDWARF(t)
844 abstractOriginSanity(t, "testdata/httptest", OptAllInl4)
845 }
846
847 func TestAbstractOriginSanityIssue25459(t *testing.T) {
848 testenv.MustHaveGoBuild(t)
849
850 mustHaveDWARF(t)
851 if runtime.GOARCH != "amd64" && runtime.GOARCH != "386" {
852 t.Skip("skipping on not-amd64 not-386; location lists not supported")
853 }
854
855 abstractOriginSanity(t, "testdata/issue25459", DefaultOpt)
856 }
857
858 func TestAbstractOriginSanityIssue26237(t *testing.T) {
859 testenv.MustHaveGoBuild(t)
860
861 mustHaveDWARF(t)
862 abstractOriginSanity(t, "testdata/issue26237", DefaultOpt)
863 }
864
865 func TestRuntimeTypeAttrInternal(t *testing.T) {
866 testenv.MustHaveGoBuild(t)
867
868
869 testenv.MustInternalLink(t, testenv.NoSpecialBuildTypes)
870
871 mustHaveDWARF(t)
872
873 testRuntimeTypeAttr(t, "-ldflags=-linkmode=internal")
874 }
875
876
877 func TestRuntimeTypeAttrExternal(t *testing.T) {
878 testenv.MustHaveGoBuild(t)
879 testenv.MustHaveCGO(t)
880
881 mustHaveDWARF(t)
882
883
884 if runtime.GOARCH == "ppc64" {
885 t.Skip("-linkmode=external not supported on ppc64")
886 }
887
888 testRuntimeTypeAttr(t, "-ldflags=-linkmode=external")
889 }
890
891 func testRuntimeTypeAttr(t *testing.T, flags string) {
892 t.Parallel()
893
894 const prog = `
895 package main
896
897 import "unsafe"
898
899 type X struct{ _ int }
900
901 func main() {
902 var x interface{} = &X{}
903 p := *(*uintptr)(unsafe.Pointer(&x))
904 print(p)
905 }
906 `
907 dir := t.TempDir()
908
909 f := gobuild(t, dir, prog, flags)
910 defer f.Close()
911
912 out, err := testenv.Command(t, f.path).CombinedOutput()
913 if err != nil {
914 t.Fatalf("could not run test program: %v", err)
915 }
916 addr, err := strconv.ParseUint(string(out), 10, 64)
917 if err != nil {
918 t.Fatalf("could not parse type address from program output %q: %v", out, err)
919 }
920
921 symbols, err := f.Symbols()
922 if err != nil {
923 t.Fatalf("error reading symbols: %v", err)
924 }
925 var types *objfilepkg.Sym
926 for _, sym := range symbols {
927 if sym.Name == "runtime.types" {
928 types = &sym
929 break
930 }
931 }
932 if types == nil {
933 t.Fatal("couldn't find runtime.types in symbols")
934 }
935
936 d, err := f.DWARF()
937 if err != nil {
938 t.Fatalf("error reading DWARF: %v", err)
939 }
940
941 rdr := d.Reader()
942 ex := dwtest.Examiner{}
943 if err := ex.Populate(rdr); err != nil {
944 t.Fatalf("error reading DWARF: %v", err)
945 }
946 dies := ex.Named("*main.X")
947 if len(dies) != 1 {
948 t.Fatalf("wanted 1 DIE named *main.X, found %v", len(dies))
949 }
950 rtAttr := dies[0].Val(intdwarf.DW_AT_go_runtime_type)
951 if rtAttr == nil {
952 t.Fatalf("*main.X DIE had no runtime type attr. DIE: %v", dies[0])
953 }
954
955 if platform.DefaultPIE(runtime.GOOS, runtime.GOARCH, false) {
956 return
957 }
958 if rtAttr.(uint64)+types.Addr != addr {
959 t.Errorf("DWARF type offset was %#x+%#x, but test program said %#x", rtAttr.(uint64), types.Addr, addr)
960 }
961 }
962
963 func TestIssue27614(t *testing.T) {
964
965
966
967 testenv.MustHaveGoBuild(t)
968
969 mustHaveDWARF(t)
970
971 t.Parallel()
972
973 dir := t.TempDir()
974
975 const prog = `package main
976
977 import "fmt"
978
979 type astruct struct {
980 X int
981 }
982
983 type bstruct struct {
984 X float32
985 }
986
987 var globalptr *astruct
988 var globalvar astruct
989 var bvar0, bvar1, bvar2 bstruct
990
991 func main() {
992 fmt.Println(globalptr, globalvar, bvar0, bvar1, bvar2)
993 }
994 `
995
996 f := gobuild(t, dir, prog, NoOpt)
997
998 defer f.Close()
999
1000 data, err := f.DWARF()
1001 if err != nil {
1002 t.Fatal(err)
1003 }
1004
1005 rdr := data.Reader()
1006
1007 var astructTypeDIE, bstructTypeDIE, ptrastructTypeDIE *dwarf.Entry
1008 var globalptrDIE, globalvarDIE *dwarf.Entry
1009 var bvarDIE [3]*dwarf.Entry
1010
1011 for {
1012 e, err := rdr.Next()
1013 if err != nil {
1014 t.Fatal(err)
1015 }
1016 if e == nil {
1017 break
1018 }
1019
1020 name, _ := e.Val(dwarf.AttrName).(string)
1021
1022 switch e.Tag {
1023 case dwarf.TagTypedef:
1024 switch name {
1025 case "main.astruct":
1026 astructTypeDIE = e
1027 case "main.bstruct":
1028 bstructTypeDIE = e
1029 }
1030 case dwarf.TagPointerType:
1031 if name == "*main.astruct" {
1032 ptrastructTypeDIE = e
1033 }
1034 case dwarf.TagVariable:
1035 switch name {
1036 case "main.globalptr":
1037 globalptrDIE = e
1038 case "main.globalvar":
1039 globalvarDIE = e
1040 default:
1041 const bvarprefix = "main.bvar"
1042 if strings.HasPrefix(name, bvarprefix) {
1043 i, _ := strconv.Atoi(name[len(bvarprefix):])
1044 bvarDIE[i] = e
1045 }
1046 }
1047 }
1048 }
1049
1050 typedieof := func(e *dwarf.Entry) dwarf.Offset {
1051 return e.Val(dwarf.AttrType).(dwarf.Offset)
1052 }
1053
1054 if off := typedieof(ptrastructTypeDIE); off != astructTypeDIE.Offset {
1055 t.Errorf("type attribute of *main.astruct references %#x, not main.astruct DIE at %#x\n", off, astructTypeDIE.Offset)
1056 }
1057
1058 if off := typedieof(globalptrDIE); off != ptrastructTypeDIE.Offset {
1059 t.Errorf("type attribute of main.globalptr references %#x, not *main.astruct DIE at %#x\n", off, ptrastructTypeDIE.Offset)
1060 }
1061
1062 if off := typedieof(globalvarDIE); off != astructTypeDIE.Offset {
1063 t.Errorf("type attribute of main.globalvar1 references %#x, not main.astruct DIE at %#x\n", off, astructTypeDIE.Offset)
1064 }
1065
1066 for i := range bvarDIE {
1067 if off := typedieof(bvarDIE[i]); off != bstructTypeDIE.Offset {
1068 t.Errorf("type attribute of main.bvar%d references %#x, not main.bstruct DIE at %#x\n", i, off, bstructTypeDIE.Offset)
1069 }
1070 }
1071 }
1072
1073 func TestStaticTmp(t *testing.T) {
1074
1075
1076
1077
1078
1079 testenv.MustHaveGoBuild(t)
1080
1081 mustHaveDWARF(t)
1082
1083 t.Parallel()
1084
1085 dir := t.TempDir()
1086
1087 const prog = `package main
1088
1089 var stmp_0 string
1090 var a []int
1091
1092 func init() {
1093 a = []int{ 7 }
1094 }
1095
1096 func main() {
1097 println(a[0])
1098 }
1099 `
1100
1101 f := gobuild(t, dir, prog, NoOpt)
1102
1103 defer f.Close()
1104
1105 d, err := f.DWARF()
1106 if err != nil {
1107 t.Fatalf("error reading DWARF: %v", err)
1108 }
1109
1110 rdr := d.Reader()
1111 for {
1112 e, err := rdr.Next()
1113 if err != nil {
1114 t.Fatal(err)
1115 }
1116 if e == nil {
1117 break
1118 }
1119 if e.Tag != dwarf.TagVariable {
1120 continue
1121 }
1122 name, ok := e.Val(dwarf.AttrName).(string)
1123 if !ok {
1124 continue
1125 }
1126 if strings.Contains(name, "stmp") {
1127 t.Errorf("statictmp variable found in debug_info: %s at %x", name, e.Offset)
1128 }
1129 }
1130
1131
1132
1133
1134
1135
1136 if !testenv.CanInternalLink(false) {
1137 return
1138 }
1139
1140 syms, err := f.Symbols()
1141 if err != nil {
1142 t.Fatalf("error reading symbols: %v", err)
1143 }
1144 for _, sym := range syms {
1145 if strings.Contains(sym.Name, "stmp") {
1146 t.Errorf("statictmp variable found in symbol table: %s", sym.Name)
1147 }
1148 }
1149 }
1150
1151 func TestPackageNameAttr(t *testing.T) {
1152 const dwarfAttrGoPackageName = dwarf.Attr(0x2905)
1153 const dwarfGoLanguage = 22
1154
1155 testenv.MustHaveGoBuild(t)
1156
1157 mustHaveDWARF(t)
1158
1159 t.Parallel()
1160
1161 dir := t.TempDir()
1162
1163 const prog = "package main\nfunc main() {\nprintln(\"hello world\")\n}\n"
1164
1165 f := gobuild(t, dir, prog, NoOpt)
1166
1167 defer f.Close()
1168
1169 d, err := f.DWARF()
1170 if err != nil {
1171 t.Fatalf("error reading DWARF: %v", err)
1172 }
1173
1174 rdr := d.Reader()
1175 runtimeUnitSeen := false
1176 for {
1177 e, err := rdr.Next()
1178 if err != nil {
1179 t.Fatal(err)
1180 }
1181 if e == nil {
1182 break
1183 }
1184 if e.Tag != dwarf.TagCompileUnit {
1185 continue
1186 }
1187 if lang, _ := e.Val(dwarf.AttrLanguage).(int64); lang != dwarfGoLanguage {
1188 continue
1189 }
1190
1191 pn, ok := e.Val(dwarfAttrGoPackageName).(string)
1192 if !ok {
1193 name, _ := e.Val(dwarf.AttrName).(string)
1194 t.Errorf("found compile unit without package name: %s", name)
1195
1196 }
1197 if pn == "" {
1198 name, _ := e.Val(dwarf.AttrName).(string)
1199 t.Errorf("found compile unit with empty package name: %s", name)
1200 } else {
1201 if pn == "runtime" {
1202 runtimeUnitSeen = true
1203 }
1204 }
1205 }
1206
1207
1208 if !runtimeUnitSeen {
1209 t.Errorf("no package name for runtime unit")
1210 }
1211 }
1212
1213 func TestMachoIssue32233(t *testing.T) {
1214 testenv.MustHaveGoBuild(t)
1215 testenv.MustHaveCGO(t)
1216
1217 if runtime.GOOS != "darwin" {
1218 t.Skip("skipping; test only interesting on darwin")
1219 }
1220
1221 f := gobuildTestdata(t, "testdata/issue32233/main", DefaultOpt)
1222 f.Close()
1223 }
1224
1225 func TestWindowsIssue36495(t *testing.T) {
1226 testenv.MustHaveGoBuild(t)
1227 if runtime.GOOS != "windows" {
1228 t.Skip("skipping: test only on windows")
1229 }
1230
1231 dir := t.TempDir()
1232
1233 prog := `
1234 package main
1235
1236 import "fmt"
1237
1238 func main() {
1239 fmt.Println("Hello World")
1240 }`
1241 f := gobuild(t, dir, prog, NoOpt)
1242 defer f.Close()
1243 exe, err := pe.Open(f.path)
1244 if err != nil {
1245 t.Fatalf("error opening pe file: %v", err)
1246 }
1247 defer exe.Close()
1248 dw, err := exe.DWARF()
1249 if err != nil {
1250 t.Fatalf("error parsing DWARF: %v", err)
1251 }
1252 rdr := dw.Reader()
1253 for {
1254 e, err := rdr.Next()
1255 if err != nil {
1256 t.Fatalf("error reading DWARF: %v", err)
1257 }
1258 if e == nil {
1259 break
1260 }
1261 if e.Tag != dwarf.TagCompileUnit {
1262 continue
1263 }
1264 lnrdr, err := dw.LineReader(e)
1265 if err != nil {
1266 t.Fatalf("error creating DWARF line reader: %v", err)
1267 }
1268 if lnrdr != nil {
1269 var lne dwarf.LineEntry
1270 for {
1271 err := lnrdr.Next(&lne)
1272 if err == io.EOF {
1273 break
1274 }
1275 if err != nil {
1276 t.Fatalf("error reading next DWARF line: %v", err)
1277 }
1278 if strings.Contains(lne.File.Name, `\`) {
1279 t.Errorf("filename should not contain backslash: %v", lne.File.Name)
1280 }
1281 }
1282 }
1283 rdr.SkipChildren()
1284 }
1285 }
1286
1287 func TestIssue38192(t *testing.T) {
1288 testenv.MustHaveGoBuild(t)
1289
1290 mustHaveDWARF(t)
1291
1292 t.Parallel()
1293
1294
1295
1296 f := gobuildTestdata(t, "testdata/issue38192", DefaultOpt)
1297 defer f.Close()
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313 rows := []dwarf.LineEntry{}
1314 dw, err := f.DWARF()
1315 if err != nil {
1316 t.Fatalf("error parsing DWARF: %v", err)
1317 }
1318 rdr := dw.Reader()
1319 for {
1320 e, err := rdr.Next()
1321 if err != nil {
1322 t.Fatalf("error reading DWARF: %v", err)
1323 }
1324 if e == nil {
1325 break
1326 }
1327 if e.Tag != dwarf.TagCompileUnit {
1328 continue
1329 }
1330
1331 name := e.Val(dwarf.AttrName).(string)
1332 if name != "main" {
1333 continue
1334 }
1335 lnrdr, err := dw.LineReader(e)
1336 if err != nil {
1337 t.Fatalf("error creating DWARF line reader: %v", err)
1338 }
1339 if lnrdr != nil {
1340 var lne dwarf.LineEntry
1341 for {
1342 err := lnrdr.Next(&lne)
1343 if err == io.EOF {
1344 break
1345 }
1346 if err != nil {
1347 t.Fatalf("error reading next DWARF line: %v", err)
1348 }
1349 if !strings.HasSuffix(lne.File.Name, "ld/testdata/issue38192/oneline.s") {
1350 continue
1351 }
1352 rows = append(rows, lne)
1353 }
1354 }
1355 rdr.SkipChildren()
1356 }
1357 f.Close()
1358
1359
1360
1361
1362
1363
1364 pcs := make(map[uint64]bool)
1365 line8seen := false
1366 for _, r := range rows {
1367 pcs[r.Address] = true
1368 if r.Line == 8 {
1369 line8seen = true
1370 }
1371 }
1372 failed := false
1373 if len(pcs) < 2 {
1374 failed = true
1375 t.Errorf("not enough line table rows for main.singleInstruction (got %d, wanted > 1", len(pcs))
1376 }
1377 if !line8seen {
1378 failed = true
1379 t.Errorf("line table does not contain correct line for main.singleInstruction")
1380 }
1381 if !failed {
1382 return
1383 }
1384 for i, r := range rows {
1385 t.Logf("row %d: A=%x F=%s L=%d\n", i, r.Address, r.File.Name, r.Line)
1386 }
1387 }
1388
1389 func TestIssue39757(t *testing.T) {
1390 testenv.MustHaveGoBuild(t)
1391
1392 mustHaveDWARF(t)
1393
1394 t.Parallel()
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408 f := gobuildTestdata(t, "testdata/issue39757", DefaultOpt)
1409 defer f.Close()
1410
1411 syms, err := f.Symbols()
1412 if err != nil {
1413 t.Fatal(err)
1414 }
1415
1416 var addr uint64
1417 for _, sym := range syms {
1418 if sym.Name == "main.main" {
1419 addr = sym.Addr
1420 break
1421 }
1422 }
1423 if addr == 0 {
1424 t.Fatal("cannot find main.main in symbols")
1425 }
1426
1427
1428
1429
1430
1431 dw, err := f.DWARF()
1432 if err != nil {
1433 t.Fatalf("error parsing DWARF: %v", err)
1434 }
1435 rdr := dw.Reader()
1436 ex := &dwtest.Examiner{}
1437 if err := ex.Populate(rdr); err != nil {
1438 t.Fatalf("error reading DWARF: %v", err)
1439 }
1440
1441 maindie := findSubprogramDIE(t, ex, "main.main")
1442
1443
1444
1445
1446
1447 lowpc, highpc, perr := dwtest.SubprogLoAndHighPc(maindie)
1448 if perr != nil {
1449 t.Fatalf("main.main DIE malformed: %v", perr)
1450 }
1451 t.Logf("lo=0x%x hi=0x%x\n", lowpc, highpc)
1452
1453
1454 mainIdx := ex.IdxFromOffset(maindie.Offset)
1455 cuentry := ex.Parent(mainIdx)
1456 if cuentry == nil {
1457 t.Fatalf("main.main DIE appears orphaned")
1458 }
1459 lnrdr, lerr := dw.LineReader(cuentry)
1460 if lerr != nil {
1461 t.Fatalf("error creating DWARF line reader: %v", err)
1462 }
1463 if lnrdr == nil {
1464 t.Fatalf("no line table for main.main compilation unit")
1465 }
1466 rows := []dwarf.LineEntry{}
1467 mainrows := 0
1468 var lne dwarf.LineEntry
1469 for {
1470 err := lnrdr.Next(&lne)
1471 if err == io.EOF {
1472 break
1473 }
1474 rows = append(rows, lne)
1475 if err != nil {
1476 t.Fatalf("error reading next DWARF line: %v", err)
1477 }
1478 if lne.Address < lowpc || lne.Address > highpc {
1479 continue
1480 }
1481 if !strings.HasSuffix(lne.File.Name, "issue39757main.go") {
1482 t.Errorf("found row with file=%s (not issue39757main.go)", lne.File.Name)
1483 }
1484 mainrows++
1485 }
1486 f.Close()
1487
1488
1489 if mainrows < 3 {
1490 t.Errorf("not enough line table rows for main.main (got %d, wanted > 3", mainrows)
1491 for i, r := range rows {
1492 t.Logf("row %d: A=%x F=%s L=%d\n", i, r.Address, r.File.Name, r.Line)
1493 }
1494 }
1495 }
1496
1497 func TestIssue42484(t *testing.T) {
1498 testenv.MustHaveGoBuild(t)
1499
1500
1501
1502
1503 testenv.MustInternalLink(t, testenv.NoSpecialBuildTypes)
1504
1505 mustHaveDWARF(t)
1506
1507 t.Parallel()
1508
1509 f := gobuildTestdata(t, "testdata/issue42484", NoOpt)
1510
1511 var lastAddr uint64
1512 var lastFile string
1513 var lastLine int
1514
1515 dw, err := f.DWARF()
1516 if err != nil {
1517 t.Fatalf("error parsing DWARF: %v", err)
1518 }
1519 rdr := dw.Reader()
1520 for {
1521 e, err := rdr.Next()
1522 if err != nil {
1523 t.Fatalf("error reading DWARF: %v", err)
1524 }
1525 if e == nil {
1526 break
1527 }
1528 if e.Tag != dwarf.TagCompileUnit {
1529 continue
1530 }
1531 lnrdr, err := dw.LineReader(e)
1532 if err != nil {
1533 t.Fatalf("error creating DWARF line reader: %v", err)
1534 }
1535 if lnrdr != nil {
1536 var lne dwarf.LineEntry
1537 for {
1538 err := lnrdr.Next(&lne)
1539 if err == io.EOF {
1540 break
1541 }
1542 if err != nil {
1543 t.Fatalf("error reading next DWARF line: %v", err)
1544 }
1545 if lne.EndSequence {
1546 continue
1547 }
1548 if lne.Address == lastAddr && (lne.File.Name != lastFile || lne.Line != lastLine) {
1549 t.Errorf("address %#x is assigned to both %s:%d and %s:%d", lastAddr, lastFile, lastLine, lne.File.Name, lne.Line)
1550 }
1551 lastAddr = lne.Address
1552 lastFile = lne.File.Name
1553 lastLine = lne.Line
1554 }
1555 }
1556 rdr.SkipChildren()
1557 }
1558 f.Close()
1559 }
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573 func processParams(die *dwarf.Entry, ex *dwtest.Examiner) string {
1574
1575
1576
1577
1578
1579
1580
1581
1582 foundParams := make(map[string]string)
1583
1584
1585 pIdx := ex.IdxFromOffset(die.Offset)
1586 childDies := ex.Children(pIdx)
1587 idx := 0
1588 for _, child := range childDies {
1589 if child.Tag == dwarf.TagFormalParameter {
1590
1591
1592
1593
1594
1595
1596 st := -1
1597 if vp, ok := child.Val(dwarf.AttrVarParam).(bool); ok {
1598 if vp {
1599 st = 2
1600 } else {
1601 st = 1
1602 }
1603 }
1604 if name, ok := child.Val(dwarf.AttrName).(string); ok {
1605 foundParams[name] = fmt.Sprintf("%d:%d", idx, st)
1606 idx++
1607 }
1608 }
1609 }
1610
1611 found := make([]string, 0, len(foundParams))
1612 for k, v := range foundParams {
1613 found = append(found, fmt.Sprintf("%s:%s", k, v))
1614 }
1615 sort.Strings(found)
1616
1617 return fmt.Sprintf("%+v", found)
1618 }
1619
1620 func TestOutputParamAbbrevAndAttr(t *testing.T) {
1621 testenv.MustHaveGoBuild(t)
1622
1623 mustHaveDWARF(t)
1624 t.Parallel()
1625
1626
1627
1628
1629
1630
1631 const prog = `
1632 package main
1633
1634 //go:noinline
1635 func ABC(c1, c2, c3 int, d1, d2, d3, d4 string, f1, f2, f3 float32, g1 [1024]int) (r1 int, r2 int, r3 [1024]int, r4 byte, r5 string, r6 float32) {
1636 g1[0] = 6
1637 r1, r2, r3, r4, r5, r6 = c3, c2+c1, g1, 'a', d1+d2+d3+d4, f1+f2+f3
1638 return
1639 }
1640
1641 func main() {
1642 a := [1024]int{}
1643 v1, v2, v3, v4, v5, v6 := ABC(1, 2, 3, "a", "b", "c", "d", 1.0, 2.0, 1.0, a)
1644 println(v1, v2, v3[0], v4, v5, v6)
1645 }
1646 `
1647 _, ex := gobuildAndExamine(t, prog, NoOpt)
1648
1649 abcdie := findSubprogramDIE(t, ex, "main.ABC")
1650
1651
1652 found := processParams(abcdie, ex)
1653
1654
1655
1656
1657 expected := "[c1:0:1 c2:1:1 c3:2:1 d1:3:1 d2:4:1 d3:5:1 d4:6:1 f1:7:1 f2:8:1 f3:9:1 g1:10:1 r1:11:2 r2:12:2 r3:13:2 r4:14:2 r5:15:2 r6:16:2]"
1658 if found != expected {
1659 t.Errorf("param check failed, wanted:\n%s\ngot:\n%s\n",
1660 expected, found)
1661 }
1662 }
1663
1664 func TestDictIndex(t *testing.T) {
1665
1666
1667
1668 testenv.MustHaveGoBuild(t)
1669
1670 mustHaveDWARF(t)
1671 t.Parallel()
1672
1673 const prog = `
1674 package main
1675
1676 import "fmt"
1677
1678 type CustomInt int
1679
1680 func testfn[T any](arg T) {
1681 var mapvar = make(map[int]T)
1682 mapvar[0] = arg
1683 fmt.Println(arg, mapvar)
1684 }
1685
1686 func main() {
1687 testfn(CustomInt(3))
1688 }
1689 `
1690
1691 dir := t.TempDir()
1692 f := gobuild(t, dir, prog, NoOpt)
1693 defer f.Close()
1694
1695 d, err := f.DWARF()
1696 if err != nil {
1697 t.Fatalf("error reading DWARF: %v", err)
1698 }
1699
1700 rdr := d.Reader()
1701 found := false
1702 for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
1703 if err != nil {
1704 t.Fatalf("error reading DWARF: %v", err)
1705 }
1706 name, _ := entry.Val(dwarf.AttrName).(string)
1707 if strings.HasPrefix(name, "main.testfn") {
1708 found = true
1709 break
1710 }
1711 }
1712
1713 if !found {
1714 t.Fatalf("could not find main.testfn")
1715 }
1716
1717 offs := []dwarf.Offset{}
1718 for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
1719 if err != nil {
1720 t.Fatalf("error reading DWARF: %v", err)
1721 }
1722 if entry.Tag == 0 {
1723 break
1724 }
1725 name, _ := entry.Val(dwarf.AttrName).(string)
1726 switch name {
1727 case "arg", "mapvar":
1728 offs = append(offs, entry.Val(dwarf.AttrType).(dwarf.Offset))
1729 }
1730 }
1731 if len(offs) != 2 {
1732 t.Errorf("wrong number of variables found in main.testfn %d", len(offs))
1733 }
1734 for _, off := range offs {
1735 rdr.Seek(off)
1736 entry, err := rdr.Next()
1737 if err != nil {
1738 t.Fatalf("error reading DWARF: %v", err)
1739 }
1740 if _, ok := entry.Val(intdwarf.DW_AT_go_dict_index).(int64); !ok {
1741 t.Errorf("could not find DW_AT_go_dict_index attribute offset %#x (%T)", off, entry.Val(intdwarf.DW_AT_go_dict_index))
1742 }
1743 }
1744
1745 rdr.Seek(0)
1746 ex := dwtest.Examiner{}
1747 if err := ex.Populate(rdr); err != nil {
1748 t.Fatalf("error reading DWARF: %v", err)
1749 }
1750 for _, typeName := range []string{"main.CustomInt", "map[int]main.CustomInt"} {
1751 dies := ex.Named(typeName)
1752 if len(dies) != 1 {
1753 t.Errorf("wanted 1 DIE named %s, found %v", typeName, len(dies))
1754 }
1755 if dies[0].Val(intdwarf.DW_AT_go_runtime_type).(uint64) == 0 {
1756 t.Errorf("type %s does not have DW_AT_go_runtime_type", typeName)
1757 }
1758 }
1759 }
1760
1761 func TestOptimizedOutParamHandling(t *testing.T) {
1762 testenv.MustHaveGoBuild(t)
1763
1764 mustHaveDWARF(t)
1765 t.Parallel()
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780 const prog = `
1781 package main
1782
1783 // First testcase. All input params in registers, all params used.
1784
1785 //go:noinline
1786 func tc1(p1, p2 int, p3 string) (int, string) {
1787 return p1 + p2, p3 + "foo"
1788 }
1789
1790 // Second testcase. Some params in registers, some on stack.
1791
1792 //go:noinline
1793 func tc2(p1 int, p2 [128]int, p3 string) (int, string, [128]int) {
1794 return p1 + p2[p1], p3 + "foo", [128]int{p1}
1795 }
1796
1797 // Third testcase. Named return params.
1798
1799 //go:noinline
1800 func tc3(p1 int, p2 [128]int, p3 string) (r1 int, r2 bool, r3 string, r4 [128]int) {
1801 if p1 == 101 {
1802 r1 = p1 + p2[p1]
1803 r2 = p3 == "foo"
1804 r4 = [128]int{p1}
1805 return
1806 } else {
1807 return p1 - p2[p1+3], false, "bar", [128]int{p1 + 2}
1808 }
1809 }
1810
1811 // Fourth testcase. Some thing are used, some are unused.
1812
1813 //go:noinline
1814 func tc4(p1, p1un int, p2, p2un [128]int, p3, p3un string) (r1 int, r1un int, r2 bool, r3 string, r4, r4un [128]int) {
1815 if p1 == 101 {
1816 r1 = p1 + p2[p2[0]]
1817 r2 = p3 == "foo"
1818 r4 = [128]int{p1}
1819 return
1820 } else {
1821 return p1, -1, true, "plex", [128]int{p1 + 2}, [128]int{-1}
1822 }
1823 }
1824
1825 func main() {
1826 {
1827 r1, r2 := tc1(3, 4, "five")
1828 println(r1, r2)
1829 }
1830 {
1831 x := [128]int{9}
1832 r1, r2, r3 := tc2(3, x, "five")
1833 println(r1, r2, r3[0])
1834 }
1835 {
1836 x := [128]int{9}
1837 r1, r2, r3, r4 := tc3(3, x, "five")
1838 println(r1, r2, r3, r4[0])
1839 }
1840 {
1841 x := [128]int{3}
1842 y := [128]int{7}
1843 r1, r1u, r2, r3, r4, r4u := tc4(0, 1, x, y, "a", "b")
1844 println(r1, r1u, r2, r3, r4[0], r4u[1])
1845 }
1846
1847 }
1848 `
1849 _, ex := gobuildAndExamine(t, prog, DefaultOpt)
1850
1851 testcases := []struct {
1852 tag string
1853 expected string
1854 }{
1855 {
1856 tag: "tc1",
1857 expected: "[p1:0:1 p2:1:1 p3:2:1 ~r0:3:2 ~r1:4:2]",
1858 },
1859 {
1860 tag: "tc2",
1861 expected: "[p1:0:1 p2:1:1 p3:2:1 ~r0:3:2 ~r1:4:2 ~r2:5:2]",
1862 },
1863 {
1864 tag: "tc3",
1865 expected: "[p1:0:1 p2:1:1 p3:2:1 r1:3:2 r2:4:2 r3:5:2 r4:6:2]",
1866 },
1867 {
1868 tag: "tc4",
1869 expected: "[p1:0:1 p1un:1:1 p2:2:1 p2un:3:1 p3:4:1 p3un:5:1 r1:6:2 r1un:7:2 r2:8:2 r3:9:2 r4:10:2 r4un:11:2]",
1870 },
1871 }
1872
1873 for _, tc := range testcases {
1874
1875 which := fmt.Sprintf("main.%s", tc.tag)
1876 die := findSubprogramDIE(t, ex, which)
1877
1878
1879 foundParams := processParams(die, ex)
1880 if foundParams != tc.expected {
1881 t.Errorf("check failed for testcase %s -- wanted:\n%s\ngot:%s\n",
1882 tc.tag, tc.expected, foundParams)
1883 }
1884 }
1885 }
1886 func TestIssue54320(t *testing.T) {
1887
1888
1889 testenv.MustHaveGoBuild(t)
1890
1891 mustHaveDWARF(t)
1892
1893 t.Parallel()
1894
1895 const prog = `
1896 package main
1897
1898 import "fmt"
1899
1900 func main() {
1901 fmt.Printf("Hello world\n");
1902 }
1903 `
1904
1905 dir := t.TempDir()
1906 f := gobuild(t, dir, prog, "-ldflags=-debugtramp=2")
1907 defer f.Close()
1908
1909 d, err := f.DWARF()
1910 if err != nil {
1911 t.Fatalf("error reading DWARF: %v", err)
1912 }
1913
1914 rdr := d.Reader()
1915 found := false
1916 var entry *dwarf.Entry
1917 for entry, err = rdr.Next(); entry != nil; entry, err = rdr.Next() {
1918 if err != nil {
1919 t.Fatalf("error reading DWARF: %v", err)
1920 }
1921 if entry.Tag != dwarf.TagCompileUnit {
1922 continue
1923 }
1924 name, _ := entry.Val(dwarf.AttrName).(string)
1925 if name == "main" {
1926 found = true
1927 break
1928 }
1929 rdr.SkipChildren()
1930 }
1931
1932 if !found {
1933 t.Fatalf("could not find main compile unit")
1934 }
1935 lr, err := d.LineReader(entry)
1936 if err != nil {
1937 t.Fatalf("error obtaining linereader: %v", err)
1938 }
1939
1940 var le dwarf.LineEntry
1941 found = false
1942 for {
1943 if err := lr.Next(&le); err != nil {
1944 if err == io.EOF {
1945 break
1946 }
1947 t.Fatalf("error reading linentry: %v", err)
1948 }
1949
1950 if le.File == nil {
1951 continue
1952 }
1953 file := filepath.Base(le.File.Name)
1954 if file == "test.go" {
1955 found = true
1956 break
1957 }
1958 }
1959 if !found {
1960 t.Errorf("no LPT entries for test.go")
1961 }
1962 }
1963
1964 const zeroSizedVarProg = `
1965 package main
1966
1967 import (
1968 "fmt"
1969 )
1970
1971 func main() {
1972 zeroSizedVariable := struct{}{}
1973 fmt.Println(zeroSizedVariable)
1974 }
1975 `
1976
1977 func TestZeroSizedVariable(t *testing.T) {
1978 testenv.MustHaveGoBuild(t)
1979
1980 mustHaveDWARF(t)
1981 t.Parallel()
1982
1983 if testing.Short() {
1984 t.Skip("skipping test in short mode.")
1985 }
1986
1987
1988
1989
1990
1991 for _, opt := range []string{NoOpt, DefaultOpt} {
1992 opt := opt
1993 t.Run(opt, func(t *testing.T) {
1994 _, ex := gobuildAndExamine(t, zeroSizedVarProg, opt)
1995
1996
1997 abcs := ex.Named("zeroSizedVariable")
1998 if len(abcs) == 0 {
1999 t.Fatalf("unable to locate DIE for zeroSizedVariable")
2000 }
2001 if len(abcs) != 1 {
2002 t.Fatalf("more than one zeroSizedVariable DIE")
2003 }
2004 })
2005 }
2006 }
2007
2008 func TestConsistentGoKindAndRuntimeType(t *testing.T) {
2009 testenv.MustHaveGoBuild(t)
2010
2011 mustHaveDWARF(t)
2012 t.Parallel()
2013
2014 if testing.Short() {
2015 t.Skip("skipping test in short mode.")
2016 }
2017
2018
2019
2020 _, ex := gobuildAndExamine(t, zeroSizedVarProg, DefaultOpt)
2021
2022
2023 typesChecked := 0
2024 failures := 0
2025 for _, die := range ex.DIEs() {
2026
2027 rtt, hasRT := die.Val(intdwarf.DW_AT_go_runtime_type).(uint64)
2028 if !hasRT || rtt == 0 {
2029 continue
2030 }
2031
2032 if name, _ := die.Val(intdwarf.DW_AT_name).(string); name == "unsafe.Pointer" {
2033 continue
2034 }
2035 typesChecked++
2036
2037 if val, ok := die.Val(intdwarf.DW_AT_go_kind).(int64); !ok || val == 0 {
2038 failures++
2039
2040 if failures <= 10 {
2041 idx := ex.IdxFromOffset(die.Offset)
2042 t.Logf("type DIE has DW_AT_go_runtime_type but invalid DW_AT_go_kind:\n")
2043 ex.DumpEntry(idx, false, 0)
2044 }
2045 t.Errorf("bad type DIE at offset %d\n", die.Offset)
2046 }
2047 }
2048 if typesChecked == 0 {
2049 t.Fatalf("something went wrong, 0 types checked")
2050 } else {
2051 t.Logf("%d types checked\n", typesChecked)
2052 }
2053 }
2054
View as plain text