github.com/Rookout/GoSDK@v0.1.48/pkg/services/instrumentation/binary_info/binary_info.go (about) 1 // The MIT License (MIT) 2 3 // Copyright (c) 2014 Derek Parker 4 5 // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 // this software and associated documentation files (the "Software"), to deal in 7 // the Software without restriction, including without limitation the rights to 8 // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 // the Software, and to permit persons to whom the Software is furnished to do so, 10 // subject to the following conditions: 11 12 // The above copyright notice and this permission notice shall be included in all 13 // copies or substantial portions of the Software. 14 15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 22 package binary_info 23 24 import ( 25 "bytes" 26 "compress/zlib" 27 "debug/dwarf" 28 "debug/elf" 29 "encoding/binary" 30 "errors" 31 "fmt" 32 "io" 33 "os" 34 "path/filepath" 35 "runtime" 36 "sort" 37 "strings" 38 "sync" 39 40 "github.com/Rookout/GoSDK/pkg/logger" 41 "github.com/Rookout/GoSDK/pkg/rookoutErrors" 42 "github.com/Rookout/GoSDK/pkg/services/instrumentation/dwarf/frame" 43 "github.com/Rookout/GoSDK/pkg/services/instrumentation/dwarf/godwarf" 44 "github.com/Rookout/GoSDK/pkg/services/instrumentation/dwarf/line" 45 "github.com/Rookout/GoSDK/pkg/services/instrumentation/dwarf/loclist" 46 "github.com/Rookout/GoSDK/pkg/services/instrumentation/dwarf/op" 47 "github.com/Rookout/GoSDK/pkg/services/instrumentation/dwarf/reader" 48 "github.com/Rookout/GoSDK/pkg/services/instrumentation/dwarf/util" 49 "github.com/Rookout/GoSDK/pkg/utils" 50 "github.com/hashicorp/golang-lru/simplelru" 51 ) 52 53 type Function struct { 54 Name string 55 Entry, End uint64 56 Offset dwarf.Offset 57 cu *compileUnit 58 59 60 InlinedCalls []InlinedCall 61 trampoline bool 62 } 63 64 const crosscall2SPOffsetBad = 0x8 65 66 type BinaryInfo struct { 67 68 69 70 sigreturnfn *Function 71 72 73 74 75 crosscall2fn *Function 76 debugLocBytes []byte 77 debugLoclistBytes []byte 78 PointerSize int 79 80 debugInfoDirectories []string 81 82 83 Functions []Function 84 85 Sources []string 86 87 LookupFunc map[string]*Function 88 89 90 SymNames map[uint64]*elf.Symbol 91 92 93 94 Images []*Image 95 96 ElfDynamicSection ElfDynamicSection 97 98 99 100 101 102 103 104 105 106 107 PackageMap map[string][]string 108 109 FrameEntries frame.FrameDescriptionEntries 110 111 types map[string]dwarfRef 112 packageVars []packageVar 113 114 115 116 117 NameOfRuntimeType map[uint64]NameOfRuntimeTypeEntry 118 119 120 consts constantsMap 121 122 123 124 125 inlinedCallLines map[fileLine][]uint64 126 127 Dwarf *dwarf.Data 128 TypeCache sync.Map 129 } 130 131 type NameOfRuntimeTypeEntry struct { 132 Typename string 133 Kind int64 134 } 135 136 type fileLine struct { 137 file string 138 line int 139 } 140 141 142 type dwarfRef struct { 143 imageIndex int 144 offset dwarf.Offset 145 } 146 147 148 type InlinedCall struct { 149 cu *compileUnit 150 LowPC, HighPC uint64 151 } 152 153 154 155 156 type packageVar struct { 157 name string 158 cu *compileUnit 159 offset dwarf.Offset 160 addr uint64 161 } 162 163 type buildIDHeader struct { 164 Namesz uint32 165 Descsz uint32 166 Type uint32 167 } 168 169 170 type ElfDynamicSection struct { 171 Addr uint64 172 Size uint64 173 } 174 175 176 func NewBinaryInfo() *BinaryInfo { 177 pointerSize := 4 << (^uintptr(0) >> 63) 178 r := &BinaryInfo{NameOfRuntimeType: make(map[uint64]NameOfRuntimeTypeEntry), PointerSize: pointerSize} 179 return r 180 } 181 182 183 func (bi *BinaryInfo) LoadBinaryInfo(path string, entryPoint uint64, debugInfoDirs []string) error { 184 bi.debugInfoDirectories = debugInfoDirs 185 186 187 return bi.AddImage(path, entryPoint) 188 } 189 190 var dwarfTreeCacheSize = 512 191 192 193 194 195 196 func (bi *BinaryInfo) AddImage(path string, addr uint64) error { 197 198 if len(bi.Images) > 0 && !strings.HasPrefix(path, "/") { 199 return nil 200 } 201 for _, image := range bi.Images { 202 if image.Path == path && image.addr == addr { 203 return nil 204 } 205 } 206 207 208 image := &Image{Path: path, addr: addr} 209 image.dwarfTreeCache, _ = simplelru.NewLRU(dwarfTreeCacheSize, nil) 210 211 212 image.Index = len(bi.Images) 213 bi.Images = append(bi.Images, image) 214 err := loadBinaryInfo(bi, image, path, addr) 215 if err != nil { 216 bi.Images[len(bi.Images)-1].loadErr = err 217 } 218 return err 219 } 220 221 func isSupportedArch(a archID) bool { 222 if _, ok := supportedArchs[a]; ok { 223 return true 224 } 225 return false 226 } 227 228 229 230 func GetDebugSection(f *File, name string) ([]byte, error) { 231 sec := f.Section(getSectionName(name)) 232 if sec != nil { 233 return sec.Data() 234 } 235 sec = f.Section(getCompressedSectionName(name)) 236 if sec == nil { 237 return nil, fmt.Errorf("could not find .debug_%s section", name) 238 } 239 b, err := sec.Data() 240 if err != nil { 241 return nil, err 242 } 243 return decompressMaybe(b) 244 } 245 246 func decompressMaybe(b []byte) ([]byte, error) { 247 if len(b) < 12 || string(b[:4]) != "ZLIB" { 248 249 return b, nil 250 } 251 252 dlen := binary.BigEndian.Uint64(b[4:12]) 253 dbuf := make([]byte, dlen) 254 r, err := zlib.NewReader(bytes.NewBuffer(b[12:])) 255 if err != nil { 256 return nil, err 257 } 258 if _, err := io.ReadFull(r, dbuf); err != nil { 259 return nil, err 260 } 261 if err := r.Close(); err != nil { 262 return nil, err 263 } 264 return dbuf, nil 265 } 266 267 268 269 270 func (bi *BinaryInfo) parseDebugFrame(image *Image, exe *File, debugInfoBytes []byte) error { 271 debugFrameData, err := GetDebugSection(exe, "frame") 272 ehFrameSection := getEhFrameSection(exe) 273 if ehFrameSection == nil && debugFrameData == nil { 274 return fmt.Errorf("could not get .debug_frame section and .eh_frame section: %v", err) 275 } 276 var ehFrameData []byte 277 var ehFrameAddr uint64 278 if ehFrameSection != nil { 279 ehFrameAddr = ehFrameSection.Addr 280 ehFrameData, _ = ehFrameSection.Data() 281 } 282 byteOrder := frame.DwarfEndian(debugInfoBytes) 283 284 if debugFrameData != nil { 285 fe, err := frame.Parse(debugFrameData, byteOrder, image.StaticBase, bi.PointerSize, 0) 286 if err != nil { 287 return fmt.Errorf("could not parse .debug_frame section: %v", err) 288 } 289 bi.FrameEntries = bi.FrameEntries.Append(fe) 290 } 291 292 if ehFrameData != nil && ehFrameAddr > 0 { 293 fe, err := frame.Parse(ehFrameData, byteOrder, image.StaticBase, bi.PointerSize, ehFrameAddr) 294 if err != nil { 295 if debugFrameData == nil { 296 return fmt.Errorf("could not parse .eh_frame section: %v", err) 297 } 298 return nil 299 } 300 bi.FrameEntries = bi.FrameEntries.Append(fe) 301 } 302 303 return nil 304 } 305 306 func shouldFilterSource(path string) bool { 307 if utils.Contains(utils.TrueValues, os.Getenv("ROOKOUT_DONT_FILTER_SOURCES")) { 308 return false 309 } 310 311 312 if strings.Contains(path, "shouldrunprologue") || strings.Contains(path, "prepforcallback") { 313 return false 314 } 315 316 return strings.Contains(path, "gorook") || strings.Contains(path, "gosdk") 317 } 318 319 func (bi *BinaryInfo) loadSources(compileUnits []*compileUnit) { 320 for _, cu := range compileUnits { 321 if cu.lineInfo == nil { 322 continue 323 } 324 for _, fileEntry := range cu.lineInfo.FileNames { 325 if shouldFilterSource(fileEntry.Path) { 326 continue 327 } 328 bi.Sources = append(bi.Sources, fileEntry.Path) 329 } 330 } 331 sort.Strings(bi.Sources) 332 bi.Sources = uniq(bi.Sources) 333 } 334 335 func (bi *BinaryInfo) loadDebugInfoMaps(image *Image, debugInfoBytes, debugLineBytes []byte) error { 336 if bi.types == nil { 337 bi.types = make(map[string]dwarfRef) 338 } 339 if bi.consts == nil { 340 bi.consts = make(map[dwarfRef]*constantType) 341 } 342 if bi.PackageMap == nil { 343 bi.PackageMap = make(map[string][]string) 344 } 345 if bi.inlinedCallLines == nil { 346 bi.inlinedCallLines = make(map[fileLine][]uint64) 347 } 348 349 image.RuntimeTypeToDIE = make(map[uint64]runtimeTypeDIE) 350 351 ctxt := newLoadDebugInfoMapsContext(bi, image, util.ReadUnitVersions(debugInfoBytes)) 352 353 reader := image.Dwarf.Reader() 354 355 for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() { 356 if err != nil { 357 return errors.New("error reading debug_info") 358 } 359 switch entry.Tag { 360 case dwarf.TagCompileUnit: 361 cu := &compileUnit{} 362 cu.image = image 363 cu.entry = entry 364 cu.offset = entry.Offset 365 cu.Version = ctxt.offsetToVersion[cu.offset] 366 if lang, _ := entry.Val(dwarf.AttrLanguage).(int64); lang == godwarf.DW_LANG_Go { 367 cu.IsGo = true 368 } 369 cu.name, _ = entry.Val(dwarf.AttrName).(string) 370 compdir, _ := entry.Val(dwarf.AttrCompDir).(string) 371 if compdir != "" { 372 cu.name = filepath.Join(compdir, cu.name) 373 } 374 375 if shouldFilterSource(cu.name) { 376 continue 377 } 378 379 cu.ranges, _ = image.Dwarf.Ranges(entry) 380 for i := range cu.ranges { 381 cu.ranges[i][0] += image.StaticBase 382 cu.ranges[i][1] += image.StaticBase 383 } 384 if len(cu.ranges) >= 1 { 385 cu.lowPC = cu.ranges[0][0] 386 } 387 lineInfoOffset, hasLineInfo := entry.Val(dwarf.AttrStmtList).(int64) 388 if hasLineInfo && lineInfoOffset >= 0 && lineInfoOffset < int64(len(debugLineBytes)) { 389 cu.lineInfo = line.Parse(compdir, bytes.NewBuffer(debugLineBytes[lineInfoOffset:]), image.debugLineStr, nil, image.StaticBase, runtime.GOOS == "windows", bi.PointerSize) 390 } 391 cu.producer, _ = entry.Val(dwarf.AttrProducer).(string) 392 if cu.IsGo && cu.producer != "" { 393 semicolon := strings.Index(cu.producer, ";") 394 if semicolon < 0 { 395 cu.optimized = GoVersionAfterOrEqual(1, 10) 396 } else { 397 cu.optimized = !strings.Contains(cu.producer[semicolon:], "-N") || !strings.Contains(cu.producer[semicolon:], "-l") 398 cu.producer = cu.producer[:semicolon] 399 } 400 } 401 gopkg, _ := entry.Val(godwarf.AttrGoPackageName).(string) 402 if cu.IsGo && gopkg != "" { 403 bi.PackageMap[gopkg] = append(bi.PackageMap[gopkg], escapePackagePath(strings.Replace(cu.name, "\\", "/", -1))) 404 } 405 image.compileUnits = append(image.compileUnits, cu) 406 if entry.Children { 407 err := bi.loadDebugInfoMapsCompileUnit(ctxt, image, reader, cu) 408 if err != nil { 409 return err 410 } 411 } 412 413 case dwarf.TagPartialUnit: 414 reader.SkipChildren() 415 416 default: 417 418 reader.SkipChildren() 419 } 420 } 421 422 sort.Sort(compileUnitsByOffset(image.compileUnits)) 423 sort.Sort(functionsDebugInfoByEntry(bi.Functions)) 424 sort.Sort(packageVarsByAddr(bi.packageVars)) 425 426 bi.LookupFunc = make(map[string]*Function) 427 for i := range bi.Functions { 428 bi.LookupFunc[bi.Functions[i].Name] = &bi.Functions[i] 429 } 430 bi.sigreturnfn = bi.LookupFunc["runtime.sigreturn"] 431 bi.crosscall2fn = bi.LookupFunc["crosscall2"] 432 433 bi.loadSources(image.compileUnits) 434 return nil 435 } 436 437 type loadDebugInfoMapsContext struct { 438 ardr *dwarf.Reader 439 abstractOriginTable map[dwarf.Offset]int 440 knownPackageVars map[string]struct{} 441 offsetToVersion map[dwarf.Offset]uint8 442 } 443 444 func newLoadDebugInfoMapsContext(bi *BinaryInfo, image *Image, offsetToVersion map[dwarf.Offset]uint8) *loadDebugInfoMapsContext { 445 ctxt := &loadDebugInfoMapsContext{} 446 447 ctxt.ardr = image.Dwarf.Reader() 448 ctxt.abstractOriginTable = make(map[dwarf.Offset]int) 449 ctxt.offsetToVersion = offsetToVersion 450 451 ctxt.knownPackageVars = map[string]struct{}{} 452 for _, v := range bi.packageVars { 453 ctxt.knownPackageVars[v.name] = struct{}{} 454 } 455 456 return ctxt 457 } 458 459 460 461 462 func escapePackagePath(pkg string) string { 463 slash := strings.Index(pkg, "/") 464 if slash < 0 { 465 slash = 0 466 } 467 return pkg[:slash] + strings.Replace(pkg[slash:], ".", "%2e", -1) 468 } 469 470 type functionsDebugInfoByEntry []Function 471 472 func (v functionsDebugInfoByEntry) Len() int { return len(v) } 473 func (v functionsDebugInfoByEntry) Less(i, j int) bool { return v[i].Entry < v[j].Entry } 474 func (v functionsDebugInfoByEntry) Swap(i, j int) { v[i], v[j] = v[j], v[i] } 475 476 type packageVarsByAddr []packageVar 477 478 func (v packageVarsByAddr) Len() int { return len(v) } 479 func (v packageVarsByAddr) Less(i int, j int) bool { return v[i].addr < v[j].addr } 480 func (v packageVarsByAddr) Swap(i int, j int) { v[i], v[j] = v[j], v[i] } 481 482 483 func (bi *BinaryInfo) loadDebugInfoMapsCompileUnit(ctxt *loadDebugInfoMapsContext, image *Image, reader *dwarf.Reader, cu *compileUnit) error { 484 hasAttrGoPkgName := GoVersionAfterOrEqual(1, 13) 485 486 depth := 0 487 488 for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() { 489 if err != nil { 490 return errors.New("error reading debug_info") 491 } 492 switch entry.Tag { 493 case 0: 494 if depth == 0 { 495 return nil 496 } else { 497 depth-- 498 } 499 case dwarf.TagImportedUnit: 500 err = bi.loadDebugInfoMapsImportedUnit(entry, ctxt, image, cu) 501 if err != nil { 502 return err 503 } 504 reader.SkipChildren() 505 506 case dwarf.TagArrayType, dwarf.TagBaseType, dwarf.TagClassType, dwarf.TagStructType, dwarf.TagUnionType, dwarf.TagConstType, dwarf.TagVolatileType, dwarf.TagRestrictType, dwarf.TagEnumerationType, dwarf.TagPointerType, dwarf.TagSubroutineType, dwarf.TagTypedef, dwarf.TagUnspecifiedType: 507 if name, ok := entry.Val(dwarf.AttrName).(string); ok { 508 if !cu.IsGo { 509 name = "C." + name 510 } 511 if _, exists := bi.types[name]; !exists { 512 bi.types[name] = dwarfRef{image.Index, entry.Offset} 513 } 514 } 515 if cu != nil && cu.IsGo && !hasAttrGoPkgName { 516 bi.registerTypeToPackageMap(entry) 517 } 518 image.registerRuntimeTypeToDIE(entry) 519 reader.SkipChildren() 520 521 case dwarf.TagVariable: 522 if n, ok := entry.Val(dwarf.AttrName).(string); ok { 523 var addr uint64 524 if loc, ok := entry.Val(dwarf.AttrLocation).([]byte); ok { 525 if len(loc) == bi.PointerSize+1 && op.Opcode(loc[0]) == op.DW_OP_addr { 526 addr, _ = util.ReadUintRaw(bytes.NewReader(loc[1:]), binary.LittleEndian, bi.PointerSize) 527 } 528 } 529 if !cu.IsGo { 530 n = "C." + n 531 } 532 if _, known := ctxt.knownPackageVars[n]; !known { 533 bi.packageVars = append(bi.packageVars, packageVar{n, cu, entry.Offset, addr + image.StaticBase}) 534 } 535 } 536 reader.SkipChildren() 537 538 case dwarf.TagConstant: 539 name, okName := entry.Val(dwarf.AttrName).(string) 540 typ, okType := entry.Val(dwarf.AttrType).(dwarf.Offset) 541 val, okVal := entry.Val(dwarf.AttrConstValue).(int64) 542 if okName && okType && okVal { 543 if !cu.IsGo { 544 name = "C." + name 545 } 546 ct := bi.consts[dwarfRef{image.Index, typ}] 547 if ct == nil { 548 ct = &constantType{} 549 bi.consts[dwarfRef{image.Index, typ}] = ct 550 } 551 ct.values = append(ct.values, constantValue{name: name, fullName: name, value: val}) 552 } 553 reader.SkipChildren() 554 555 case dwarf.TagSubprogram: 556 inlined := false 557 if inval, ok := entry.Val(dwarf.AttrInline).(int64); ok { 558 inlined = inval >= 1 559 } 560 561 if inlined { 562 err = bi.addAbstractSubprogram(entry, ctxt, reader, cu) 563 if err != nil { 564 return err 565 } 566 } else { 567 originOffset, hasAbstractOrigin := entry.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset) 568 if hasAbstractOrigin { 569 err = bi.addConcreteInlinedSubprogram(entry, originOffset, ctxt, reader, cu) 570 if err != nil { 571 return err 572 } 573 } else { 574 err = bi.addConcreteSubprogram(entry, ctxt, reader, cu) 575 if err != nil { 576 return err 577 } 578 } 579 } 580 581 default: 582 if entry.Children { 583 depth++ 584 } 585 } 586 } 587 588 return nil 589 } 590 591 592 593 func (bi *BinaryInfo) loadDebugInfoMapsImportedUnit(entry *dwarf.Entry, ctxt *loadDebugInfoMapsContext, image *Image, cu *compileUnit) error { 594 off, ok := entry.Val(dwarf.AttrImport).(dwarf.Offset) 595 if !ok { 596 return nil 597 } 598 reader := image.Dwarf.Reader() 599 reader.Seek(off) 600 imentry, err := reader.Next() 601 if err != nil { 602 return nil 603 } 604 if imentry.Tag != dwarf.TagPartialUnit { 605 return nil 606 } 607 return bi.loadDebugInfoMapsCompileUnit(ctxt, image, reader, cu) 608 } 609 610 func (bi *BinaryInfo) registerTypeToPackageMap(entry *dwarf.Entry) { 611 if entry.Tag != dwarf.TagTypedef && entry.Tag != dwarf.TagBaseType && entry.Tag != dwarf.TagClassType && entry.Tag != dwarf.TagStructType { 612 return 613 } 614 615 typename, ok := entry.Val(dwarf.AttrName).(string) 616 if !ok || complexType(typename) { 617 return 618 } 619 620 dot := strings.LastIndex(typename, ".") 621 if dot < 0 { 622 return 623 } 624 path := typename[:dot] 625 slash := strings.LastIndex(path, "/") 626 if slash < 0 || slash+1 >= len(path) { 627 return 628 } 629 name := path[slash+1:] 630 bi.PackageMap[name] = []string{path} 631 } 632 633 func (bi *BinaryInfo) addConcreteInlinedSubprogram(entry *dwarf.Entry, originOffset dwarf.Offset, ctxt *loadDebugInfoMapsContext, reader *dwarf.Reader, cu *compileUnit) error { 634 lowpc, highpc, ok := subprogramEntryRange(entry, cu.image) 635 if !ok { 636 if entry.Children { 637 reader.SkipChildren() 638 } 639 return nil 640 } 641 642 originIdx, ok := ctxt.abstractOriginTable[originOffset] 643 if !ok { 644 if entry.Children { 645 reader.SkipChildren() 646 } 647 return nil 648 } 649 650 fn := &bi.Functions[originIdx] 651 fn.Offset = entry.Offset 652 fn.Entry = lowpc 653 fn.End = highpc 654 655 if entry.Children { 656 err := bi.loadDebugInfoMapsInlinedCalls(ctxt, reader, cu) 657 if err != nil { 658 return err 659 } 660 } 661 662 return nil 663 } 664 665 func (bi *BinaryInfo) loadDebugInfoMapsInlinedCalls(ctxt *loadDebugInfoMapsContext, reader *dwarf.Reader, cu *compileUnit) error { 666 for { 667 entry, err := reader.Next() 668 if err != nil { 669 return errors.New("error reading debug_info") 670 } 671 switch entry.Tag { 672 case 0: 673 return nil 674 case dwarf.TagInlinedSubroutine: 675 originOffset, ok := entry.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset) 676 if !ok { 677 reader.SkipChildren() 678 continue 679 } 680 681 originIdx, ok := ctxt.abstractOriginTable[originOffset] 682 if !ok { 683 reader.SkipChildren() 684 continue 685 } 686 fn := &bi.Functions[originIdx] 687 688 lowpc, highpc, ok := subprogramEntryRange(entry, cu.image) 689 if !ok { 690 reader.SkipChildren() 691 continue 692 } 693 694 callfileidx, ok1 := entry.Val(dwarf.AttrCallFile).(int64) 695 callline, ok2 := entry.Val(dwarf.AttrCallLine).(int64) 696 if !ok1 || !ok2 { 697 reader.SkipChildren() 698 continue 699 } 700 if cu.lineInfo == nil { 701 reader.SkipChildren() 702 continue 703 } 704 if int(callfileidx-1) >= len(cu.lineInfo.FileNames) { 705 reader.SkipChildren() 706 continue 707 } 708 callfile := cu.lineInfo.FileNames[callfileidx-1].Path 709 710 fn.InlinedCalls = append(fn.InlinedCalls, InlinedCall{ 711 cu: cu, 712 LowPC: lowpc, 713 HighPC: highpc, 714 }) 715 716 fl := fileLine{callfile, int(callline)} 717 bi.inlinedCallLines[fl] = append(bi.inlinedCallLines[fl], lowpc) 718 } 719 reader.SkipChildren() 720 } 721 } 722 723 func subprogramEntryRange(entry *dwarf.Entry, image *Image) (lowpc, highpc uint64, ok bool) { 724 ok = false 725 if ranges, _ := image.Dwarf.Ranges(entry); len(ranges) >= 1 { 726 ok = true 727 lowpc = ranges[0][0] + image.StaticBase 728 highpc = ranges[0][1] + image.StaticBase 729 } 730 return lowpc, highpc, ok 731 } 732 733 func (bi *BinaryInfo) addConcreteSubprogram(entry *dwarf.Entry, ctxt *loadDebugInfoMapsContext, reader *dwarf.Reader, cu *compileUnit) error { 734 lowpc, highpc, ok := subprogramEntryRange(entry, cu.image) 735 if !ok { 736 if entry.Children { 737 reader.SkipChildren() 738 } 739 return nil 740 } 741 742 name, ok := subprogramEntryName(entry, cu) 743 if !ok { 744 if entry.Children { 745 reader.SkipChildren() 746 } 747 return nil 748 } 749 750 fn := Function{ 751 Name: name, 752 Entry: lowpc, 753 End: highpc, 754 Offset: entry.Offset, 755 cu: cu, 756 } 757 bi.Functions = append(bi.Functions, fn) 758 759 if entry.Children { 760 err := bi.loadDebugInfoMapsInlinedCalls(ctxt, reader, cu) 761 if err != nil { 762 return err 763 } 764 } 765 766 return nil 767 } 768 769 func subprogramEntryName(entry *dwarf.Entry, cu *compileUnit) (string, bool) { 770 name, ok := entry.Val(dwarf.AttrName).(string) 771 if !ok { 772 return "", false 773 } 774 if !cu.IsGo { 775 name = "C." + name 776 } 777 return name, true 778 } 779 780 func (bi *BinaryInfo) addAbstractSubprogram(entry *dwarf.Entry, ctxt *loadDebugInfoMapsContext, reader *dwarf.Reader, cu *compileUnit) error { 781 name, ok := subprogramEntryName(entry, cu) 782 if !ok { 783 if entry.Children { 784 reader.SkipChildren() 785 } 786 return nil 787 } 788 789 fn := Function{ 790 Name: name, 791 Offset: entry.Offset, 792 cu: cu, 793 } 794 795 if entry.Children { 796 err := bi.loadDebugInfoMapsInlinedCalls(ctxt, reader, cu) 797 if err != nil { 798 return err 799 } 800 } 801 802 bi.Functions = append(bi.Functions, fn) 803 ctxt.abstractOriginTable[entry.Offset] = len(bi.Functions) - 1 804 return nil 805 } 806 807 func (bi *BinaryInfo) getBestMatchingFile(filename string) ([]*compileUnit, string, rookoutErrors.RookoutError) { 808 809 var topCu []*compileUnit 810 fm := utils.NewFileMatcher() 811 for _, image := range bi.Images { 812 for _, cu := range image.compileUnits { 813 if cu.lineInfo == nil { 814 continue 815 } 816 for _, f := range cu.lineInfo.FileNames { 817 matchScore := utils.GetPathMatchingScore(filename, f.Path) 818 switch fm.UpdateMatch(matchScore, f.Path) { 819 case utils.NewBestMatch: 820 logger.Logger().Debugf("NewBestMatch: filepath: %s", f.Path) 821 topCu = []*compileUnit{cu} 822 case utils.SameBestMatch: 823 logger.Logger().Debugf("SameBestMatch: filepath: %s", f.Path) 824 topCu = append(topCu, cu) 825 } 826 } 827 } 828 } 829 830 if !fm.AnyMatch() { 831 832 return nil, "", rookoutErrors.NewFileNotFound(filename) 833 } 834 if !fm.IsUnique() { 835 836 return nil, "", rookoutErrors.NewMultipleFilesFound(filename) 837 } 838 839 return topCu, fm.GetBestFile(), nil 840 } 841 842 843 844 func (bi *BinaryInfo) PCToInlineFunc(pc uint64) *Function { 845 fn := bi.PCToFunc(pc) 846 dwarfTree, err := fn.cu.image.GetDwarfTree(fn.Offset) 847 if err != nil { 848 return fn 849 } 850 entries := reader.InlineStack(dwarfTree, pc) 851 if len(entries) == 0 { 852 return fn 853 } 854 855 fnname, okname := entries[0].Val(dwarf.AttrName).(string) 856 if !okname { 857 return fn 858 } 859 860 return bi.LookupFunc[fnname] 861 } 862 863 864 865 func (bi *BinaryInfo) PCToFunc(pc uint64) *Function { 866 i := sort.Search(len(bi.Functions), func(i int) bool { 867 fn := bi.Functions[i] 868 return pc <= fn.Entry || (fn.Entry <= pc && pc < fn.End) 869 }) 870 if i != len(bi.Functions) { 871 fn := &bi.Functions[i] 872 if fn.Entry <= pc && pc < fn.End { 873 return fn 874 } 875 } 876 return nil 877 } 878 879 func (bi *BinaryInfo) FuncToImage(fn *Function) *Image { 880 if fn == nil { 881 return bi.Images[0] 882 } 883 884 return fn.cu.image 885 } 886 887 888 func (bi *BinaryInfo) PCToLine(pc uint64) (string, int, *Function) { 889 fn := bi.PCToFunc(pc) 890 if fn == nil { 891 return "", 0, nil 892 } 893 f, ln := fn.cu.lineInfo.PCToLine(fn.Entry, pc) 894 return f, ln, fn 895 } 896 897 func (bi *BinaryInfo) LocationExpr(entry godwarf.Entry, attr dwarf.Attr, pc uint64) ([]byte, *LocationExpr, error) { 898 899 a := entry.Val(attr) 900 if a == nil { 901 return nil, nil, fmt.Errorf("no location attribute %s", attr) 902 } 903 if instr, ok := a.([]byte); ok { 904 return instr, &LocationExpr{isBlock: true, instr: instr}, nil 905 } 906 off, ok := a.(int64) 907 if !ok { 908 return nil, nil, fmt.Errorf("could not interpret location attribute %s", attr) 909 } 910 instr := bi.loclistEntry(off, pc) 911 if instr == nil { 912 return nil, nil, fmt.Errorf("could not find loclist entry at %#x for address %#x", off, pc) 913 } 914 return instr, &LocationExpr{pc: pc, off: off, instr: instr}, nil 915 } 916 917 type LocationExpr struct { 918 isBlock bool 919 off int64 920 pc uint64 921 instr []byte 922 } 923 924 925 926 func (bi *BinaryInfo) loclistEntry(off int64, pc uint64) []byte { 927 var base uint64 928 image := bi.Images[0] 929 cu := bi.findCompileUnit(pc) 930 if cu != nil { 931 base = cu.lowPC 932 image = cu.image 933 } 934 if image == nil { 935 return nil 936 } 937 938 var loclist loclist.Reader = bi.newLoclist2Reader() 939 var debugAddr *godwarf.DebugAddr 940 loclist5 := bi.newLoclist5Reader() 941 if cu != nil && cu.Version >= 5 && loclist5 != nil { 942 loclist = loclist5 943 if addrBase, ok := cu.entry.Val(dwarf.AttrAddrBase).(int64); ok { 944 debugAddr = image.debugAddr.GetSubsection(uint64(addrBase)) 945 } 946 } 947 948 if loclist.Empty() { 949 return nil 950 } 951 952 e, err := loclist.Find(int(off), image.StaticBase, base, pc, debugAddr) 953 if err != nil { 954 logger.Logger().Errorf("error reading loclist section: %v", err) 955 return nil 956 } 957 if e != nil { 958 return e.Instr 959 } 960 961 return nil 962 } 963 964 965 func (bi *BinaryInfo) findCompileUnit(pc uint64) *compileUnit { 966 for _, image := range bi.Images { 967 for _, cu := range image.compileUnits { 968 if cu.pcInRange(pc) { 969 return cu 970 } 971 } 972 } 973 return nil 974 } 975 976 func (bi *BinaryInfo) newLoclist2Reader() *loclist.Dwarf2Reader { 977 return loclist.NewDwarf2Reader(bi.debugLocBytes, bi.PointerSize) 978 } 979 980 func (bi *BinaryInfo) newLoclist5Reader() *loclist.Dwarf5Reader { 981 return loclist.NewDwarf5Reader(bi.debugLoclistBytes) 982 } 983 984 985 func (bi *BinaryInfo) PCToImage(pc uint64) *Image { 986 fn := bi.PCToFunc(pc) 987 return fn.cu.image 988 } 989 990 991 992 993 994 func (bi *BinaryInfo) Location(entry godwarf.Entry, attr dwarf.Attr, pc uint64, regs op.DwarfRegisters) (int64, []op.Piece, *LocationExpr, error) { 995 instr, descr, err := bi.LocationExpr(entry, attr, pc) 996 if err != nil { 997 return 0, nil, nil, err 998 } 999 addr, pieces, err := op.ExecuteStackProgram(®s, instr, bi.PointerSize) 1000 return addr, pieces, descr, err 1001 } 1002 1003 1004 func (bi *BinaryInfo) FindType(name string) (godwarf.Type, error) { 1005 ref, found := bi.types[name] 1006 if !found { 1007 return nil, errors.New("no type entry found, use 'types' for a list of valid types") 1008 } 1009 image := bi.Images[ref.imageIndex] 1010 return godwarf.ReadType(image.Dwarf, ref.imageIndex, ref.offset, &image.TypeCache) 1011 } 1012 1013 func (bi *BinaryInfo) GetConst(typ godwarf.Type) *constantType { 1014 return bi.consts.Get(typ) 1015 } 1016 1017 func (bi *BinaryInfo) ReadVariableEntry(entry *godwarf.Tree) (name string, typ godwarf.Type, err error) { 1018 name, ok := entry.Val(dwarf.AttrName).(string) 1019 if !ok { 1020 return "", nil, fmt.Errorf("malformed variable DIE (name)") 1021 } 1022 1023 typ, err = entry.Type(bi.Dwarf, 0, &bi.TypeCache) 1024 if err != nil { 1025 return "", nil, err 1026 } 1027 1028 return name, typ, nil 1029 } 1030 1031 func (bi *BinaryInfo) RuntimeTypeTypename() string { 1032 if GoVersionAfterOrEqual(1, 21) { 1033 return "internal/abi.Type" 1034 } 1035 return "runtime._type" 1036 }