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 f(nil)
897 }
898 //go:noinline
899 func f(x *X) { // Make sure that there is dwarf recorded for *X.
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
1495
1496
1497
1498 testenv.MustInternalLink(t, testenv.NoSpecialBuildTypes)
1499
1500 mustHaveDWARF(t)
1501
1502 t.Parallel()
1503
1504 f := gobuildTestdata(t, "testdata/issue42484", NoOpt)
1505
1506 var lastAddr uint64
1507 var lastFile string
1508 var lastLine int
1509
1510 dw, err := f.DWARF()
1511 if err != nil {
1512 t.Fatalf("error parsing DWARF: %v", err)
1513 }
1514 rdr := dw.Reader()
1515 for {
1516 e, err := rdr.Next()
1517 if err != nil {
1518 t.Fatalf("error reading DWARF: %v", err)
1519 }
1520 if e == nil {
1521 break
1522 }
1523 if e.Tag != dwarf.TagCompileUnit {
1524 continue
1525 }
1526 lnrdr, err := dw.LineReader(e)
1527 if err != nil {
1528 t.Fatalf("error creating DWARF line reader: %v", err)
1529 }
1530 if lnrdr != nil {
1531 var lne dwarf.LineEntry
1532 for {
1533 err := lnrdr.Next(&lne)
1534 if err == io.EOF {
1535 break
1536 }
1537 if err != nil {
1538 t.Fatalf("error reading next DWARF line: %v", err)
1539 }
1540 if lne.EndSequence {
1541 continue
1542 }
1543 if lne.Address == lastAddr && (lne.File.Name != lastFile || lne.Line != lastLine) {
1544 t.Errorf("address %#x is assigned to both %s:%d and %s:%d", lastAddr, lastFile, lastLine, lne.File.Name, lne.Line)
1545 }
1546 lastAddr = lne.Address
1547 lastFile = lne.File.Name
1548 lastLine = lne.Line
1549 }
1550 }
1551 rdr.SkipChildren()
1552 }
1553 f.Close()
1554 }
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568 func processParams(die *dwarf.Entry, ex *dwtest.Examiner) string {
1569
1570
1571
1572
1573
1574
1575
1576
1577 foundParams := make(map[string]string)
1578
1579
1580 pIdx := ex.IdxFromOffset(die.Offset)
1581 childDies := ex.Children(pIdx)
1582 idx := 0
1583 for _, child := range childDies {
1584 if child.Tag == dwarf.TagFormalParameter {
1585
1586
1587
1588
1589
1590
1591 st := -1
1592 if vp, ok := child.Val(dwarf.AttrVarParam).(bool); ok {
1593 if vp {
1594 st = 2
1595 } else {
1596 st = 1
1597 }
1598 }
1599 if name, ok := child.Val(dwarf.AttrName).(string); ok {
1600 foundParams[name] = fmt.Sprintf("%d:%d", idx, st)
1601 idx++
1602 }
1603 }
1604 }
1605
1606 found := make([]string, 0, len(foundParams))
1607 for k, v := range foundParams {
1608 found = append(found, fmt.Sprintf("%s:%s", k, v))
1609 }
1610 sort.Strings(found)
1611
1612 return fmt.Sprintf("%+v", found)
1613 }
1614
1615 func TestOutputParamAbbrevAndAttr(t *testing.T) {
1616 testenv.MustHaveGoBuild(t)
1617
1618 mustHaveDWARF(t)
1619 t.Parallel()
1620
1621
1622
1623
1624
1625
1626 const prog = `
1627 package main
1628
1629 //go:noinline
1630 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) {
1631 g1[0] = 6
1632 r1, r2, r3, r4, r5, r6 = c3, c2+c1, g1, 'a', d1+d2+d3+d4, f1+f2+f3
1633 return
1634 }
1635
1636 func main() {
1637 a := [1024]int{}
1638 v1, v2, v3, v4, v5, v6 := ABC(1, 2, 3, "a", "b", "c", "d", 1.0, 2.0, 1.0, a)
1639 println(v1, v2, v3[0], v4, v5, v6)
1640 }
1641 `
1642 _, ex := gobuildAndExamine(t, prog, NoOpt)
1643
1644 abcdie := findSubprogramDIE(t, ex, "main.ABC")
1645
1646
1647 found := processParams(abcdie, ex)
1648
1649
1650
1651
1652 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]"
1653 if found != expected {
1654 t.Errorf("param check failed, wanted:\n%s\ngot:\n%s\n",
1655 expected, found)
1656 }
1657 }
1658
1659 func TestDictIndex(t *testing.T) {
1660
1661
1662
1663 testenv.MustHaveGoBuild(t)
1664
1665 mustHaveDWARF(t)
1666 t.Parallel()
1667
1668 const prog = `
1669 package main
1670
1671 import "fmt"
1672
1673 type CustomInt int
1674
1675 func testfn[T any](arg T) {
1676 var mapvar = make(map[int]T)
1677 mapvar[0] = arg
1678 fmt.Println(arg, mapvar)
1679 }
1680
1681 func main() {
1682 testfn(CustomInt(3))
1683 }
1684 `
1685
1686 dir := t.TempDir()
1687 f := gobuild(t, dir, prog, NoOpt)
1688 defer f.Close()
1689
1690 d, err := f.DWARF()
1691 if err != nil {
1692 t.Fatalf("error reading DWARF: %v", err)
1693 }
1694
1695 rdr := d.Reader()
1696 found := false
1697 for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
1698 if err != nil {
1699 t.Fatalf("error reading DWARF: %v", err)
1700 }
1701 name, _ := entry.Val(dwarf.AttrName).(string)
1702 if strings.HasPrefix(name, "main.testfn") {
1703 found = true
1704 break
1705 }
1706 }
1707
1708 if !found {
1709 t.Fatalf("could not find main.testfn")
1710 }
1711
1712 offs := []dwarf.Offset{}
1713 for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() {
1714 if err != nil {
1715 t.Fatalf("error reading DWARF: %v", err)
1716 }
1717 if entry.Tag == 0 {
1718 break
1719 }
1720 name, _ := entry.Val(dwarf.AttrName).(string)
1721 switch name {
1722 case "arg", "mapvar":
1723 offs = append(offs, entry.Val(dwarf.AttrType).(dwarf.Offset))
1724 }
1725 }
1726 if len(offs) != 2 {
1727 t.Errorf("wrong number of variables found in main.testfn %d", len(offs))
1728 }
1729 for _, off := range offs {
1730 rdr.Seek(off)
1731 entry, err := rdr.Next()
1732 if err != nil {
1733 t.Fatalf("error reading DWARF: %v", err)
1734 }
1735 if _, ok := entry.Val(intdwarf.DW_AT_go_dict_index).(int64); !ok {
1736 t.Errorf("could not find DW_AT_go_dict_index attribute offset %#x (%T)", off, entry.Val(intdwarf.DW_AT_go_dict_index))
1737 }
1738 }
1739
1740 rdr.Seek(0)
1741 ex := dwtest.Examiner{}
1742 if err := ex.Populate(rdr); err != nil {
1743 t.Fatalf("error reading DWARF: %v", err)
1744 }
1745 for _, typeName := range []string{"main.CustomInt", "map[int]main.CustomInt"} {
1746 dies := ex.Named(typeName)
1747 if len(dies) != 1 {
1748 t.Errorf("wanted 1 DIE named %s, found %v", typeName, len(dies))
1749 }
1750 if dies[0].Val(intdwarf.DW_AT_go_runtime_type).(uint64) == 0 {
1751 t.Errorf("type %s does not have DW_AT_go_runtime_type", typeName)
1752 }
1753 }
1754 }
1755
1756 func TestOptimizedOutParamHandling(t *testing.T) {
1757 testenv.MustHaveGoBuild(t)
1758
1759 mustHaveDWARF(t)
1760 t.Parallel()
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775 const prog = `
1776 package main
1777
1778 // First testcase. All input params in registers, all params used.
1779
1780 //go:noinline
1781 func tc1(p1, p2 int, p3 string) (int, string) {
1782 return p1 + p2, p3 + "foo"
1783 }
1784
1785 // Second testcase. Some params in registers, some on stack.
1786
1787 //go:noinline
1788 func tc2(p1 int, p2 [128]int, p3 string) (int, string, [128]int) {
1789 return p1 + p2[p1], p3 + "foo", [128]int{p1}
1790 }
1791
1792 // Third testcase. Named return params.
1793
1794 //go:noinline
1795 func tc3(p1 int, p2 [128]int, p3 string) (r1 int, r2 bool, r3 string, r4 [128]int) {
1796 if p1 == 101 {
1797 r1 = p1 + p2[p1]
1798 r2 = p3 == "foo"
1799 r4 = [128]int{p1}
1800 return
1801 } else {
1802 return p1 - p2[p1+3], false, "bar", [128]int{p1 + 2}
1803 }
1804 }
1805
1806 // Fourth testcase. Some thing are used, some are unused.
1807
1808 //go:noinline
1809 func tc4(p1, p1un int, p2, p2un [128]int, p3, p3un string) (r1 int, r1un int, r2 bool, r3 string, r4, r4un [128]int) {
1810 if p1 == 101 {
1811 r1 = p1 + p2[p2[0]]
1812 r2 = p3 == "foo"
1813 r4 = [128]int{p1}
1814 return
1815 } else {
1816 return p1, -1, true, "plex", [128]int{p1 + 2}, [128]int{-1}
1817 }
1818 }
1819
1820 func main() {
1821 {
1822 r1, r2 := tc1(3, 4, "five")
1823 println(r1, r2)
1824 }
1825 {
1826 x := [128]int{9}
1827 r1, r2, r3 := tc2(3, x, "five")
1828 println(r1, r2, r3[0])
1829 }
1830 {
1831 x := [128]int{9}
1832 r1, r2, r3, r4 := tc3(3, x, "five")
1833 println(r1, r2, r3, r4[0])
1834 }
1835 {
1836 x := [128]int{3}
1837 y := [128]int{7}
1838 r1, r1u, r2, r3, r4, r4u := tc4(0, 1, x, y, "a", "b")
1839 println(r1, r1u, r2, r3, r4[0], r4u[1])
1840 }
1841
1842 }
1843 `
1844 _, ex := gobuildAndExamine(t, prog, DefaultOpt)
1845
1846 testcases := []struct {
1847 tag string
1848 expected string
1849 }{
1850 {
1851 tag: "tc1",
1852 expected: "[p1:0:1 p2:1:1 p3:2:1 ~r0:3:2 ~r1:4:2]",
1853 },
1854 {
1855 tag: "tc2",
1856 expected: "[p1:0:1 p2:1:1 p3:2:1 ~r0:3:2 ~r1:4:2 ~r2:5:2]",
1857 },
1858 {
1859 tag: "tc3",
1860 expected: "[p1:0:1 p2:1:1 p3:2:1 r1:3:2 r2:4:2 r3:5:2 r4:6:2]",
1861 },
1862 {
1863 tag: "tc4",
1864 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]",
1865 },
1866 }
1867
1868 for _, tc := range testcases {
1869
1870 which := fmt.Sprintf("main.%s", tc.tag)
1871 die := findSubprogramDIE(t, ex, which)
1872
1873
1874 foundParams := processParams(die, ex)
1875 if foundParams != tc.expected {
1876 t.Errorf("check failed for testcase %s -- wanted:\n%s\ngot:%s\n",
1877 tc.tag, tc.expected, foundParams)
1878 }
1879 }
1880 }
1881 func TestIssue54320(t *testing.T) {
1882
1883
1884 testenv.MustHaveGoBuild(t)
1885
1886 mustHaveDWARF(t)
1887
1888 t.Parallel()
1889
1890 const prog = `
1891 package main
1892
1893 import "fmt"
1894
1895 func main() {
1896 fmt.Printf("Hello world\n");
1897 }
1898 `
1899
1900 dir := t.TempDir()
1901 f := gobuild(t, dir, prog, "-ldflags=-debugtramp=2")
1902 defer f.Close()
1903
1904 d, err := f.DWARF()
1905 if err != nil {
1906 t.Fatalf("error reading DWARF: %v", err)
1907 }
1908
1909 rdr := d.Reader()
1910 found := false
1911 var entry *dwarf.Entry
1912 for entry, err = rdr.Next(); entry != nil; entry, err = rdr.Next() {
1913 if err != nil {
1914 t.Fatalf("error reading DWARF: %v", err)
1915 }
1916 if entry.Tag != dwarf.TagCompileUnit {
1917 continue
1918 }
1919 name, _ := entry.Val(dwarf.AttrName).(string)
1920 if name == "main" {
1921 found = true
1922 break
1923 }
1924 rdr.SkipChildren()
1925 }
1926
1927 if !found {
1928 t.Fatalf("could not find main compile unit")
1929 }
1930 lr, err := d.LineReader(entry)
1931 if err != nil {
1932 t.Fatalf("error obtaining linereader: %v", err)
1933 }
1934
1935 var le dwarf.LineEntry
1936 found = false
1937 for {
1938 if err := lr.Next(&le); err != nil {
1939 if err == io.EOF {
1940 break
1941 }
1942 t.Fatalf("error reading linentry: %v", err)
1943 }
1944
1945 if le.File == nil {
1946 continue
1947 }
1948 file := filepath.Base(le.File.Name)
1949 if file == "test.go" {
1950 found = true
1951 break
1952 }
1953 }
1954 if !found {
1955 t.Errorf("no LPT entries for test.go")
1956 }
1957 }
1958
1959 const zeroSizedVarProg = `
1960 package main
1961
1962 import (
1963 "fmt"
1964 )
1965
1966 func main() {
1967 zeroSizedVariable := struct{}{}
1968 fmt.Println(zeroSizedVariable)
1969 }
1970 `
1971
1972 func TestZeroSizedVariable(t *testing.T) {
1973 testenv.MustHaveGoBuild(t)
1974
1975 mustHaveDWARF(t)
1976 t.Parallel()
1977
1978 if testing.Short() {
1979 t.Skip("skipping test in short mode.")
1980 }
1981
1982
1983
1984
1985
1986 for _, opt := range []string{NoOpt, DefaultOpt} {
1987 opt := opt
1988 t.Run(opt, func(t *testing.T) {
1989 _, ex := gobuildAndExamine(t, zeroSizedVarProg, opt)
1990
1991
1992 abcs := ex.Named("zeroSizedVariable")
1993 if len(abcs) == 0 {
1994 t.Fatalf("unable to locate DIE for zeroSizedVariable")
1995 }
1996 if len(abcs) != 1 {
1997 t.Fatalf("more than one zeroSizedVariable DIE")
1998 }
1999 })
2000 }
2001 }
2002
2003 func TestConsistentGoKindAndRuntimeType(t *testing.T) {
2004 testenv.MustHaveGoBuild(t)
2005
2006 mustHaveDWARF(t)
2007 t.Parallel()
2008
2009 if testing.Short() {
2010 t.Skip("skipping test in short mode.")
2011 }
2012
2013
2014
2015 _, ex := gobuildAndExamine(t, zeroSizedVarProg, DefaultOpt)
2016
2017
2018 typesChecked := 0
2019 failures := 0
2020 for _, die := range ex.DIEs() {
2021
2022 rtt, hasRT := die.Val(intdwarf.DW_AT_go_runtime_type).(uint64)
2023 if !hasRT || rtt == 0 {
2024 continue
2025 }
2026
2027 if name, _ := die.Val(intdwarf.DW_AT_name).(string); name == "unsafe.Pointer" {
2028 continue
2029 }
2030 typesChecked++
2031
2032 if val, ok := die.Val(intdwarf.DW_AT_go_kind).(int64); !ok || val == 0 {
2033 failures++
2034
2035 if failures <= 10 {
2036 idx := ex.IdxFromOffset(die.Offset)
2037 t.Logf("type DIE has DW_AT_go_runtime_type but invalid DW_AT_go_kind:\n")
2038 ex.DumpEntry(idx, false, 0)
2039 }
2040 t.Errorf("bad type DIE at offset %d\n", die.Offset)
2041 }
2042 }
2043 if typesChecked == 0 {
2044 t.Fatalf("something went wrong, 0 types checked")
2045 } else {
2046 t.Logf("%d types checked\n", typesChecked)
2047 }
2048 }
2049
View as plain text