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