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
872 if runtime.GOARCH == "ppc64" {
873 t.Skip("-linkmode=external not supported on ppc64")
874 }
875
876 testRuntimeTypeAttr(t, "-ldflags=-linkmode=external")
877 }
878
879 func testRuntimeTypeAttr(t *testing.T, flags string) {
880 t.Parallel()
881
882 const prog = `
883 package main
884
885 import "unsafe"
886
887 type X struct{ _ int }
888
889 func main() {
890 var x interface{} = &X{}
891 p := *(*uintptr)(unsafe.Pointer(&x))
892 print(p)
893 f(nil)
894 }
895 //go:noinline
896 func f(x *X) { // Make sure that there is dwarf recorded for *X.
897 }
898 `
899 dir := t.TempDir()
900
901 f := gobuild(t, dir, prog, flags)
902 defer f.Close()
903
904 out, err := testenv.Command(t, f.path).CombinedOutput()
905 if err != nil {
906 t.Fatalf("could not run test program: %v", err)
907 }
908 addr, err := strconv.ParseUint(string(out), 10, 64)
909 if err != nil {
910 t.Fatalf("could not parse type address from program output %q: %v", out, err)
911 }
912
913 symbols, err := f.Symbols()
914 if err != nil {
915 t.Fatalf("error reading symbols: %v", err)
916 }
917 var types *objfilepkg.Sym
918 for _, sym := range symbols {
919 if sym.Name == "runtime.types" {
920 types = &sym
921 break
922 }
923 }
924 if types == nil {
925 t.Fatal("couldn't find runtime.types in symbols")
926 }
927
928 d, err := f.DWARF()
929 if err != nil {
930 t.Fatalf("error reading DWARF: %v", err)
931 }
932
933 rdr := d.Reader()
934 ex := dwtest.Examiner{}
935 if err := ex.Populate(rdr); err != nil {
936 t.Fatalf("error reading DWARF: %v", err)
937 }
938 dies := ex.Named("*main.X")
939 if len(dies) != 1 {
940 t.Fatalf("wanted 1 DIE named *main.X, found %v", len(dies))
941 }
942 rtAttr := dies[0].Val(intdwarf.DW_AT_go_runtime_type)
943 if rtAttr == nil {
944 t.Fatalf("*main.X DIE had no runtime type attr. DIE: %v", dies[0])
945 }
946
947 if platform.DefaultPIE(runtime.GOOS, runtime.GOARCH, false) {
948 return
949 }
950 if rtAttr.(uint64)+types.Addr != addr {
951 t.Errorf("DWARF type offset was %#x+%#x, but test program said %#x", rtAttr.(uint64), types.Addr, addr)
952 }
953 }
954
955 func TestIssue27614(t *testing.T) {
956
957
958
959 testenv.MustHaveGoBuild(t)
960
961 mustHaveDWARF(t)
962
963 t.Parallel()
964
965 dir := t.TempDir()
966
967 const prog = `package main
968
969 import "fmt"
970
971 type astruct struct {
972 X int
973 }
974
975 type bstruct struct {
976 X float32
977 }
978
979 var globalptr *astruct
980 var globalvar astruct
981 var bvar0, bvar1, bvar2 bstruct
982
983 func main() {
984 fmt.Println(globalptr, globalvar, bvar0, bvar1, bvar2)
985 }
986 `
987
988 f := gobuild(t, dir, prog, NoOpt)
989
990 defer f.Close()
991
992 data, err := f.DWARF()
993 if err != nil {
994 t.Fatal(err)
995 }
996
997 rdr := data.Reader()
998
999 var astructTypeDIE, bstructTypeDIE, ptrastructTypeDIE *dwarf.Entry
1000 var globalptrDIE, globalvarDIE *dwarf.Entry
1001 var bvarDIE [3]*dwarf.Entry
1002
1003 for {
1004 e, err := rdr.Next()
1005 if err != nil {
1006 t.Fatal(err)
1007 }
1008 if e == nil {
1009 break
1010 }
1011
1012 name, _ := e.Val(dwarf.AttrName).(string)
1013
1014 switch e.Tag {
1015 case dwarf.TagTypedef:
1016 switch name {
1017 case "main.astruct":
1018 astructTypeDIE = e
1019 case "main.bstruct":
1020 bstructTypeDIE = e
1021 }
1022 case dwarf.TagPointerType:
1023 if name == "*main.astruct" {
1024 ptrastructTypeDIE = e
1025 }
1026 case dwarf.TagVariable:
1027 switch name {
1028 case "main.globalptr":
1029 globalptrDIE = e
1030 case "main.globalvar":
1031 globalvarDIE = e
1032 default:
1033 const bvarprefix = "main.bvar"
1034 if strings.HasPrefix(name, bvarprefix) {
1035 i, _ := strconv.Atoi(name[len(bvarprefix):])
1036 bvarDIE[i] = e
1037 }
1038 }
1039 }
1040 }
1041
1042 typedieof := func(e *dwarf.Entry) dwarf.Offset {
1043 return e.Val(dwarf.AttrType).(dwarf.Offset)
1044 }
1045
1046 if off := typedieof(ptrastructTypeDIE); off != astructTypeDIE.Offset {
1047 t.Errorf("type attribute of *main.astruct references %#x, not main.astruct DIE at %#x\n", off, astructTypeDIE.Offset)
1048 }
1049
1050 if off := typedieof(globalptrDIE); off != ptrastructTypeDIE.Offset {
1051 t.Errorf("type attribute of main.globalptr references %#x, not *main.astruct DIE at %#x\n", off, ptrastructTypeDIE.Offset)
1052 }
1053
1054 if off := typedieof(globalvarDIE); off != astructTypeDIE.Offset {
1055 t.Errorf("type attribute of main.globalvar1 references %#x, not main.astruct DIE at %#x\n", off, astructTypeDIE.Offset)
1056 }
1057
1058 for i := range bvarDIE {
1059 if off := typedieof(bvarDIE[i]); off != bstructTypeDIE.Offset {
1060 t.Errorf("type attribute of main.bvar%d references %#x, not main.bstruct DIE at %#x\n", i, off, bstructTypeDIE.Offset)
1061 }
1062 }
1063 }
1064
1065 func TestStaticTmp(t *testing.T) {
1066
1067
1068
1069
1070
1071 testenv.MustHaveGoBuild(t)
1072
1073 mustHaveDWARF(t)
1074
1075 t.Parallel()
1076
1077 dir := t.TempDir()
1078
1079 const prog = `package main
1080
1081 var stmp_0 string
1082 var a []int
1083
1084 func init() {
1085 a = []int{ 7 }
1086 }
1087
1088 func main() {
1089 println(a[0])
1090 }
1091 `
1092
1093 f := gobuild(t, dir, prog, NoOpt)
1094
1095 defer f.Close()
1096
1097 d, err := f.DWARF()
1098 if err != nil {
1099 t.Fatalf("error reading DWARF: %v", err)
1100 }
1101
1102 rdr := d.Reader()
1103 for {
1104 e, err := rdr.Next()
1105 if err != nil {
1106 t.Fatal(err)
1107 }
1108 if e == nil {
1109 break
1110 }
1111 if e.Tag != dwarf.TagVariable {
1112 continue
1113 }
1114 name, ok := e.Val(dwarf.AttrName).(string)
1115 if !ok {
1116 continue
1117 }
1118 if strings.Contains(name, "stmp") {
1119 t.Errorf("statictmp variable found in debug_info: %s at %x", name, e.Offset)
1120 }
1121 }
1122
1123
1124
1125
1126
1127
1128 if !testenv.CanInternalLink(false) {
1129 return
1130 }
1131
1132 syms, err := f.Symbols()
1133 if err != nil {
1134 t.Fatalf("error reading symbols: %v", err)
1135 }
1136 for _, sym := range syms {
1137 if strings.Contains(sym.Name, "stmp") {
1138 t.Errorf("statictmp variable found in symbol table: %s", sym.Name)
1139 }
1140 }
1141 }
1142
1143 func TestPackageNameAttr(t *testing.T) {
1144 const dwarfAttrGoPackageName = dwarf.Attr(0x2905)
1145 const dwarfGoLanguage = 22
1146
1147 testenv.MustHaveGoBuild(t)
1148
1149 mustHaveDWARF(t)
1150
1151 t.Parallel()
1152
1153 dir := t.TempDir()
1154
1155 const prog = "package main\nfunc main() {\nprintln(\"hello world\")\n}\n"
1156
1157 f := gobuild(t, dir, prog, NoOpt)
1158
1159 defer f.Close()
1160
1161 d, err := f.DWARF()
1162 if err != nil {
1163 t.Fatalf("error reading DWARF: %v", err)
1164 }
1165
1166 rdr := d.Reader()
1167 runtimeUnitSeen := false
1168 for {
1169 e, err := rdr.Next()
1170 if err != nil {
1171 t.Fatal(err)
1172 }
1173 if e == nil {
1174 break
1175 }
1176 if e.Tag != dwarf.TagCompileUnit {
1177 continue
1178 }
1179 if lang, _ := e.Val(dwarf.AttrLanguage).(int64); lang != dwarfGoLanguage {
1180 continue
1181 }
1182
1183 pn, ok := e.Val(dwarfAttrGoPackageName).(string)
1184 if !ok {
1185 name, _ := e.Val(dwarf.AttrName).(string)
1186 t.Errorf("found compile unit without package name: %s", name)
1187
1188 }
1189 if pn == "" {
1190 name, _ := e.Val(dwarf.AttrName).(string)
1191 t.Errorf("found compile unit with empty package name: %s", name)
1192 } else {
1193 if pn == "runtime" {
1194 runtimeUnitSeen = true
1195 }
1196 }
1197 }
1198
1199
1200 if !runtimeUnitSeen {
1201 t.Errorf("no package name for runtime unit")
1202 }
1203 }
1204
1205 func TestMachoIssue32233(t *testing.T) {
1206 testenv.MustHaveGoBuild(t)
1207 testenv.MustHaveCGO(t)
1208
1209 if runtime.GOOS != "darwin" {
1210 t.Skip("skipping; test only interesting on darwin")
1211 }
1212
1213 f := gobuildTestdata(t, "testdata/issue32233/main", DefaultOpt)
1214 f.Close()
1215 }
1216
1217 func TestWindowsIssue36495(t *testing.T) {
1218 testenv.MustHaveGoBuild(t)
1219 if runtime.GOOS != "windows" {
1220 t.Skip("skipping: test only on windows")
1221 }
1222
1223 dir := t.TempDir()
1224
1225 prog := `
1226 package main
1227
1228 import "fmt"
1229
1230 func main() {
1231 fmt.Println("Hello World")
1232 }`
1233 f := gobuild(t, dir, prog, NoOpt)
1234 defer f.Close()
1235 exe, err := pe.Open(f.path)
1236 if err != nil {
1237 t.Fatalf("error opening pe file: %v", err)
1238 }
1239 defer exe.Close()
1240 dw, err := exe.DWARF()
1241 if err != nil {
1242 t.Fatalf("error parsing DWARF: %v", err)
1243 }
1244 rdr := dw.Reader()
1245 for {
1246 e, err := rdr.Next()
1247 if err != nil {
1248 t.Fatalf("error reading DWARF: %v", err)
1249 }
1250 if e == nil {
1251 break
1252 }
1253 if e.Tag != dwarf.TagCompileUnit {
1254 continue
1255 }
1256 lnrdr, err := dw.LineReader(e)
1257 if err != nil {
1258 t.Fatalf("error creating DWARF line reader: %v", err)
1259 }
1260 if lnrdr != nil {
1261 var lne dwarf.LineEntry
1262 for {
1263 err := lnrdr.Next(&lne)
1264 if err == io.EOF {
1265 break
1266 }
1267 if err != nil {
1268 t.Fatalf("error reading next DWARF line: %v", err)
1269 }
1270 if strings.Contains(lne.File.Name, `\`) {
1271 t.Errorf("filename should not contain backslash: %v", lne.File.Name)
1272 }
1273 }
1274 }
1275 rdr.SkipChildren()
1276 }
1277 }
1278
1279 func TestIssue38192(t *testing.T) {
1280 testenv.MustHaveGoBuild(t)
1281
1282 mustHaveDWARF(t)
1283
1284 t.Parallel()
1285
1286
1287
1288 f := gobuildTestdata(t, "testdata/issue38192", DefaultOpt)
1289 defer f.Close()
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305 rows := []dwarf.LineEntry{}
1306 dw, err := f.DWARF()
1307 if err != nil {
1308 t.Fatalf("error parsing DWARF: %v", err)
1309 }
1310 rdr := dw.Reader()
1311 for {
1312 e, err := rdr.Next()
1313 if err != nil {
1314 t.Fatalf("error reading DWARF: %v", err)
1315 }
1316 if e == nil {
1317 break
1318 }
1319 if e.Tag != dwarf.TagCompileUnit {
1320 continue
1321 }
1322
1323 name := e.Val(dwarf.AttrName).(string)
1324 if name != "main" {
1325 continue
1326 }
1327 lnrdr, err := dw.LineReader(e)
1328 if err != nil {
1329 t.Fatalf("error creating DWARF line reader: %v", err)
1330 }
1331 if lnrdr != nil {
1332 var lne dwarf.LineEntry
1333 for {
1334 err := lnrdr.Next(&lne)
1335 if err == io.EOF {
1336 break
1337 }
1338 if err != nil {
1339 t.Fatalf("error reading next DWARF line: %v", err)
1340 }
1341 if !strings.HasSuffix(lne.File.Name, "ld/testdata/issue38192/oneline.s") {
1342 continue
1343 }
1344 rows = append(rows, lne)
1345 }
1346 }
1347 rdr.SkipChildren()
1348 }
1349 f.Close()
1350
1351
1352
1353
1354
1355
1356 pcs := make(map[uint64]bool)
1357 line8seen := false
1358 for _, r := range rows {
1359 pcs[r.Address] = true
1360 if r.Line == 8 {
1361 line8seen = true
1362 }
1363 }
1364 failed := false
1365 if len(pcs) < 2 {
1366 failed = true
1367 t.Errorf("not enough line table rows for main.singleInstruction (got %d, wanted > 1", len(pcs))
1368 }
1369 if !line8seen {
1370 failed = true
1371 t.Errorf("line table does not contain correct line for main.singleInstruction")
1372 }
1373 if !failed {
1374 return
1375 }
1376 for i, r := range rows {
1377 t.Logf("row %d: A=%x F=%s L=%d\n", i, r.Address, r.File.Name, r.Line)
1378 }
1379 }
1380
1381 func TestIssue39757(t *testing.T) {
1382 testenv.MustHaveGoBuild(t)
1383
1384 mustHaveDWARF(t)
1385
1386 t.Parallel()
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400 f := gobuildTestdata(t, "testdata/issue39757", DefaultOpt)
1401 defer f.Close()
1402
1403 syms, err := f.Symbols()
1404 if err != nil {
1405 t.Fatal(err)
1406 }
1407
1408 var addr uint64
1409 for _, sym := range syms {
1410 if sym.Name == "main.main" {
1411 addr = sym.Addr
1412 break
1413 }
1414 }
1415 if addr == 0 {
1416 t.Fatal("cannot find main.main in symbols")
1417 }
1418
1419
1420
1421
1422
1423 dw, err := f.DWARF()
1424 if err != nil {
1425 t.Fatalf("error parsing DWARF: %v", err)
1426 }
1427 rdr := dw.Reader()
1428 ex := &dwtest.Examiner{}
1429 if err := ex.Populate(rdr); err != nil {
1430 t.Fatalf("error reading DWARF: %v", err)
1431 }
1432
1433 maindie := findSubprogramDIE(t, ex, "main.main")
1434
1435
1436
1437
1438
1439 lowpc, highpc, perr := dwtest.SubprogLoAndHighPc(maindie)
1440 if perr != nil {
1441 t.Fatalf("main.main DIE malformed: %v", perr)
1442 }
1443 t.Logf("lo=0x%x hi=0x%x\n", lowpc, highpc)
1444
1445
1446 mainIdx := ex.IdxFromOffset(maindie.Offset)
1447 cuentry := ex.Parent(mainIdx)
1448 if cuentry == nil {
1449 t.Fatalf("main.main DIE appears orphaned")
1450 }
1451 lnrdr, lerr := dw.LineReader(cuentry)
1452 if lerr != nil {
1453 t.Fatalf("error creating DWARF line reader: %v", err)
1454 }
1455 if lnrdr == nil {
1456 t.Fatalf("no line table for main.main compilation unit")
1457 }
1458 rows := []dwarf.LineEntry{}
1459 mainrows := 0
1460 var lne dwarf.LineEntry
1461 for {
1462 err := lnrdr.Next(&lne)
1463 if err == io.EOF {
1464 break
1465 }
1466 rows = append(rows, lne)
1467 if err != nil {
1468 t.Fatalf("error reading next DWARF line: %v", err)
1469 }
1470 if lne.Address < lowpc || lne.Address > highpc {
1471 continue
1472 }
1473 if !strings.HasSuffix(lne.File.Name, "issue39757main.go") {
1474 t.Errorf("found row with file=%s (not issue39757main.go)", lne.File.Name)
1475 }
1476 mainrows++
1477 }
1478 f.Close()
1479
1480
1481 if mainrows < 3 {
1482 t.Errorf("not enough line table rows for main.main (got %d, wanted > 3", mainrows)
1483 for i, r := range rows {
1484 t.Logf("row %d: A=%x F=%s L=%d\n", i, r.Address, r.File.Name, r.Line)
1485 }
1486 }
1487 }
1488
1489 func TestIssue42484(t *testing.T) {
1490 testenv.MustHaveGoBuild(t)
1491
1492
1493
1494
1495 testenv.MustInternalLink(t, testenv.NoSpecialBuildTypes)
1496
1497 mustHaveDWARF(t)
1498
1499 t.Parallel()
1500
1501 f := gobuildTestdata(t, "testdata/issue42484", NoOpt)
1502
1503 var lastAddr uint64
1504 var lastFile string
1505 var lastLine int
1506
1507 dw, err := f.DWARF()
1508 if err != nil {
1509 t.Fatalf("error parsing DWARF: %v", err)
1510 }
1511 rdr := dw.Reader()
1512 for {
1513 e, err := rdr.Next()
1514 if err != nil {
1515 t.Fatalf("error reading DWARF: %v", err)
1516 }
1517 if e == nil {
1518 break
1519 }
1520 if e.Tag != dwarf.TagCompileUnit {
1521 continue
1522 }
1523 lnrdr, err := dw.LineReader(e)
1524 if err != nil {
1525 t.Fatalf("error creating DWARF line reader: %v", err)
1526 }
1527 if lnrdr != nil {
1528 var lne dwarf.LineEntry
1529 for {
1530 err := lnrdr.Next(&lne)
1531 if err == io.EOF {
1532 break
1533 }
1534 if err != nil {
1535 t.Fatalf("error reading next DWARF line: %v", err)
1536 }
1537 if lne.EndSequence {
1538 continue
1539 }
1540 if lne.Address == lastAddr && (lne.File.Name != lastFile || lne.Line != lastLine) {
1541 t.Errorf("address %#x is assigned to both %s:%d and %s:%d", lastAddr, lastFile, lastLine, lne.File.Name, lne.Line)
1542 }
1543 lastAddr = lne.Address
1544 lastFile = lne.File.Name
1545 lastLine = lne.Line
1546 }
1547 }
1548 rdr.SkipChildren()
1549 }
1550 f.Close()
1551 }
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565 func processParams(die *dwarf.Entry, ex *dwtest.Examiner) string {
1566
1567
1568
1569
1570
1571
1572
1573
1574 foundParams := make(map[string]string)
1575
1576
1577 pIdx := ex.IdxFromOffset(die.Offset)
1578 childDies := ex.Children(pIdx)
1579 idx := 0
1580 for _, child := range childDies {
1581 if child.Tag == dwarf.TagFormalParameter {
1582
1583
1584
1585
1586
1587
1588 st := -1
1589 if vp, ok := child.Val(dwarf.AttrVarParam).(bool); ok {
1590 if vp {
1591 st = 2
1592 } else {
1593 st = 1
1594 }
1595 }
1596 if name, ok := child.Val(dwarf.AttrName).(string); ok {
1597 foundParams[name] = fmt.Sprintf("%d:%d", idx, st)
1598 idx++
1599 }
1600 }
1601 }
1602
1603 found := make([]string, 0, len(foundParams))
1604 for k, v := range foundParams {
1605 found = append(found, fmt.Sprintf("%s:%s", k, v))
1606 }
1607 sort.Strings(found)
1608
1609 return fmt.Sprintf("%+v", found)
1610 }
1611
1612 func TestOutputParamAbbrevAndAttr(t *testing.T) {
1613 testenv.MustHaveGoBuild(t)
1614
1615 mustHaveDWARF(t)
1616 t.Parallel()
1617
1618
1619
1620
1621
1622
1623 const prog = `
1624 package main
1625
1626 //go:noinline
1627 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) {
1628 g1[0] = 6
1629 r1, r2, r3, r4, r5, r6 = c3, c2+c1, g1, 'a', d1+d2+d3+d4, f1+f2+f3
1630 return
1631 }
1632
1633 func main() {
1634 a := [1024]int{}
1635 v1, v2, v3, v4, v5, v6 := ABC(1, 2, 3, "a", "b", "c", "d", 1.0, 2.0, 1.0, a)
1636 println(v1, v2, v3[0], v4, v5, v6)
1637 }
1638 `
1639 _, ex := gobuildAndExamine(t, prog, NoOpt)
1640
1641 abcdie := findSubprogramDIE(t, ex, "main.ABC")
1642
1643
1644 found := processParams(abcdie, ex)
1645
1646
1647
1648
1649 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]"
1650 if found != expected {
1651 t.Errorf("param check failed, wanted:\n%s\ngot:\n%s\n",
1652 expected, found)
1653 }
1654 }
1655
1656 func TestDictIndex(t *testing.T) {
1657
1658
1659
1660 testenv.MustHaveGoBuild(t)
1661
1662 mustHaveDWARF(t)
1663 t.Parallel()
1664
1665 const prog = `
1666 package main
1667
1668 import "fmt"
1669
1670 type CustomInt int
1671
1672 func testfn[T any](arg T) {
1673 var mapvar = make(map[int]T)
1674 mapvar[0] = arg
1675 fmt.Println(arg, mapvar)
1676 }
1677
1678 func main() {
1679 testfn(CustomInt(3))
1680 }
1681 `
1682
1683 dir := t.TempDir()
1684 f := gobuild(t, dir, prog, NoOpt)
1685 defer f.Close()
1686
1687 d, err := f.DWARF()
1688 if err != nil {
1689 t.Fatalf("error reading DWARF: %v", err)
1690 }
1691
1692 rdr := d.Reader()
1693 found := false
1694 for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
1695 if err != nil {
1696 t.Fatalf("error reading DWARF: %v", err)
1697 }
1698 name, _ := entry.Val(dwarf.AttrName).(string)
1699 if strings.HasPrefix(name, "main.testfn") {
1700 found = true
1701 break
1702 }
1703 }
1704
1705 if !found {
1706 t.Fatalf("could not find main.testfn")
1707 }
1708
1709 offs := []dwarf.Offset{}
1710 for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
1711 if err != nil {
1712 t.Fatalf("error reading DWARF: %v", err)
1713 }
1714 if entry.Tag == 0 {
1715 break
1716 }
1717 name, _ := entry.Val(dwarf.AttrName).(string)
1718 switch name {
1719 case "arg", "mapvar":
1720 offs = append(offs, entry.Val(dwarf.AttrType).(dwarf.Offset))
1721 }
1722 }
1723 if len(offs) != 2 {
1724 t.Errorf("wrong number of variables found in main.testfn %d", len(offs))
1725 }
1726 for _, off := range offs {
1727 rdr.Seek(off)
1728 entry, err := rdr.Next()
1729 if err != nil {
1730 t.Fatalf("error reading DWARF: %v", err)
1731 }
1732 if _, ok := entry.Val(intdwarf.DW_AT_go_dict_index).(int64); !ok {
1733 t.Errorf("could not find DW_AT_go_dict_index attribute offset %#x (%T)", off, entry.Val(intdwarf.DW_AT_go_dict_index))
1734 }
1735 }
1736
1737 rdr.Seek(0)
1738 ex := dwtest.Examiner{}
1739 if err := ex.Populate(rdr); err != nil {
1740 t.Fatalf("error reading DWARF: %v", err)
1741 }
1742 for _, typeName := range []string{"main.CustomInt", "map[int]main.CustomInt"} {
1743 dies := ex.Named(typeName)
1744 if len(dies) != 1 {
1745 t.Errorf("wanted 1 DIE named %s, found %v", typeName, len(dies))
1746 }
1747 if dies[0].Val(intdwarf.DW_AT_go_runtime_type).(uint64) == 0 {
1748 t.Errorf("type %s does not have DW_AT_go_runtime_type", typeName)
1749 }
1750 }
1751 }
1752
1753 func TestOptimizedOutParamHandling(t *testing.T) {
1754 testenv.MustHaveGoBuild(t)
1755
1756 mustHaveDWARF(t)
1757 t.Parallel()
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772 const prog = `
1773 package main
1774
1775 // First testcase. All input params in registers, all params used.
1776
1777 //go:noinline
1778 func tc1(p1, p2 int, p3 string) (int, string) {
1779 return p1 + p2, p3 + "foo"
1780 }
1781
1782 // Second testcase. Some params in registers, some on stack.
1783
1784 //go:noinline
1785 func tc2(p1 int, p2 [128]int, p3 string) (int, string, [128]int) {
1786 return p1 + p2[p1], p3 + "foo", [128]int{p1}
1787 }
1788
1789 // Third testcase. Named return params.
1790
1791 //go:noinline
1792 func tc3(p1 int, p2 [128]int, p3 string) (r1 int, r2 bool, r3 string, r4 [128]int) {
1793 if p1 == 101 {
1794 r1 = p1 + p2[p1]
1795 r2 = p3 == "foo"
1796 r4 = [128]int{p1}
1797 return
1798 } else {
1799 return p1 - p2[p1+3], false, "bar", [128]int{p1 + 2}
1800 }
1801 }
1802
1803 // Fourth testcase. Some thing are used, some are unused.
1804
1805 //go:noinline
1806 func tc4(p1, p1un int, p2, p2un [128]int, p3, p3un string) (r1 int, r1un int, r2 bool, r3 string, r4, r4un [128]int) {
1807 if p1 == 101 {
1808 r1 = p1 + p2[p2[0]]
1809 r2 = p3 == "foo"
1810 r4 = [128]int{p1}
1811 return
1812 } else {
1813 return p1, -1, true, "plex", [128]int{p1 + 2}, [128]int{-1}
1814 }
1815 }
1816
1817 func main() {
1818 {
1819 r1, r2 := tc1(3, 4, "five")
1820 println(r1, r2)
1821 }
1822 {
1823 x := [128]int{9}
1824 r1, r2, r3 := tc2(3, x, "five")
1825 println(r1, r2, r3[0])
1826 }
1827 {
1828 x := [128]int{9}
1829 r1, r2, r3, r4 := tc3(3, x, "five")
1830 println(r1, r2, r3, r4[0])
1831 }
1832 {
1833 x := [128]int{3}
1834 y := [128]int{7}
1835 r1, r1u, r2, r3, r4, r4u := tc4(0, 1, x, y, "a", "b")
1836 println(r1, r1u, r2, r3, r4[0], r4u[1])
1837 }
1838
1839 }
1840 `
1841 _, ex := gobuildAndExamine(t, prog, DefaultOpt)
1842
1843 testcases := []struct {
1844 tag string
1845 expected string
1846 }{
1847 {
1848 tag: "tc1",
1849 expected: "[p1:0:1 p2:1:1 p3:2:1 ~r0:3:2 ~r1:4:2]",
1850 },
1851 {
1852 tag: "tc2",
1853 expected: "[p1:0:1 p2:1:1 p3:2:1 ~r0:3:2 ~r1:4:2 ~r2:5:2]",
1854 },
1855 {
1856 tag: "tc3",
1857 expected: "[p1:0:1 p2:1:1 p3:2:1 r1:3:2 r2:4:2 r3:5:2 r4:6:2]",
1858 },
1859 {
1860 tag: "tc4",
1861 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]",
1862 },
1863 }
1864
1865 for _, tc := range testcases {
1866
1867 which := fmt.Sprintf("main.%s", tc.tag)
1868 die := findSubprogramDIE(t, ex, which)
1869
1870
1871 foundParams := processParams(die, ex)
1872 if foundParams != tc.expected {
1873 t.Errorf("check failed for testcase %s -- wanted:\n%s\ngot:%s\n",
1874 tc.tag, tc.expected, foundParams)
1875 }
1876 }
1877 }
1878 func TestIssue54320(t *testing.T) {
1879
1880
1881 testenv.MustHaveGoBuild(t)
1882
1883 mustHaveDWARF(t)
1884
1885 t.Parallel()
1886
1887 const prog = `
1888 package main
1889
1890 import "fmt"
1891
1892 func main() {
1893 fmt.Printf("Hello world\n");
1894 }
1895 `
1896
1897 dir := t.TempDir()
1898 f := gobuild(t, dir, prog, "-ldflags=-debugtramp=2")
1899 defer f.Close()
1900
1901 d, err := f.DWARF()
1902 if err != nil {
1903 t.Fatalf("error reading DWARF: %v", err)
1904 }
1905
1906 rdr := d.Reader()
1907 found := false
1908 var entry *dwarf.Entry
1909 for entry, err = rdr.Next(); entry != nil; entry, err = rdr.Next() {
1910 if err != nil {
1911 t.Fatalf("error reading DWARF: %v", err)
1912 }
1913 if entry.Tag != dwarf.TagCompileUnit {
1914 continue
1915 }
1916 name, _ := entry.Val(dwarf.AttrName).(string)
1917 if name == "main" {
1918 found = true
1919 break
1920 }
1921 rdr.SkipChildren()
1922 }
1923
1924 if !found {
1925 t.Fatalf("could not find main compile unit")
1926 }
1927 lr, err := d.LineReader(entry)
1928 if err != nil {
1929 t.Fatalf("error obtaining linereader: %v", err)
1930 }
1931
1932 var le dwarf.LineEntry
1933 found = false
1934 for {
1935 if err := lr.Next(&le); err != nil {
1936 if err == io.EOF {
1937 break
1938 }
1939 t.Fatalf("error reading linentry: %v", err)
1940 }
1941
1942 if le.File == nil {
1943 continue
1944 }
1945 file := filepath.Base(le.File.Name)
1946 if file == "test.go" {
1947 found = true
1948 break
1949 }
1950 }
1951 if !found {
1952 t.Errorf("no LPT entries for test.go")
1953 }
1954 }
1955
1956 const zeroSizedVarProg = `
1957 package main
1958
1959 import (
1960 "fmt"
1961 )
1962
1963 func main() {
1964 zeroSizedVariable := struct{}{}
1965 fmt.Println(zeroSizedVariable)
1966 }
1967 `
1968
1969 func TestZeroSizedVariable(t *testing.T) {
1970 testenv.MustHaveGoBuild(t)
1971
1972 mustHaveDWARF(t)
1973 t.Parallel()
1974
1975 if testing.Short() {
1976 t.Skip("skipping test in short mode.")
1977 }
1978
1979
1980
1981
1982
1983 for _, opt := range []string{NoOpt, DefaultOpt} {
1984 t.Run(opt, func(t *testing.T) {
1985 _, ex := gobuildAndExamine(t, zeroSizedVarProg, opt)
1986
1987
1988 abcs := ex.Named("zeroSizedVariable")
1989 if len(abcs) == 0 {
1990 t.Fatalf("unable to locate DIE for zeroSizedVariable")
1991 }
1992 if len(abcs) != 1 {
1993 t.Fatalf("more than one zeroSizedVariable DIE")
1994 }
1995 })
1996 }
1997 }
1998
1999 func TestConsistentGoKindAndRuntimeType(t *testing.T) {
2000 testenv.MustHaveGoBuild(t)
2001
2002 mustHaveDWARF(t)
2003 t.Parallel()
2004
2005 if testing.Short() {
2006 t.Skip("skipping test in short mode.")
2007 }
2008
2009
2010
2011 _, ex := gobuildAndExamine(t, zeroSizedVarProg, DefaultOpt)
2012
2013
2014 typesChecked := 0
2015 failures := 0
2016 for _, die := range ex.DIEs() {
2017
2018 rtt, hasRT := die.Val(intdwarf.DW_AT_go_runtime_type).(uint64)
2019 if !hasRT || rtt == 0 {
2020 continue
2021 }
2022
2023 if name, _ := die.Val(intdwarf.DW_AT_name).(string); name == "unsafe.Pointer" {
2024 continue
2025 }
2026 typesChecked++
2027
2028 if val, ok := die.Val(intdwarf.DW_AT_go_kind).(int64); !ok || val == 0 {
2029 failures++
2030
2031 if failures <= 10 {
2032 idx := ex.IdxFromOffset(die.Offset)
2033 t.Logf("type DIE has DW_AT_go_runtime_type but invalid DW_AT_go_kind:\n")
2034 ex.DumpEntry(idx, false, 0)
2035 }
2036 t.Errorf("bad type DIE at offset %d\n", die.Offset)
2037 }
2038 }
2039 if typesChecked == 0 {
2040 t.Fatalf("something went wrong, 0 types checked")
2041 } else {
2042 t.Logf("%d types checked\n", typesChecked)
2043 }
2044 }
2045
View as plain text