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 tc := tc
412 t.Run(tc.name, func(t *testing.T) {
413 t.Parallel()
414
415 d, ex := gobuildAndExamine(t, tc.prog, NoOpt)
416
417 maindie := findSubprogramDIE(t, ex, "main.main")
418
419 mainIdx := ex.IdxFromOffset(maindie.Offset)
420
421 fileIdx, fileIdxOK := maindie.Val(dwarf.AttrDeclFile).(int64)
422 if !fileIdxOK {
423 t.Errorf("missing or invalid DW_AT_decl_file for main")
424 }
425 file, err := ex.FileRef(d, mainIdx, fileIdx)
426 if err != nil {
427 t.Fatalf("FileRef: %v", err)
428 }
429 base := filepath.Base(file)
430 if base != tc.file {
431 t.Errorf("DW_AT_decl_file for main is %v, want %v", base, tc.file)
432 }
433
434 line, lineOK := maindie.Val(dwarf.AttrDeclLine).(int64)
435 if !lineOK {
436 t.Errorf("missing or invalid DW_AT_decl_line for main")
437 }
438 if line != tc.line {
439 t.Errorf("DW_AT_decl_line for main is %v, want %d", line, tc.line)
440 }
441 })
442 }
443 }
444
445 func TestVarDeclLine(t *testing.T) {
446 testenv.MustHaveGoBuild(t)
447 t.Parallel()
448
449 mustHaveDWARF(t)
450
451 const prog = `package main
452 %s
453 func main() {
454
455 var i int
456 i = i
457 }
458 `
459 tests := []struct {
460 name string
461 prog string
462 line int64
463 }{
464 {
465 name: "normal",
466 prog: fmt.Sprintf(prog, ""),
467 line: 5,
468 },
469 {
470 name: "line-directive",
471 prog: fmt.Sprintf(prog, "//line /foobar.go:200"),
472 line: 202,
473 },
474 }
475 for _, tc := range tests {
476 tc := tc
477 t.Run(tc.name, func(t *testing.T) {
478 t.Parallel()
479
480 _, ex := gobuildAndExamine(t, tc.prog, NoOpt)
481
482 maindie := findSubprogramDIE(t, ex, "main.main")
483
484 mainIdx := ex.IdxFromOffset(maindie.Offset)
485 childDies := ex.Children(mainIdx)
486 var iEntry *dwarf.Entry
487 for _, child := range childDies {
488 if child.Tag == dwarf.TagVariable && child.Val(dwarf.AttrName).(string) == "i" {
489 iEntry = child
490 break
491 }
492 }
493 if iEntry == nil {
494 t.Fatalf("didn't find DW_TAG_variable for i in main.main")
495 }
496
497
498 line, lineOK := iEntry.Val(dwarf.AttrDeclLine).(int64)
499 if !lineOK {
500 t.Errorf("missing or invalid DW_AT_decl_line for i")
501 }
502 if line != tc.line {
503 t.Errorf("DW_AT_decl_line for i is %v, want %d", line, tc.line)
504 }
505 })
506 }
507 }
508
509
510
511 func TestInlinedRoutineCallFileLine(t *testing.T) {
512 testenv.MustHaveGoBuild(t)
513
514 mustHaveDWARF(t)
515
516 t.Parallel()
517
518 const prog = `
519 package main
520
521 var G int
522
523 //go:noinline
524 func notinlined() int {
525 return 42
526 }
527
528 func inlined() int {
529 return notinlined()
530 }
531
532 %s
533 func main() {
534 x := inlined()
535 G = x
536 }
537 `
538 tests := []struct {
539 name string
540 prog string
541 file string
542 line int64
543 }{
544 {
545 name: "normal",
546 prog: fmt.Sprintf(prog, ""),
547 file: "test.go",
548 line: 17,
549 },
550 {
551 name: "line-directive",
552 prog: fmt.Sprintf(prog, "//line /foobar.go:200"),
553 file: "foobar.go",
554 line: 201,
555 },
556 }
557 for _, tc := range tests {
558 tc := tc
559 t.Run(tc.name, func(t *testing.T) {
560 t.Parallel()
561
562
563
564
565
566
567 d, ex := gobuildAndExamine(t, tc.prog, OptInl4)
568
569 maindie := findSubprogramDIE(t, ex, "main.main")
570
571
572 mainIdx := ex.IdxFromOffset(maindie.Offset)
573 childDies := ex.Children(mainIdx)
574 found := false
575 for _, child := range childDies {
576 if child.Tag != dwarf.TagInlinedSubroutine {
577 continue
578 }
579
580
581 if found {
582 t.Fatalf("Found multiple inlined subroutines, expect only one")
583 }
584 found = true
585
586
587 ooff, originOK := child.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
588 if !originOK {
589 t.Fatalf("no abstract origin attr for inlined subroutine at offset %v", child.Offset)
590 }
591 originDIE := ex.EntryFromOffset(ooff)
592 if originDIE == nil {
593 t.Fatalf("can't locate origin DIE at off %v", ooff)
594 }
595
596
597 name, ok := originDIE.Val(dwarf.AttrName).(string)
598 if !ok {
599 t.Fatalf("no name attr for inlined subroutine at offset %v", child.Offset)
600 }
601 if name != "main.inlined" {
602 t.Fatalf("expected inlined routine %s got %s", "main.cand", name)
603 }
604
605
606
607
608
609
610 cf, cfOK := child.Val(dwarf.AttrCallFile).(int64)
611 if !cfOK {
612 t.Fatalf("no call_file attr for inlined subroutine at offset %v", child.Offset)
613 }
614 file, err := ex.FileRef(d, mainIdx, cf)
615 if err != nil {
616 t.Errorf("FileRef: %v", err)
617 continue
618 }
619 base := filepath.Base(file)
620 if base != tc.file {
621 t.Errorf("bad call_file attribute, found '%s', want '%s'",
622 file, tc.file)
623 }
624
625
626
627 cl, clOK := child.Val(dwarf.AttrCallLine).(int64)
628 if !clOK {
629 t.Fatalf("no call_line attr for inlined subroutine at offset %v", child.Offset)
630 }
631 if cl != tc.line {
632 t.Errorf("bad call_line attribute, found %d, want %d", cl, tc.line)
633 }
634 }
635 if !found {
636 t.Fatalf("not enough inlined subroutines found in main.main")
637 }
638 })
639 }
640 }
641
642
643 func TestInlinedRoutineArgsVars(t *testing.T) {
644 testenv.MustHaveGoBuild(t)
645
646 mustHaveDWARF(t)
647
648 t.Parallel()
649
650 const prog = `
651 package main
652
653 var G int
654
655 func noinline(x int) int {
656 defer func() { G += x }()
657 return x
658 }
659
660 func cand(x, y int) int {
661 return noinline(x+y) ^ (y - x)
662 }
663
664 func main() {
665 x := cand(G*G,G|7%G)
666 G = x
667 }
668 `
669
670
671
672
673
674 _, ex := gobuildAndExamine(t, prog, OptInl4)
675
676 maindie := findSubprogramDIE(t, ex, "main.main")
677
678
679 mainIdx := ex.IdxFromOffset(maindie.Offset)
680 childDies := ex.Children(mainIdx)
681 found := false
682 for _, child := range childDies {
683 if child.Tag != dwarf.TagInlinedSubroutine {
684 continue
685 }
686
687
688 if found {
689 t.Fatalf("Found multiple inlined subroutines, expect only one")
690 }
691 found = true
692
693
694 ooff, originOK := child.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
695 if !originOK {
696 t.Fatalf("no abstract origin attr for inlined subroutine at offset %v", child.Offset)
697 }
698 originDIE := ex.EntryFromOffset(ooff)
699 if originDIE == nil {
700 t.Fatalf("can't locate origin DIE at off %v", ooff)
701 }
702
703
704 name, ok := originDIE.Val(dwarf.AttrName).(string)
705 if !ok {
706 t.Fatalf("no name attr for inlined subroutine at offset %v", child.Offset)
707 }
708 if name != "main.cand" {
709 t.Fatalf("expected inlined routine %s got %s", "main.cand", name)
710 }
711
712
713
714
715
716 absFcnIdx := ex.IdxFromOffset(ooff)
717 absFcnChildDies := ex.Children(absFcnIdx)
718 if len(absFcnChildDies) != 2 {
719 t.Fatalf("expected abstract function: expected 2 children, got %d children", len(absFcnChildDies))
720 }
721 formalCount := 0
722 for _, absChild := range absFcnChildDies {
723 if absChild.Tag == dwarf.TagFormalParameter {
724 formalCount += 1
725 continue
726 }
727 t.Fatalf("abstract function child DIE: expected formal, got %v", absChild.Tag)
728 }
729 if formalCount != 2 {
730 t.Fatalf("abstract function DIE: expected 2 formals, got %d", formalCount)
731 }
732
733 omap := make(map[dwarf.Offset]bool)
734
735
736
737
738 inlIdx := ex.IdxFromOffset(child.Offset)
739 inlChildDies := ex.Children(inlIdx)
740 for _, k := range inlChildDies {
741 ooff, originOK := k.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
742 if !originOK {
743 t.Fatalf("no abstract origin attr for child of inlined subroutine at offset %v", k.Offset)
744 }
745 if _, found := omap[ooff]; found {
746 t.Fatalf("duplicate abstract origin at child of inlined subroutine at offset %v", k.Offset)
747 }
748 omap[ooff] = true
749 }
750 }
751 if !found {
752 t.Fatalf("not enough inlined subroutines found in main.main")
753 }
754 }
755
756 func abstractOriginSanity(t *testing.T, pkgDir string, flags string) {
757 t.Parallel()
758
759
760 f := gobuildTestdata(t, filepath.Join(pkgDir, "main"), flags)
761 defer f.Close()
762
763 d, err := f.DWARF()
764 if err != nil {
765 t.Fatalf("error reading DWARF: %v", err)
766 }
767 rdr := d.Reader()
768 ex := dwtest.Examiner{}
769 if err := ex.Populate(rdr); err != nil {
770 t.Fatalf("error reading DWARF: %v", err)
771 }
772
773
774
775 abscount := 0
776 for i, die := range ex.DIEs() {
777
778 ooff, originOK := die.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
779 if !originOK {
780 continue
781 }
782
783
784 abscount += 1
785 originDIE := ex.EntryFromOffset(ooff)
786 if originDIE == nil {
787 ex.DumpEntry(i, false, 0)
788 t.Fatalf("unresolved abstract origin ref in DIE at offset 0x%x\n", die.Offset)
789 }
790
791
792
793
794
795 pidx := ex.IdxFromOffset(die.Offset)
796 if pidx < 0 {
797 t.Fatalf("can't locate DIE id")
798 }
799 kids := ex.Children(pidx)
800 for _, kid := range kids {
801 if kid.Tag != dwarf.TagVariable &&
802 kid.Tag != dwarf.TagFormalParameter {
803 continue
804 }
805 kooff, originOK := kid.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
806 if !originOK {
807 continue
808 }
809 childOriginDIE := ex.EntryFromOffset(kooff)
810 if childOriginDIE == nil {
811 ex.DumpEntry(i, false, 0)
812 t.Fatalf("unresolved abstract origin ref in DIE at offset %x", kid.Offset)
813 }
814 coidx := ex.IdxFromOffset(childOriginDIE.Offset)
815 childOriginParent := ex.Parent(coidx)
816 if childOriginParent != originDIE {
817 ex.DumpEntry(i, false, 0)
818 t.Fatalf("unexpected parent of abstract origin DIE at offset %v", childOriginDIE.Offset)
819 }
820 }
821 }
822 if abscount == 0 {
823 t.Fatalf("no abstract origin refs found, something is wrong")
824 }
825 }
826
827 func TestAbstractOriginSanity(t *testing.T) {
828 testenv.MustHaveGoBuild(t)
829
830 if testing.Short() {
831 t.Skip("skipping test in short mode.")
832 }
833
834 mustHaveDWARF(t)
835 abstractOriginSanity(t, "testdata/httptest", OptAllInl4)
836 }
837
838 func TestAbstractOriginSanityIssue25459(t *testing.T) {
839 testenv.MustHaveGoBuild(t)
840
841 mustHaveDWARF(t)
842 if runtime.GOARCH != "amd64" && runtime.GOARCH != "386" {
843 t.Skip("skipping on not-amd64 not-386; location lists not supported")
844 }
845
846 abstractOriginSanity(t, "testdata/issue25459", DefaultOpt)
847 }
848
849 func TestAbstractOriginSanityIssue26237(t *testing.T) {
850 testenv.MustHaveGoBuild(t)
851
852 mustHaveDWARF(t)
853 abstractOriginSanity(t, "testdata/issue26237", DefaultOpt)
854 }
855
856 func TestRuntimeTypeAttrInternal(t *testing.T) {
857 testenv.MustHaveGoBuild(t)
858
859
860 testenv.MustInternalLink(t, testenv.NoSpecialBuildTypes)
861
862 mustHaveDWARF(t)
863
864 testRuntimeTypeAttr(t, "-ldflags=-linkmode=internal")
865 }
866
867
868 func TestRuntimeTypeAttrExternal(t *testing.T) {
869 testenv.MustHaveGoBuild(t)
870 testenv.MustHaveCGO(t)
871
872 mustHaveDWARF(t)
873
874
875 if runtime.GOARCH == "ppc64" {
876 t.Skip("-linkmode=external not supported on ppc64")
877 }
878
879 testRuntimeTypeAttr(t, "-ldflags=-linkmode=external")
880 }
881
882 func testRuntimeTypeAttr(t *testing.T, flags string) {
883 t.Parallel()
884
885 const prog = `
886 package main
887
888 import "unsafe"
889
890 type X struct{ _ int }
891
892 func main() {
893 var x interface{} = &X{}
894 p := *(*uintptr)(unsafe.Pointer(&x))
895 print(p)
896 }
897 `
898 dir := t.TempDir()
899
900 f := gobuild(t, dir, prog, flags)
901 defer f.Close()
902
903 out, err := testenv.Command(t, f.path).CombinedOutput()
904 if err != nil {
905 t.Fatalf("could not run test program: %v", err)
906 }
907 addr, err := strconv.ParseUint(string(out), 10, 64)
908 if err != nil {
909 t.Fatalf("could not parse type address from program output %q: %v", out, err)
910 }
911
912 symbols, err := f.Symbols()
913 if err != nil {
914 t.Fatalf("error reading symbols: %v", err)
915 }
916 var types *objfilepkg.Sym
917 for _, sym := range symbols {
918 if sym.Name == "runtime.types" {
919 types = &sym
920 break
921 }
922 }
923 if types == nil {
924 t.Fatal("couldn't find runtime.types in symbols")
925 }
926
927 d, err := f.DWARF()
928 if err != nil {
929 t.Fatalf("error reading DWARF: %v", err)
930 }
931
932 rdr := d.Reader()
933 ex := dwtest.Examiner{}
934 if err := ex.Populate(rdr); err != nil {
935 t.Fatalf("error reading DWARF: %v", err)
936 }
937 dies := ex.Named("*main.X")
938 if len(dies) != 1 {
939 t.Fatalf("wanted 1 DIE named *main.X, found %v", len(dies))
940 }
941 rtAttr := dies[0].Val(intdwarf.DW_AT_go_runtime_type)
942 if rtAttr == nil {
943 t.Fatalf("*main.X DIE had no runtime type attr. DIE: %v", dies[0])
944 }
945
946 if platform.DefaultPIE(runtime.GOOS, runtime.GOARCH, false) {
947 return
948 }
949 if rtAttr.(uint64)+types.Addr != addr {
950 t.Errorf("DWARF type offset was %#x+%#x, but test program said %#x", rtAttr.(uint64), types.Addr, addr)
951 }
952 }
953
954 func TestIssue27614(t *testing.T) {
955
956
957
958 testenv.MustHaveGoBuild(t)
959
960 mustHaveDWARF(t)
961
962 t.Parallel()
963
964 dir := t.TempDir()
965
966 const prog = `package main
967
968 import "fmt"
969
970 type astruct struct {
971 X int
972 }
973
974 type bstruct struct {
975 X float32
976 }
977
978 var globalptr *astruct
979 var globalvar astruct
980 var bvar0, bvar1, bvar2 bstruct
981
982 func main() {
983 fmt.Println(globalptr, globalvar, bvar0, bvar1, bvar2)
984 }
985 `
986
987 f := gobuild(t, dir, prog, NoOpt)
988
989 defer f.Close()
990
991 data, err := f.DWARF()
992 if err != nil {
993 t.Fatal(err)
994 }
995
996 rdr := data.Reader()
997
998 var astructTypeDIE, bstructTypeDIE, ptrastructTypeDIE *dwarf.Entry
999 var globalptrDIE, globalvarDIE *dwarf.Entry
1000 var bvarDIE [3]*dwarf.Entry
1001
1002 for {
1003 e, err := rdr.Next()
1004 if err != nil {
1005 t.Fatal(err)
1006 }
1007 if e == nil {
1008 break
1009 }
1010
1011 name, _ := e.Val(dwarf.AttrName).(string)
1012
1013 switch e.Tag {
1014 case dwarf.TagTypedef:
1015 switch name {
1016 case "main.astruct":
1017 astructTypeDIE = e
1018 case "main.bstruct":
1019 bstructTypeDIE = e
1020 }
1021 case dwarf.TagPointerType:
1022 if name == "*main.astruct" {
1023 ptrastructTypeDIE = e
1024 }
1025 case dwarf.TagVariable:
1026 switch name {
1027 case "main.globalptr":
1028 globalptrDIE = e
1029 case "main.globalvar":
1030 globalvarDIE = e
1031 default:
1032 const bvarprefix = "main.bvar"
1033 if strings.HasPrefix(name, bvarprefix) {
1034 i, _ := strconv.Atoi(name[len(bvarprefix):])
1035 bvarDIE[i] = e
1036 }
1037 }
1038 }
1039 }
1040
1041 typedieof := func(e *dwarf.Entry) dwarf.Offset {
1042 return e.Val(dwarf.AttrType).(dwarf.Offset)
1043 }
1044
1045 if off := typedieof(ptrastructTypeDIE); off != astructTypeDIE.Offset {
1046 t.Errorf("type attribute of *main.astruct references %#x, not main.astruct DIE at %#x\n", off, astructTypeDIE.Offset)
1047 }
1048
1049 if off := typedieof(globalptrDIE); off != ptrastructTypeDIE.Offset {
1050 t.Errorf("type attribute of main.globalptr references %#x, not *main.astruct DIE at %#x\n", off, ptrastructTypeDIE.Offset)
1051 }
1052
1053 if off := typedieof(globalvarDIE); off != astructTypeDIE.Offset {
1054 t.Errorf("type attribute of main.globalvar1 references %#x, not main.astruct DIE at %#x\n", off, astructTypeDIE.Offset)
1055 }
1056
1057 for i := range bvarDIE {
1058 if off := typedieof(bvarDIE[i]); off != bstructTypeDIE.Offset {
1059 t.Errorf("type attribute of main.bvar%d references %#x, not main.bstruct DIE at %#x\n", i, off, bstructTypeDIE.Offset)
1060 }
1061 }
1062 }
1063
1064 func TestStaticTmp(t *testing.T) {
1065
1066
1067
1068
1069
1070 testenv.MustHaveGoBuild(t)
1071
1072 mustHaveDWARF(t)
1073
1074 t.Parallel()
1075
1076 dir := t.TempDir()
1077
1078 const prog = `package main
1079
1080 var stmp_0 string
1081 var a []int
1082
1083 func init() {
1084 a = []int{ 7 }
1085 }
1086
1087 func main() {
1088 println(a[0])
1089 }
1090 `
1091
1092 f := gobuild(t, dir, prog, NoOpt)
1093
1094 defer f.Close()
1095
1096 d, err := f.DWARF()
1097 if err != nil {
1098 t.Fatalf("error reading DWARF: %v", err)
1099 }
1100
1101 rdr := d.Reader()
1102 for {
1103 e, err := rdr.Next()
1104 if err != nil {
1105 t.Fatal(err)
1106 }
1107 if e == nil {
1108 break
1109 }
1110 if e.Tag != dwarf.TagVariable {
1111 continue
1112 }
1113 name, ok := e.Val(dwarf.AttrName).(string)
1114 if !ok {
1115 continue
1116 }
1117 if strings.Contains(name, "stmp") {
1118 t.Errorf("statictmp variable found in debug_info: %s at %x", name, e.Offset)
1119 }
1120 }
1121
1122
1123
1124
1125
1126
1127 if !testenv.CanInternalLink(false) {
1128 return
1129 }
1130
1131 syms, err := f.Symbols()
1132 if err != nil {
1133 t.Fatalf("error reading symbols: %v", err)
1134 }
1135 for _, sym := range syms {
1136 if strings.Contains(sym.Name, "stmp") {
1137 t.Errorf("statictmp variable found in symbol table: %s", sym.Name)
1138 }
1139 }
1140 }
1141
1142 func TestPackageNameAttr(t *testing.T) {
1143 const dwarfAttrGoPackageName = dwarf.Attr(0x2905)
1144 const dwarfGoLanguage = 22
1145
1146 testenv.MustHaveGoBuild(t)
1147
1148 mustHaveDWARF(t)
1149
1150 t.Parallel()
1151
1152 dir := t.TempDir()
1153
1154 const prog = "package main\nfunc main() {\nprintln(\"hello world\")\n}\n"
1155
1156 f := gobuild(t, dir, prog, NoOpt)
1157
1158 defer f.Close()
1159
1160 d, err := f.DWARF()
1161 if err != nil {
1162 t.Fatalf("error reading DWARF: %v", err)
1163 }
1164
1165 rdr := d.Reader()
1166 runtimeUnitSeen := false
1167 for {
1168 e, err := rdr.Next()
1169 if err != nil {
1170 t.Fatal(err)
1171 }
1172 if e == nil {
1173 break
1174 }
1175 if e.Tag != dwarf.TagCompileUnit {
1176 continue
1177 }
1178 if lang, _ := e.Val(dwarf.AttrLanguage).(int64); lang != dwarfGoLanguage {
1179 continue
1180 }
1181
1182 pn, ok := e.Val(dwarfAttrGoPackageName).(string)
1183 if !ok {
1184 name, _ := e.Val(dwarf.AttrName).(string)
1185 t.Errorf("found compile unit without package name: %s", name)
1186
1187 }
1188 if pn == "" {
1189 name, _ := e.Val(dwarf.AttrName).(string)
1190 t.Errorf("found compile unit with empty package name: %s", name)
1191 } else {
1192 if pn == "runtime" {
1193 runtimeUnitSeen = true
1194 }
1195 }
1196 }
1197
1198
1199 if !runtimeUnitSeen {
1200 t.Errorf("no package name for runtime unit")
1201 }
1202 }
1203
1204 func TestMachoIssue32233(t *testing.T) {
1205 testenv.MustHaveGoBuild(t)
1206 testenv.MustHaveCGO(t)
1207
1208 if runtime.GOOS != "darwin" {
1209 t.Skip("skipping; test only interesting on darwin")
1210 }
1211
1212 f := gobuildTestdata(t, "testdata/issue32233/main", DefaultOpt)
1213 f.Close()
1214 }
1215
1216 func TestWindowsIssue36495(t *testing.T) {
1217 testenv.MustHaveGoBuild(t)
1218 if runtime.GOOS != "windows" {
1219 t.Skip("skipping: test only on windows")
1220 }
1221
1222 dir := t.TempDir()
1223
1224 prog := `
1225 package main
1226
1227 import "fmt"
1228
1229 func main() {
1230 fmt.Println("Hello World")
1231 }`
1232 f := gobuild(t, dir, prog, NoOpt)
1233 defer f.Close()
1234 exe, err := pe.Open(f.path)
1235 if err != nil {
1236 t.Fatalf("error opening pe file: %v", err)
1237 }
1238 defer exe.Close()
1239 dw, err := exe.DWARF()
1240 if err != nil {
1241 t.Fatalf("error parsing DWARF: %v", err)
1242 }
1243 rdr := dw.Reader()
1244 for {
1245 e, err := rdr.Next()
1246 if err != nil {
1247 t.Fatalf("error reading DWARF: %v", err)
1248 }
1249 if e == nil {
1250 break
1251 }
1252 if e.Tag != dwarf.TagCompileUnit {
1253 continue
1254 }
1255 lnrdr, err := dw.LineReader(e)
1256 if err != nil {
1257 t.Fatalf("error creating DWARF line reader: %v", err)
1258 }
1259 if lnrdr != nil {
1260 var lne dwarf.LineEntry
1261 for {
1262 err := lnrdr.Next(&lne)
1263 if err == io.EOF {
1264 break
1265 }
1266 if err != nil {
1267 t.Fatalf("error reading next DWARF line: %v", err)
1268 }
1269 if strings.Contains(lne.File.Name, `\`) {
1270 t.Errorf("filename should not contain backslash: %v", lne.File.Name)
1271 }
1272 }
1273 }
1274 rdr.SkipChildren()
1275 }
1276 }
1277
1278 func TestIssue38192(t *testing.T) {
1279 testenv.MustHaveGoBuild(t)
1280
1281 mustHaveDWARF(t)
1282
1283 t.Parallel()
1284
1285
1286
1287 f := gobuildTestdata(t, "testdata/issue38192", DefaultOpt)
1288 defer f.Close()
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304 rows := []dwarf.LineEntry{}
1305 dw, err := f.DWARF()
1306 if err != nil {
1307 t.Fatalf("error parsing DWARF: %v", err)
1308 }
1309 rdr := dw.Reader()
1310 for {
1311 e, err := rdr.Next()
1312 if err != nil {
1313 t.Fatalf("error reading DWARF: %v", err)
1314 }
1315 if e == nil {
1316 break
1317 }
1318 if e.Tag != dwarf.TagCompileUnit {
1319 continue
1320 }
1321
1322 name := e.Val(dwarf.AttrName).(string)
1323 if name != "main" {
1324 continue
1325 }
1326 lnrdr, err := dw.LineReader(e)
1327 if err != nil {
1328 t.Fatalf("error creating DWARF line reader: %v", err)
1329 }
1330 if lnrdr != nil {
1331 var lne dwarf.LineEntry
1332 for {
1333 err := lnrdr.Next(&lne)
1334 if err == io.EOF {
1335 break
1336 }
1337 if err != nil {
1338 t.Fatalf("error reading next DWARF line: %v", err)
1339 }
1340 if !strings.HasSuffix(lne.File.Name, "ld/testdata/issue38192/oneline.s") {
1341 continue
1342 }
1343 rows = append(rows, lne)
1344 }
1345 }
1346 rdr.SkipChildren()
1347 }
1348 f.Close()
1349
1350
1351
1352
1353
1354
1355 pcs := make(map[uint64]bool)
1356 line8seen := false
1357 for _, r := range rows {
1358 pcs[r.Address] = true
1359 if r.Line == 8 {
1360 line8seen = true
1361 }
1362 }
1363 failed := false
1364 if len(pcs) < 2 {
1365 failed = true
1366 t.Errorf("not enough line table rows for main.singleInstruction (got %d, wanted > 1", len(pcs))
1367 }
1368 if !line8seen {
1369 failed = true
1370 t.Errorf("line table does not contain correct line for main.singleInstruction")
1371 }
1372 if !failed {
1373 return
1374 }
1375 for i, r := range rows {
1376 t.Logf("row %d: A=%x F=%s L=%d\n", i, r.Address, r.File.Name, r.Line)
1377 }
1378 }
1379
1380 func TestIssue39757(t *testing.T) {
1381 testenv.MustHaveGoBuild(t)
1382
1383 mustHaveDWARF(t)
1384
1385 t.Parallel()
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399 f := gobuildTestdata(t, "testdata/issue39757", DefaultOpt)
1400 defer f.Close()
1401
1402 syms, err := f.Symbols()
1403 if err != nil {
1404 t.Fatal(err)
1405 }
1406
1407 var addr uint64
1408 for _, sym := range syms {
1409 if sym.Name == "main.main" {
1410 addr = sym.Addr
1411 break
1412 }
1413 }
1414 if addr == 0 {
1415 t.Fatal("cannot find main.main in symbols")
1416 }
1417
1418
1419
1420
1421
1422 dw, err := f.DWARF()
1423 if err != nil {
1424 t.Fatalf("error parsing DWARF: %v", err)
1425 }
1426 rdr := dw.Reader()
1427 ex := &dwtest.Examiner{}
1428 if err := ex.Populate(rdr); err != nil {
1429 t.Fatalf("error reading DWARF: %v", err)
1430 }
1431
1432 maindie := findSubprogramDIE(t, ex, "main.main")
1433
1434
1435
1436
1437
1438 lowpc, highpc, perr := dwtest.SubprogLoAndHighPc(maindie)
1439 if perr != nil {
1440 t.Fatalf("main.main DIE malformed: %v", perr)
1441 }
1442 t.Logf("lo=0x%x hi=0x%x\n", lowpc, highpc)
1443
1444
1445 mainIdx := ex.IdxFromOffset(maindie.Offset)
1446 cuentry := ex.Parent(mainIdx)
1447 if cuentry == nil {
1448 t.Fatalf("main.main DIE appears orphaned")
1449 }
1450 lnrdr, lerr := dw.LineReader(cuentry)
1451 if lerr != nil {
1452 t.Fatalf("error creating DWARF line reader: %v", err)
1453 }
1454 if lnrdr == nil {
1455 t.Fatalf("no line table for main.main compilation unit")
1456 }
1457 rows := []dwarf.LineEntry{}
1458 mainrows := 0
1459 var lne dwarf.LineEntry
1460 for {
1461 err := lnrdr.Next(&lne)
1462 if err == io.EOF {
1463 break
1464 }
1465 rows = append(rows, lne)
1466 if err != nil {
1467 t.Fatalf("error reading next DWARF line: %v", err)
1468 }
1469 if lne.Address < lowpc || lne.Address > highpc {
1470 continue
1471 }
1472 if !strings.HasSuffix(lne.File.Name, "issue39757main.go") {
1473 t.Errorf("found row with file=%s (not issue39757main.go)", lne.File.Name)
1474 }
1475 mainrows++
1476 }
1477 f.Close()
1478
1479
1480 if mainrows < 3 {
1481 t.Errorf("not enough line table rows for main.main (got %d, wanted > 3", mainrows)
1482 for i, r := range rows {
1483 t.Logf("row %d: A=%x F=%s L=%d\n", i, r.Address, r.File.Name, r.Line)
1484 }
1485 }
1486 }
1487
1488 func TestIssue42484(t *testing.T) {
1489 testenv.MustHaveGoBuild(t)
1490
1491
1492
1493
1494 testenv.MustInternalLink(t, testenv.NoSpecialBuildTypes)
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