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