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