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