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