github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/link/internal/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 intdwarf "github.com/gagliardetto/golang-go/cmd/internal/dwarf" 9 objfilepkg "github.com/gagliardetto/golang-go/cmd/internal/objfile" // renamed to avoid conflict with objfile function 10 "debug/dwarf" 11 "debug/pe" 12 "errors" 13 "fmt" 14 "github.com/gagliardetto/golang-go/not-internal/testenv" 15 "io" 16 "io/ioutil" 17 "os" 18 "os/exec" 19 "path/filepath" 20 "reflect" 21 "runtime" 22 "strconv" 23 "strings" 24 "testing" 25 ) 26 27 const ( 28 DefaultOpt = "-gcflags=" 29 NoOpt = "-gcflags=-l -N" 30 OptInl4 = "-gcflags=-l=4" 31 OptAllInl4 = "-gcflags=all=-l=4" 32 ) 33 34 func TestRuntimeTypesPresent(t *testing.T) { 35 t.Parallel() 36 testenv.MustHaveGoBuild(t) 37 38 if runtime.GOOS == "plan9" { 39 t.Skip("skipping on plan9; no DWARF symbol table in executables") 40 } 41 42 dir, err := ioutil.TempDir("", "TestRuntimeTypesPresent") 43 if err != nil { 44 t.Fatalf("could not create directory: %v", err) 45 } 46 defer os.RemoveAll(dir) 47 48 f := gobuild(t, dir, `package main; func main() { }`, NoOpt) 49 defer f.Close() 50 51 dwarf, err := f.DWARF() 52 if err != nil { 53 t.Fatalf("error reading DWARF: %v", err) 54 } 55 56 want := map[string]bool{ 57 "runtime._type": true, 58 "runtime.arraytype": true, 59 "runtime.chantype": true, 60 "runtime.functype": true, 61 "runtime.maptype": true, 62 "runtime.ptrtype": true, 63 "runtime.slicetype": true, 64 "runtime.structtype": true, 65 "runtime.interfacetype": true, 66 "runtime.itab": true, 67 "runtime.imethod": true, 68 } 69 70 found := findTypes(t, dwarf, want) 71 if len(found) != len(want) { 72 t.Errorf("found %v, want %v", found, want) 73 } 74 } 75 76 func findTypes(t *testing.T, dw *dwarf.Data, want map[string]bool) (found map[string]bool) { 77 found = make(map[string]bool) 78 rdr := dw.Reader() 79 for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() { 80 if err != nil { 81 t.Fatalf("error reading DWARF: %v", err) 82 } 83 switch entry.Tag { 84 case dwarf.TagTypedef: 85 if name, ok := entry.Val(dwarf.AttrName).(string); ok && want[name] { 86 found[name] = true 87 } 88 } 89 } 90 return 91 } 92 93 type builtFile struct { 94 *objfilepkg.File 95 path string 96 } 97 98 func gobuild(t *testing.T, dir string, testfile string, gcflags string) *builtFile { 99 src := filepath.Join(dir, "test.go") 100 dst := filepath.Join(dir, "out.exe") 101 102 if err := ioutil.WriteFile(src, []byte(testfile), 0666); err != nil { 103 t.Fatal(err) 104 } 105 106 cmd := exec.Command(testenv.GoToolPath(t), "build", gcflags, "-o", dst, src) 107 if b, err := cmd.CombinedOutput(); err != nil { 108 t.Logf("build: %s\n", b) 109 t.Fatalf("build error: %v", err) 110 } 111 112 f, err := objfilepkg.Open(dst) 113 if err != nil { 114 t.Fatal(err) 115 } 116 return &builtFile{f, dst} 117 } 118 119 // Similar to gobuild() above, but uses a main package instead of a test.go file. 120 121 func gobuildTestdata(t *testing.T, tdir string, pkgDir string, gcflags string) *builtFile { 122 dst := filepath.Join(tdir, "out.exe") 123 124 // Run a build with an updated GOPATH 125 cmd := exec.Command(testenv.GoToolPath(t), "build", gcflags, "-o", dst) 126 cmd.Dir = pkgDir 127 if b, err := cmd.CombinedOutput(); err != nil { 128 t.Logf("build: %s\n", b) 129 t.Fatalf("build error: %v", err) 130 } 131 132 f, err := objfilepkg.Open(dst) 133 if err != nil { 134 t.Fatal(err) 135 } 136 return &builtFile{f, dst} 137 } 138 139 func TestEmbeddedStructMarker(t *testing.T) { 140 t.Parallel() 141 testenv.MustHaveGoBuild(t) 142 143 if runtime.GOOS == "plan9" { 144 t.Skip("skipping on plan9; no DWARF symbol table in executables") 145 } 146 147 const prog = ` 148 package main 149 150 import "fmt" 151 152 type Foo struct { v int } 153 type Bar struct { 154 Foo 155 name string 156 } 157 type Baz struct { 158 *Foo 159 name string 160 } 161 162 func main() { 163 bar := Bar{ Foo: Foo{v: 123}, name: "onetwothree"} 164 baz := Baz{ Foo: &bar.Foo, name: "123" } 165 fmt.Println(bar, baz) 166 }` 167 168 want := map[string]map[string]bool{ 169 "main.Foo": {"v": false}, 170 "main.Bar": {"Foo": true, "name": false}, 171 "main.Baz": {"Foo": true, "name": false}, 172 } 173 174 dir, err := ioutil.TempDir("", "TestEmbeddedStructMarker") 175 if err != nil { 176 t.Fatalf("could not create directory: %v", err) 177 } 178 defer os.RemoveAll(dir) 179 180 f := gobuild(t, dir, prog, NoOpt) 181 182 defer f.Close() 183 184 d, err := f.DWARF() 185 if err != nil { 186 t.Fatalf("error reading DWARF: %v", err) 187 } 188 189 rdr := d.Reader() 190 for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() { 191 if err != nil { 192 t.Fatalf("error reading DWARF: %v", err) 193 } 194 switch entry.Tag { 195 case dwarf.TagStructType: 196 name := entry.Val(dwarf.AttrName).(string) 197 wantMembers := want[name] 198 if wantMembers == nil { 199 continue 200 } 201 gotMembers, err := findMembers(rdr) 202 if err != nil { 203 t.Fatalf("error reading DWARF: %v", err) 204 } 205 206 if !reflect.DeepEqual(gotMembers, wantMembers) { 207 t.Errorf("type %v: got map[member]embedded = %+v, want %+v", name, wantMembers, gotMembers) 208 } 209 delete(want, name) 210 } 211 } 212 if len(want) != 0 { 213 t.Errorf("failed to check all expected types: missing types = %+v", want) 214 } 215 } 216 217 func findMembers(rdr *dwarf.Reader) (map[string]bool, error) { 218 memberEmbedded := map[string]bool{} 219 // TODO(hyangah): define in debug/dwarf package 220 const goEmbeddedStruct = dwarf.Attr(intdwarf.DW_AT_go_embedded_field) 221 for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() { 222 if err != nil { 223 return nil, err 224 } 225 switch entry.Tag { 226 case dwarf.TagMember: 227 name := entry.Val(dwarf.AttrName).(string) 228 embedded := entry.Val(goEmbeddedStruct).(bool) 229 memberEmbedded[name] = embedded 230 case 0: 231 return memberEmbedded, nil 232 } 233 } 234 return memberEmbedded, nil 235 } 236 237 func TestSizes(t *testing.T) { 238 if runtime.GOOS == "plan9" { 239 t.Skip("skipping on plan9; no DWARF symbol table in executables") 240 } 241 t.Parallel() 242 243 // DWARF sizes should never be -1. 244 // See issue #21097 245 const prog = ` 246 package main 247 var x func() 248 var y [4]func() 249 func main() { 250 x = nil 251 y[0] = nil 252 } 253 ` 254 dir, err := ioutil.TempDir("", "TestSizes") 255 if err != nil { 256 t.Fatalf("could not create directory: %v", err) 257 } 258 defer os.RemoveAll(dir) 259 f := gobuild(t, dir, prog, NoOpt) 260 defer f.Close() 261 d, err := f.DWARF() 262 if err != nil { 263 t.Fatalf("error reading DWARF: %v", err) 264 } 265 rdr := d.Reader() 266 for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() { 267 if err != nil { 268 t.Fatalf("error reading DWARF: %v", err) 269 } 270 switch entry.Tag { 271 case dwarf.TagArrayType, dwarf.TagPointerType, dwarf.TagStructType, dwarf.TagBaseType, dwarf.TagSubroutineType, dwarf.TagTypedef: 272 default: 273 continue 274 } 275 typ, err := d.Type(entry.Offset) 276 if err != nil { 277 t.Fatalf("can't read type: %v", err) 278 } 279 if typ.Size() < 0 { 280 t.Errorf("subzero size %s %s %T", typ, entry.Tag, typ) 281 } 282 } 283 } 284 285 func TestFieldOverlap(t *testing.T) { 286 if runtime.GOOS == "plan9" { 287 t.Skip("skipping on plan9; no DWARF symbol table in executables") 288 } 289 t.Parallel() 290 291 // This test grew out of issue 21094, where specific sudog<T> DWARF types 292 // had elem fields set to values instead of pointers. 293 const prog = ` 294 package main 295 296 var c chan string 297 298 func main() { 299 c <- "foo" 300 } 301 ` 302 dir, err := ioutil.TempDir("", "TestFieldOverlap") 303 if err != nil { 304 t.Fatalf("could not create directory: %v", err) 305 } 306 defer os.RemoveAll(dir) 307 308 f := gobuild(t, dir, prog, NoOpt) 309 defer f.Close() 310 311 d, err := f.DWARF() 312 if err != nil { 313 t.Fatalf("error reading DWARF: %v", err) 314 } 315 316 rdr := d.Reader() 317 for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() { 318 if err != nil { 319 t.Fatalf("error reading DWARF: %v", err) 320 } 321 if entry.Tag != dwarf.TagStructType { 322 continue 323 } 324 typ, err := d.Type(entry.Offset) 325 if err != nil { 326 t.Fatalf("can't read type: %v", err) 327 } 328 s := typ.(*dwarf.StructType) 329 for i := 0; i < len(s.Field); i++ { 330 end := s.Field[i].ByteOffset + s.Field[i].Type.Size() 331 var limit int64 332 if i == len(s.Field)-1 { 333 limit = s.Size() 334 } else { 335 limit = s.Field[i+1].ByteOffset 336 } 337 if end > limit { 338 name := entry.Val(dwarf.AttrName).(string) 339 t.Fatalf("field %s.%s overlaps next field", name, s.Field[i].Name) 340 } 341 } 342 } 343 } 344 345 func varDeclCoordsAndSubrogramDeclFile(t *testing.T, testpoint string, expectFile string, expectLine int, directive string) { 346 t.Parallel() 347 348 prog := fmt.Sprintf("package main\n%s\nfunc main() {\n\nvar i int\ni = i\n}\n", directive) 349 350 dir, err := ioutil.TempDir("", testpoint) 351 if err != nil { 352 t.Fatalf("could not create directory: %v", err) 353 } 354 defer os.RemoveAll(dir) 355 356 f := gobuild(t, dir, prog, NoOpt) 357 358 d, err := f.DWARF() 359 if err != nil { 360 t.Fatalf("error reading DWARF: %v", err) 361 } 362 363 rdr := d.Reader() 364 ex := examiner{} 365 if err := ex.populate(rdr); err != nil { 366 t.Fatalf("error reading DWARF: %v", err) 367 } 368 369 // Locate the main.main DIE 370 mains := ex.Named("main.main") 371 if len(mains) == 0 { 372 t.Fatalf("unable to locate DIE for main.main") 373 } 374 if len(mains) != 1 { 375 t.Fatalf("more than one main.main DIE") 376 } 377 maindie := mains[0] 378 379 // Vet the main.main DIE 380 if maindie.Tag != dwarf.TagSubprogram { 381 t.Fatalf("unexpected tag %v on main.main DIE", maindie.Tag) 382 } 383 384 // Walk main's children and select variable "i". 385 mainIdx := ex.idxFromOffset(maindie.Offset) 386 childDies := ex.Children(mainIdx) 387 var iEntry *dwarf.Entry 388 for _, child := range childDies { 389 if child.Tag == dwarf.TagVariable && child.Val(dwarf.AttrName).(string) == "i" { 390 iEntry = child 391 break 392 } 393 } 394 if iEntry == nil { 395 t.Fatalf("didn't find DW_TAG_variable for i in main.main") 396 } 397 398 // Verify line/file attributes. 399 line := iEntry.Val(dwarf.AttrDeclLine) 400 if line == nil || line.(int64) != int64(expectLine) { 401 t.Errorf("DW_AT_decl_line for i is %v, want %d", line, expectLine) 402 } 403 404 fileIdx, fileIdxOK := maindie.Val(dwarf.AttrDeclFile).(int64) 405 if !fileIdxOK { 406 t.Errorf("missing or invalid DW_AT_decl_file for main") 407 } 408 file := ex.FileRef(t, d, mainIdx, fileIdx) 409 base := filepath.Base(file) 410 if base != expectFile { 411 t.Errorf("DW_AT_decl_file for main is %v, want %v", base, expectFile) 412 } 413 } 414 415 func TestVarDeclCoordsAndSubrogramDeclFile(t *testing.T) { 416 testenv.MustHaveGoBuild(t) 417 418 if runtime.GOOS == "plan9" { 419 t.Skip("skipping on plan9; no DWARF symbol table in executables") 420 } 421 422 varDeclCoordsAndSubrogramDeclFile(t, "TestVarDeclCoords", "test.go", 5, "") 423 } 424 425 func TestVarDeclCoordsWithLineDirective(t *testing.T) { 426 testenv.MustHaveGoBuild(t) 427 428 if runtime.GOOS == "plan9" { 429 t.Skip("skipping on plan9; no DWARF symbol table in executables") 430 } 431 432 varDeclCoordsAndSubrogramDeclFile(t, "TestVarDeclCoordsWithLineDirective", 433 "foobar.go", 202, "//line /foobar.go:200") 434 } 435 436 // Helper class for supporting queries on DIEs within a DWARF .debug_info 437 // section. Invoke the populate() method below passing in a dwarf.Reader, 438 // which will read in all DIEs and keep track of parent/child 439 // relationships. Queries can then be made to ask for DIEs by name or 440 // by offset. This will hopefully reduce boilerplate for future test 441 // writing. 442 443 type examiner struct { 444 dies []*dwarf.Entry 445 idxByOffset map[dwarf.Offset]int 446 kids map[int][]int 447 parent map[int]int 448 byname map[string][]int 449 } 450 451 // Populate the examiner using the DIEs read from rdr. 452 func (ex *examiner) populate(rdr *dwarf.Reader) error { 453 ex.idxByOffset = make(map[dwarf.Offset]int) 454 ex.kids = make(map[int][]int) 455 ex.parent = make(map[int]int) 456 ex.byname = make(map[string][]int) 457 var nesting []int 458 for entry, err := rdr.Next(); entry != nil; entry, err = rdr.Next() { 459 if err != nil { 460 return err 461 } 462 if entry.Tag == 0 { 463 // terminator 464 if len(nesting) == 0 { 465 return errors.New("nesting stack underflow") 466 } 467 nesting = nesting[:len(nesting)-1] 468 continue 469 } 470 idx := len(ex.dies) 471 ex.dies = append(ex.dies, entry) 472 if _, found := ex.idxByOffset[entry.Offset]; found { 473 return errors.New("DIE clash on offset") 474 } 475 ex.idxByOffset[entry.Offset] = idx 476 if name, ok := entry.Val(dwarf.AttrName).(string); ok { 477 ex.byname[name] = append(ex.byname[name], idx) 478 } 479 if len(nesting) > 0 { 480 parent := nesting[len(nesting)-1] 481 ex.kids[parent] = append(ex.kids[parent], idx) 482 ex.parent[idx] = parent 483 } 484 if entry.Children { 485 nesting = append(nesting, idx) 486 } 487 } 488 if len(nesting) > 0 { 489 return errors.New("unterminated child sequence") 490 } 491 return nil 492 } 493 494 func indent(ilevel int) { 495 for i := 0; i < ilevel; i++ { 496 fmt.Printf(" ") 497 } 498 } 499 500 // For debugging new tests 501 func (ex *examiner) dumpEntry(idx int, dumpKids bool, ilevel int) error { 502 if idx >= len(ex.dies) { 503 msg := fmt.Sprintf("bad DIE %d: index out of range\n", idx) 504 return errors.New(msg) 505 } 506 entry := ex.dies[idx] 507 indent(ilevel) 508 fmt.Printf("0x%x: %v\n", idx, entry.Tag) 509 for _, f := range entry.Field { 510 indent(ilevel) 511 fmt.Printf("at=%v val=0x%x\n", f.Attr, f.Val) 512 } 513 if dumpKids { 514 ksl := ex.kids[idx] 515 for _, k := range ksl { 516 ex.dumpEntry(k, true, ilevel+2) 517 } 518 } 519 return nil 520 } 521 522 // Given a DIE offset, return the previously read dwarf.Entry, or nil 523 func (ex *examiner) entryFromOffset(off dwarf.Offset) *dwarf.Entry { 524 if idx, found := ex.idxByOffset[off]; found && idx != -1 { 525 return ex.entryFromIdx(idx) 526 } 527 return nil 528 } 529 530 // Return the ID that examiner uses to refer to the DIE at offset off 531 func (ex *examiner) idxFromOffset(off dwarf.Offset) int { 532 if idx, found := ex.idxByOffset[off]; found { 533 return idx 534 } 535 return -1 536 } 537 538 // Return the dwarf.Entry pointer for the DIE with id 'idx' 539 func (ex *examiner) entryFromIdx(idx int) *dwarf.Entry { 540 if idx >= len(ex.dies) || idx < 0 { 541 return nil 542 } 543 return ex.dies[idx] 544 } 545 546 // Returns a list of child entries for a die with ID 'idx' 547 func (ex *examiner) Children(idx int) []*dwarf.Entry { 548 sl := ex.kids[idx] 549 ret := make([]*dwarf.Entry, len(sl)) 550 for i, k := range sl { 551 ret[i] = ex.entryFromIdx(k) 552 } 553 return ret 554 } 555 556 // Returns parent DIE for DIE 'idx', or nil if the DIE is top level 557 func (ex *examiner) Parent(idx int) *dwarf.Entry { 558 p, found := ex.parent[idx] 559 if !found { 560 return nil 561 } 562 return ex.entryFromIdx(p) 563 } 564 565 // ParentCU returns the enclosing compilation unit DIE for the DIE 566 // with a given index, or nil if for some reason we can't establish a 567 // parent. 568 func (ex *examiner) ParentCU(idx int) *dwarf.Entry { 569 for { 570 parentDie := ex.Parent(idx) 571 if parentDie == nil { 572 return nil 573 } 574 if parentDie.Tag == dwarf.TagCompileUnit { 575 return parentDie 576 } 577 idx = ex.idxFromOffset(parentDie.Offset) 578 } 579 } 580 581 // FileRef takes a given DIE by index and a numeric file reference 582 // (presumably from a decl_file or call_file attribute), looks up the 583 // reference in the .debug_line file table, and returns the proper 584 // string for it. We need to know which DIE is making the reference 585 // so as find the right compilation unit. 586 func (ex *examiner) FileRef(t *testing.T, dw *dwarf.Data, dieIdx int, fileRef int64) string { 587 588 // Find the parent compilation unit DIE for the specified DIE. 589 cuDie := ex.ParentCU(dieIdx) 590 if cuDie == nil { 591 t.Fatalf("no parent CU DIE for DIE with idx %d?", dieIdx) 592 return "" 593 } 594 // Construct a line reader and then use it to get the file string. 595 lr, lrerr := dw.LineReader(cuDie) 596 if lrerr != nil { 597 t.Fatal("d.LineReader: ", lrerr) 598 return "" 599 } 600 files := lr.Files() 601 if fileRef < 0 || int(fileRef) > len(files)-1 { 602 t.Fatalf("examiner.FileRef: malformed file reference %d", fileRef) 603 return "" 604 } 605 return files[fileRef].Name 606 } 607 608 // Return a list of all DIEs with name 'name'. When searching for DIEs 609 // by name, keep in mind that the returned results will include child 610 // DIEs such as params/variables. For example, asking for all DIEs named 611 // "p" for even a small program will give you 400-500 entries. 612 func (ex *examiner) Named(name string) []*dwarf.Entry { 613 sl := ex.byname[name] 614 ret := make([]*dwarf.Entry, len(sl)) 615 for i, k := range sl { 616 ret[i] = ex.entryFromIdx(k) 617 } 618 return ret 619 } 620 621 func TestInlinedRoutineRecords(t *testing.T) { 622 testenv.MustHaveGoBuild(t) 623 624 if runtime.GOOS == "plan9" { 625 t.Skip("skipping on plan9; no DWARF symbol table in executables") 626 } 627 if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" || runtime.GOOS == "darwin" { 628 t.Skip("skipping on solaris, illumos, and darwin, pending resolution of issue #23168") 629 } 630 631 t.Parallel() 632 633 const prog = ` 634 package main 635 636 var G int 637 638 func noinline(x int) int { 639 defer func() { G += x }() 640 return x 641 } 642 643 func cand(x, y int) int { 644 return noinline(x+y) ^ (y - x) 645 } 646 647 func main() { 648 x := cand(G*G,G|7%G) 649 G = x 650 } 651 ` 652 dir, err := ioutil.TempDir("", "TestInlinedRoutineRecords") 653 if err != nil { 654 t.Fatalf("could not create directory: %v", err) 655 } 656 defer os.RemoveAll(dir) 657 658 // Note: this is a build with "-l=4", as opposed to "-l -N". The 659 // test is intended to verify DWARF that is only generated when 660 // the inliner is active. We're only going to look at the DWARF for 661 // main.main, however, hence we build with "-gcflags=-l=4" as opposed 662 // to "-gcflags=all=-l=4". 663 f := gobuild(t, dir, prog, OptInl4) 664 665 d, err := f.DWARF() 666 if err != nil { 667 t.Fatalf("error reading DWARF: %v", err) 668 } 669 670 // The inlined subroutines we expect to visit 671 expectedInl := []string{"main.cand"} 672 673 rdr := d.Reader() 674 ex := examiner{} 675 if err := ex.populate(rdr); err != nil { 676 t.Fatalf("error reading DWARF: %v", err) 677 } 678 679 // Locate the main.main DIE 680 mains := ex.Named("main.main") 681 if len(mains) == 0 { 682 t.Fatalf("unable to locate DIE for main.main") 683 } 684 if len(mains) != 1 { 685 t.Fatalf("more than one main.main DIE") 686 } 687 maindie := mains[0] 688 689 // Vet the main.main DIE 690 if maindie.Tag != dwarf.TagSubprogram { 691 t.Fatalf("unexpected tag %v on main.main DIE", maindie.Tag) 692 } 693 694 // Walk main's children and pick out the inlined subroutines 695 mainIdx := ex.idxFromOffset(maindie.Offset) 696 childDies := ex.Children(mainIdx) 697 exCount := 0 698 for _, child := range childDies { 699 if child.Tag == dwarf.TagInlinedSubroutine { 700 // Found an inlined subroutine, locate abstract origin. 701 ooff, originOK := child.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset) 702 if !originOK { 703 t.Fatalf("no abstract origin attr for inlined subroutine at offset %v", child.Offset) 704 } 705 originDIE := ex.entryFromOffset(ooff) 706 if originDIE == nil { 707 t.Fatalf("can't locate origin DIE at off %v", ooff) 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 if exCount >= len(expectedInl) { 732 t.Fatalf("too many inlined subroutines found in main.main") 733 } 734 735 // Name should check out. 736 expected := expectedInl[exCount] 737 if name, ok := originDIE.Val(dwarf.AttrName).(string); ok { 738 if name != expected { 739 t.Fatalf("expected inlined routine %s got %s", name, expected) 740 } 741 } 742 exCount++ 743 744 // Verify that the call_file attribute for the inlined 745 // instance is ok. In this case it should match the file 746 // for the main routine. To do this we need to locate the 747 // compilation unit DIE that encloses what we're looking 748 // at; this can be done with the examiner. 749 cf, cfOK := child.Val(dwarf.AttrCallFile).(int64) 750 if !cfOK { 751 t.Fatalf("no call_file attr for inlined subroutine at offset %v", child.Offset) 752 } 753 file := ex.FileRef(t, d, mainIdx, cf) 754 base := filepath.Base(file) 755 if base != "test.go" { 756 t.Errorf("bad call_file attribute, found '%s', want '%s'", 757 file, "test.go") 758 } 759 760 omap := make(map[dwarf.Offset]bool) 761 762 // Walk the child variables of the inlined routine. Each 763 // of them should have a distinct abstract origin-- if two 764 // vars point to the same origin things are definitely broken. 765 inlIdx := ex.idxFromOffset(child.Offset) 766 inlChildDies := ex.Children(inlIdx) 767 for _, k := range inlChildDies { 768 ooff, originOK := k.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset) 769 if !originOK { 770 t.Fatalf("no abstract origin attr for child of inlined subroutine at offset %v", k.Offset) 771 } 772 if _, found := omap[ooff]; found { 773 t.Fatalf("duplicate abstract origin at child of inlined subroutine at offset %v", k.Offset) 774 } 775 omap[ooff] = true 776 } 777 } 778 } 779 if exCount != len(expectedInl) { 780 t.Fatalf("not enough inlined subroutines found in main.main") 781 } 782 } 783 784 func abstractOriginSanity(t *testing.T, pkgDir string, flags string) { 785 t.Parallel() 786 787 dir, err := ioutil.TempDir("", "TestAbstractOriginSanity") 788 if err != nil { 789 t.Fatalf("could not create directory: %v", err) 790 } 791 defer os.RemoveAll(dir) 792 793 // Build with inlining, to exercise DWARF inlining support. 794 f := gobuildTestdata(t, dir, filepath.Join(pkgDir, "main"), flags) 795 796 d, err := f.DWARF() 797 if err != nil { 798 t.Fatalf("error reading DWARF: %v", err) 799 } 800 rdr := d.Reader() 801 ex := examiner{} 802 if err := ex.populate(rdr); err != nil { 803 t.Fatalf("error reading DWARF: %v", err) 804 } 805 806 // Make a pass through all DIEs looking for abstract origin 807 // references. 808 abscount := 0 809 for i, die := range ex.dies { 810 // Does it have an abstract origin? 811 ooff, originOK := die.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset) 812 if !originOK { 813 continue 814 } 815 816 // All abstract origin references should be resolvable. 817 abscount += 1 818 originDIE := ex.entryFromOffset(ooff) 819 if originDIE == nil { 820 ex.dumpEntry(i, false, 0) 821 t.Fatalf("unresolved abstract origin ref in DIE at offset 0x%x\n", die.Offset) 822 } 823 824 // Suppose that DIE X has parameter/variable children {K1, 825 // K2, ... KN}. If X has an abstract origin of A, then for 826 // each KJ, the abstract origin of KJ should be a child of A. 827 // Note that this same rule doesn't hold for non-variable DIEs. 828 pidx := ex.idxFromOffset(die.Offset) 829 if pidx < 0 { 830 t.Fatalf("can't locate DIE id") 831 } 832 kids := ex.Children(pidx) 833 for _, kid := range kids { 834 if kid.Tag != dwarf.TagVariable && 835 kid.Tag != dwarf.TagFormalParameter { 836 continue 837 } 838 kooff, originOK := kid.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset) 839 if !originOK { 840 continue 841 } 842 childOriginDIE := ex.entryFromOffset(kooff) 843 if childOriginDIE == nil { 844 ex.dumpEntry(i, false, 0) 845 t.Fatalf("unresolved abstract origin ref in DIE at offset %x", kid.Offset) 846 } 847 coidx := ex.idxFromOffset(childOriginDIE.Offset) 848 childOriginParent := ex.Parent(coidx) 849 if childOriginParent != originDIE { 850 ex.dumpEntry(i, false, 0) 851 t.Fatalf("unexpected parent of abstract origin DIE at offset %v", childOriginDIE.Offset) 852 } 853 } 854 } 855 if abscount == 0 { 856 t.Fatalf("no abstract origin refs found, something is wrong") 857 } 858 } 859 860 func TestAbstractOriginSanity(t *testing.T) { 861 testenv.MustHaveGoBuild(t) 862 863 if testing.Short() { 864 t.Skip("skipping test in short mode.") 865 } 866 867 if runtime.GOOS == "plan9" { 868 t.Skip("skipping on plan9; no DWARF symbol table in executables") 869 } 870 if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" || runtime.GOOS == "darwin" { 871 t.Skip("skipping on solaris, illumos, and darwin, pending resolution of issue #23168") 872 } 873 874 if wd, err := os.Getwd(); err == nil { 875 gopathdir := filepath.Join(wd, "testdata", "httptest") 876 abstractOriginSanity(t, gopathdir, OptAllInl4) 877 } else { 878 t.Fatalf("os.Getwd() failed %v", err) 879 } 880 } 881 882 func TestAbstractOriginSanityIssue25459(t *testing.T) { 883 testenv.MustHaveGoBuild(t) 884 885 if runtime.GOOS == "plan9" { 886 t.Skip("skipping on plan9; no DWARF symbol table in executables") 887 } 888 if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" || runtime.GOOS == "darwin" { 889 t.Skip("skipping on solaris, illumos, and darwin, pending resolution of issue #23168") 890 } 891 if runtime.GOARCH != "amd64" && runtime.GOARCH != "x86" { 892 t.Skip("skipping on not-amd64 not-x86; location lists not supported") 893 } 894 895 if wd, err := os.Getwd(); err == nil { 896 gopathdir := filepath.Join(wd, "testdata", "issue25459") 897 abstractOriginSanity(t, gopathdir, DefaultOpt) 898 } else { 899 t.Fatalf("os.Getwd() failed %v", err) 900 } 901 } 902 903 func TestAbstractOriginSanityIssue26237(t *testing.T) { 904 testenv.MustHaveGoBuild(t) 905 906 if runtime.GOOS == "plan9" { 907 t.Skip("skipping on plan9; no DWARF symbol table in executables") 908 } 909 if runtime.GOOS == "solaris" || runtime.GOOS == "illumos" || runtime.GOOS == "darwin" { 910 t.Skip("skipping on solaris, illumos, and darwin, pending resolution of issue #23168") 911 } 912 if wd, err := os.Getwd(); err == nil { 913 gopathdir := filepath.Join(wd, "testdata", "issue26237") 914 abstractOriginSanity(t, gopathdir, DefaultOpt) 915 } else { 916 t.Fatalf("os.Getwd() failed %v", err) 917 } 918 } 919 920 func TestRuntimeTypeAttrInternal(t *testing.T) { 921 testenv.MustHaveGoBuild(t) 922 923 if runtime.GOOS == "plan9" { 924 t.Skip("skipping on plan9; no DWARF symbol table in executables") 925 } 926 927 if runtime.GOOS == "windows" && runtime.GOARCH == "arm" { 928 t.Skip("skipping on windows/arm; test is incompatible with relocatable binaries") 929 } 930 931 testRuntimeTypeAttr(t, "-ldflags=-linkmode=internal") 932 } 933 934 // External linking requires a host linker (https://golang.org/src/cmd/cgo/doc.go l.732) 935 func TestRuntimeTypeAttrExternal(t *testing.T) { 936 testenv.MustHaveGoBuild(t) 937 testenv.MustHaveCGO(t) 938 939 if runtime.GOOS == "plan9" { 940 t.Skip("skipping on plan9; no DWARF symbol table in executables") 941 } 942 943 // Explicitly test external linking, for dsymutil compatibility on Darwin. 944 if runtime.GOARCH == "ppc64" { 945 t.Skip("-linkmode=external not supported on ppc64") 946 } 947 testRuntimeTypeAttr(t, "-ldflags=-linkmode=external") 948 } 949 950 func testRuntimeTypeAttr(t *testing.T, flags string) { 951 t.Parallel() 952 953 const prog = ` 954 package main 955 956 import "unsafe" 957 958 type X struct{ _ int } 959 960 func main() { 961 var x interface{} = &X{} 962 p := *(*uintptr)(unsafe.Pointer(&x)) 963 print(p) 964 } 965 ` 966 dir, err := ioutil.TempDir("", "TestRuntimeType") 967 if err != nil { 968 t.Fatalf("could not create directory: %v", err) 969 } 970 defer os.RemoveAll(dir) 971 972 f := gobuild(t, dir, prog, flags) 973 out, err := exec.Command(f.path).CombinedOutput() 974 if err != nil { 975 t.Fatalf("could not run test program: %v", err) 976 } 977 addr, err := strconv.ParseUint(string(out), 10, 64) 978 if err != nil { 979 t.Fatalf("could not parse type address from program output %q: %v", out, err) 980 } 981 982 symbols, err := f.Symbols() 983 if err != nil { 984 t.Fatalf("error reading symbols: %v", err) 985 } 986 var types *objfilepkg.Sym 987 for _, sym := range symbols { 988 if sym.Name == "runtime.types" { 989 types = &sym 990 break 991 } 992 } 993 if types == nil { 994 t.Fatal("couldn't find runtime.types in symbols") 995 } 996 997 d, err := f.DWARF() 998 if err != nil { 999 t.Fatalf("error reading DWARF: %v", err) 1000 } 1001 1002 rdr := d.Reader() 1003 ex := examiner{} 1004 if err := ex.populate(rdr); err != nil { 1005 t.Fatalf("error reading DWARF: %v", err) 1006 } 1007 dies := ex.Named("*main.X") 1008 if len(dies) != 1 { 1009 t.Fatalf("wanted 1 DIE named *main.X, found %v", len(dies)) 1010 } 1011 rtAttr := dies[0].Val(intdwarf.DW_AT_go_runtime_type) 1012 if rtAttr == nil { 1013 t.Fatalf("*main.X DIE had no runtime type attr. DIE: %v", dies[0]) 1014 } 1015 1016 if rtAttr.(uint64)+types.Addr != addr { 1017 t.Errorf("DWARF type offset was %#x+%#x, but test program said %#x", rtAttr.(uint64), types.Addr, addr) 1018 } 1019 } 1020 1021 func TestIssue27614(t *testing.T) { 1022 // Type references in debug_info should always use the DW_TAG_typedef_type 1023 // for the type, when that's generated. 1024 1025 testenv.MustHaveGoBuild(t) 1026 1027 if runtime.GOOS == "plan9" { 1028 t.Skip("skipping on plan9; no DWARF symbol table in executables") 1029 } 1030 1031 t.Parallel() 1032 1033 dir, err := ioutil.TempDir("", "go-build") 1034 if err != nil { 1035 t.Fatal(err) 1036 } 1037 defer os.RemoveAll(dir) 1038 1039 const prog = `package main 1040 1041 import "fmt" 1042 1043 type astruct struct { 1044 X int 1045 } 1046 1047 type bstruct struct { 1048 X float32 1049 } 1050 1051 var globalptr *astruct 1052 var globalvar astruct 1053 var bvar0, bvar1, bvar2 bstruct 1054 1055 func main() { 1056 fmt.Println(globalptr, globalvar, bvar0, bvar1, bvar2) 1057 } 1058 ` 1059 1060 f := gobuild(t, dir, prog, NoOpt) 1061 1062 defer f.Close() 1063 1064 data, err := f.DWARF() 1065 if err != nil { 1066 t.Fatal(err) 1067 } 1068 1069 rdr := data.Reader() 1070 1071 var astructTypeDIE, bstructTypeDIE, ptrastructTypeDIE *dwarf.Entry 1072 var globalptrDIE, globalvarDIE *dwarf.Entry 1073 var bvarDIE [3]*dwarf.Entry 1074 1075 for { 1076 e, err := rdr.Next() 1077 if err != nil { 1078 t.Fatal(err) 1079 } 1080 if e == nil { 1081 break 1082 } 1083 1084 name, _ := e.Val(dwarf.AttrName).(string) 1085 1086 switch e.Tag { 1087 case dwarf.TagTypedef: 1088 switch name { 1089 case "main.astruct": 1090 astructTypeDIE = e 1091 case "main.bstruct": 1092 bstructTypeDIE = e 1093 } 1094 case dwarf.TagPointerType: 1095 if name == "*main.astruct" { 1096 ptrastructTypeDIE = e 1097 } 1098 case dwarf.TagVariable: 1099 switch name { 1100 case "main.globalptr": 1101 globalptrDIE = e 1102 case "main.globalvar": 1103 globalvarDIE = e 1104 default: 1105 const bvarprefix = "main.bvar" 1106 if strings.HasPrefix(name, bvarprefix) { 1107 i, _ := strconv.Atoi(name[len(bvarprefix):]) 1108 bvarDIE[i] = e 1109 } 1110 } 1111 } 1112 } 1113 1114 typedieof := func(e *dwarf.Entry) dwarf.Offset { 1115 return e.Val(dwarf.AttrType).(dwarf.Offset) 1116 } 1117 1118 if off := typedieof(ptrastructTypeDIE); off != astructTypeDIE.Offset { 1119 t.Errorf("type attribute of *main.astruct references %#x, not main.astruct DIE at %#x\n", off, astructTypeDIE.Offset) 1120 } 1121 1122 if off := typedieof(globalptrDIE); off != ptrastructTypeDIE.Offset { 1123 t.Errorf("type attribute of main.globalptr references %#x, not *main.astruct DIE at %#x\n", off, ptrastructTypeDIE.Offset) 1124 } 1125 1126 if off := typedieof(globalvarDIE); off != astructTypeDIE.Offset { 1127 t.Errorf("type attribute of main.globalvar1 references %#x, not main.astruct DIE at %#x\n", off, astructTypeDIE.Offset) 1128 } 1129 1130 for i := range bvarDIE { 1131 if off := typedieof(bvarDIE[i]); off != bstructTypeDIE.Offset { 1132 t.Errorf("type attribute of main.bvar%d references %#x, not main.bstruct DIE at %#x\n", i, off, bstructTypeDIE.Offset) 1133 } 1134 } 1135 } 1136 1137 func TestStaticTmp(t *testing.T) { 1138 // Checks that statictmp variables do not appear in debug_info or the 1139 // symbol table. 1140 // Also checks that statictmp variables do not collide with user defined 1141 // variables (issue #25113) 1142 1143 testenv.MustHaveGoBuild(t) 1144 1145 if runtime.GOOS == "plan9" { 1146 t.Skip("skipping on plan9; no DWARF symbol table in executables") 1147 } 1148 1149 t.Parallel() 1150 1151 dir, err := ioutil.TempDir("", "go-build") 1152 if err != nil { 1153 t.Fatal(err) 1154 } 1155 defer os.RemoveAll(dir) 1156 1157 const prog = `package main 1158 1159 var stmp_0 string 1160 var a []int 1161 1162 func init() { 1163 a = []int{ 7 } 1164 } 1165 1166 func main() { 1167 println(a[0]) 1168 } 1169 ` 1170 1171 f := gobuild(t, dir, prog, NoOpt) 1172 1173 defer f.Close() 1174 1175 d, err := f.DWARF() 1176 if err != nil { 1177 t.Fatalf("error reading DWARF: %v", err) 1178 } 1179 1180 rdr := d.Reader() 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.TagVariable { 1190 continue 1191 } 1192 name, ok := e.Val(dwarf.AttrName).(string) 1193 if !ok { 1194 continue 1195 } 1196 if strings.Contains(name, "stmp") { 1197 t.Errorf("statictmp variable found in debug_info: %s at %x", name, e.Offset) 1198 } 1199 } 1200 1201 syms, err := f.Symbols() 1202 if err != nil { 1203 t.Fatalf("error reading symbols: %v", err) 1204 } 1205 for _, sym := range syms { 1206 if strings.Contains(sym.Name, "stmp") { 1207 t.Errorf("statictmp variable found in symbol table: %s", sym.Name) 1208 } 1209 } 1210 } 1211 1212 func TestPackageNameAttr(t *testing.T) { 1213 const dwarfAttrGoPackageName = dwarf.Attr(0x2905) 1214 const dwarfGoLanguage = 22 1215 1216 testenv.MustHaveGoBuild(t) 1217 1218 if runtime.GOOS == "plan9" { 1219 t.Skip("skipping on plan9; no DWARF symbol table in executables") 1220 } 1221 1222 t.Parallel() 1223 1224 dir, err := ioutil.TempDir("", "go-build") 1225 if err != nil { 1226 t.Fatal(err) 1227 } 1228 defer os.RemoveAll(dir) 1229 1230 const prog = "package main\nfunc main() {\nprintln(\"hello world\")\n}\n" 1231 1232 f := gobuild(t, dir, prog, NoOpt) 1233 1234 defer f.Close() 1235 1236 d, err := f.DWARF() 1237 if err != nil { 1238 t.Fatalf("error reading DWARF: %v", err) 1239 } 1240 1241 rdr := d.Reader() 1242 for { 1243 e, err := rdr.Next() 1244 if err != nil { 1245 t.Fatal(err) 1246 } 1247 if e == nil { 1248 break 1249 } 1250 if e.Tag != dwarf.TagCompileUnit { 1251 continue 1252 } 1253 if lang, _ := e.Val(dwarf.AttrLanguage).(int64); lang != dwarfGoLanguage { 1254 continue 1255 } 1256 1257 _, ok := e.Val(dwarfAttrGoPackageName).(string) 1258 if !ok { 1259 name, _ := e.Val(dwarf.AttrName).(string) 1260 t.Errorf("found compile unit without package name: %s", name) 1261 } 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, err := ioutil.TempDir("", "TestMachoIssue32233") 1274 if err != nil { 1275 t.Fatalf("could not create directory: %v", err) 1276 } 1277 defer os.RemoveAll(tmpdir) 1278 1279 wd, err2 := os.Getwd() 1280 if err2 != nil { 1281 t.Fatalf("where am I? %v", err) 1282 } 1283 pdir := filepath.Join(wd, "testdata", "issue32233", "main") 1284 f := gobuildTestdata(t, tmpdir, pdir, DefaultOpt) 1285 f.Close() 1286 } 1287 1288 func TestWindowsIssue36495(t *testing.T) { 1289 testenv.MustHaveGoBuild(t) 1290 if runtime.GOOS != "windows" { 1291 t.Skip("skipping: test only on windows") 1292 } 1293 1294 dir, err := ioutil.TempDir("", "TestEmbeddedStructMarker") 1295 if err != nil { 1296 t.Fatalf("could not create directory: %v", err) 1297 } 1298 defer os.RemoveAll(dir) 1299 1300 prog := ` 1301 package main 1302 1303 import "fmt" 1304 1305 func main() { 1306 fmt.Println("Hello World") 1307 }` 1308 f := gobuild(t, dir, prog, NoOpt) 1309 exe, err := pe.Open(f.path) 1310 if err != nil { 1311 t.Fatalf("error opening pe file: %v", err) 1312 } 1313 dw, err := exe.DWARF() 1314 if err != nil { 1315 t.Fatalf("error parsing DWARF: %v", err) 1316 } 1317 rdr := dw.Reader() 1318 for { 1319 e, err := rdr.Next() 1320 if err != nil { 1321 t.Fatalf("error reading DWARF: %v", err) 1322 } 1323 if e == nil { 1324 break 1325 } 1326 if e.Tag != dwarf.TagCompileUnit { 1327 continue 1328 } 1329 lnrdr, err := dw.LineReader(e) 1330 if err != nil { 1331 t.Fatalf("error creating DWARF line reader: %v", err) 1332 } 1333 if lnrdr != nil { 1334 var lne dwarf.LineEntry 1335 for { 1336 err := lnrdr.Next(&lne) 1337 if err == io.EOF { 1338 break 1339 } 1340 if err != nil { 1341 t.Fatalf("error reading next DWARF line: %v", err) 1342 } 1343 if strings.Contains(lne.File.Name, `\`) { 1344 t.Errorf("filename should not contain backslash: %v", lne.File.Name) 1345 } 1346 } 1347 } 1348 rdr.SkipChildren() 1349 } 1350 }