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