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