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