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