gitlab.com/Raven-IO/raven-delve@v1.22.4/pkg/proc/bininfo.go (about) 1 package proc 2 3 import ( 4 "bytes" 5 "debug/dwarf" 6 "debug/elf" 7 "debug/macho" 8 "debug/pe" 9 "encoding/binary" 10 "encoding/hex" 11 "errors" 12 "fmt" 13 "go/ast" 14 "go/token" 15 "hash/crc32" 16 "io" 17 "os" 18 "path/filepath" 19 "sort" 20 "strconv" 21 "strings" 22 "sync" 23 "time" 24 25 "github.com/hashicorp/golang-lru/simplelru" 26 pdwarf "gitlab.com/Raven-IO/raven-delve/pkg/dwarf" 27 "gitlab.com/Raven-IO/raven-delve/pkg/dwarf/frame" 28 "gitlab.com/Raven-IO/raven-delve/pkg/dwarf/godwarf" 29 "gitlab.com/Raven-IO/raven-delve/pkg/dwarf/line" 30 "gitlab.com/Raven-IO/raven-delve/pkg/dwarf/loclist" 31 "gitlab.com/Raven-IO/raven-delve/pkg/dwarf/op" 32 "gitlab.com/Raven-IO/raven-delve/pkg/dwarf/reader" 33 "gitlab.com/Raven-IO/raven-delve/pkg/goversion" 34 "gitlab.com/Raven-IO/raven-delve/pkg/internal/gosym" 35 "gitlab.com/Raven-IO/raven-delve/pkg/logflags" 36 "gitlab.com/Raven-IO/raven-delve/pkg/proc/debuginfod" 37 ) 38 39 const ( 40 dwarfGoLanguage = 22 // DW_LANG_Go (from DWARF v5, section 7.12, page 231) 41 dwarfAttrAddrBase = 0x74 // debug/dwarf.AttrAddrBase in Go 1.14, defined here for compatibility with Go < 1.14 42 dwarfTreeCacheSize = 512 // size of the dwarfTree cache of each image 43 ) 44 45 // BinaryInfo holds information on the binaries being executed (this 46 // includes both the executable and also any loaded libraries). 47 type BinaryInfo struct { 48 // Architecture of this binary. 49 Arch *Arch 50 51 // GOOS operating system this binary is executing on. 52 GOOS string 53 54 DebugInfoDirectories []string 55 56 // BuildID of this binary. 57 BuildID string 58 59 // Functions is a list of all DW_TAG_subprogram entries in debug_info, sorted by entry point 60 Functions []Function 61 // Sources is a list of all source files found in debug_line. 62 Sources []string 63 // lookupFunc maps function names to a description of the function. 64 lookupFunc map[string][]*Function 65 // lookupGenericFunc maps function names, with their type parameters removed, to functions. 66 // Functions that are not generic are not added to this map. 67 lookupGenericFunc map[string][]*Function 68 69 // SymNames maps addr to a description *elf.Symbol of this addr. 70 SymNames map[uint64]*elf.Symbol 71 72 // Images is a list of loaded shared libraries (also known as 73 // shared objects on linux or DLLs on windows). 74 Images []*Image 75 76 ElfDynamicSection ElfDynamicSection 77 78 lastModified time.Time // Time the executable of this process was last modified 79 80 // PackageMap maps package names to package paths, needed to lookup types inside DWARF info. 81 // On Go1.12 this mapping is determined by using the last element of a package path, for example: 82 // gitlab.com/Raven-IO/raven-delve 83 // will map to 'delve' because it ends in '/delve'. 84 // Starting with Go1.13 debug_info will contain a special attribute 85 // (godwarf.AttrGoPackageName) containing the canonical package name for 86 // each package. 87 // If multiple packages have the same name the map entry will have more 88 // than one item in the slice. 89 PackageMap map[string][]string 90 91 frameEntries frame.FrameDescriptionEntries 92 93 types map[string]dwarfRef 94 packageVars []packageVar // packageVars is a list of all global/package variables in debug_info, sorted by address 95 96 gStructOffset uint64 97 gStructOffsetIsPtr bool 98 99 // consts[off] lists all the constants with the type defined at offset off. 100 consts constantsMap 101 102 // inlinedCallLines maps a file:line pair, corresponding to the header line 103 // of a function to a list of PC addresses where an inlined call to that 104 // function starts. 105 inlinedCallLines map[fileLine][]uint64 106 107 // dwrapUnwrapCache caches unwrapping of defer wrapper functions (dwrap) 108 dwrapUnwrapCache map[uint64]*Function 109 110 // Go 1.17 register ABI is enabled. 111 regabi bool 112 113 logger logflags.Logger 114 } 115 116 var ( 117 // ErrCouldNotDetermineRelocation is an error returned when Delve could not determine the base address of a 118 // position independent executable. 119 ErrCouldNotDetermineRelocation = errors.New("could not determine the base address of a PIE") 120 121 // ErrNoDebugInfoFound is returned when Delve cannot open the debug_info 122 // section or find an external debug info file. 123 ErrNoDebugInfoFound = errors.New("could not open debug info") 124 ) 125 126 var ( 127 supportedLinuxArch = map[elf.Machine]bool{ 128 elf.EM_X86_64: true, 129 elf.EM_AARCH64: true, 130 elf.EM_386: true, 131 elf.EM_PPC64: true, 132 } 133 134 supportedWindowsArch = map[_PEMachine]bool{ 135 _IMAGE_FILE_MACHINE_AMD64: true, 136 _IMAGE_FILE_MACHINE_ARM64: true, 137 } 138 139 supportedDarwinArch = map[macho.Cpu]bool{ 140 macho.CpuAmd64: true, 141 macho.CpuArm64: true, 142 } 143 ) 144 145 // ErrFunctionNotFound is returned when failing to find the 146 // function named 'FuncName' within the binary. 147 type ErrFunctionNotFound struct { 148 FuncName string 149 } 150 151 func (err *ErrFunctionNotFound) Error() string { 152 return fmt.Sprintf("could not find function %s\n", err.FuncName) 153 } 154 155 // FindFileLocation returns the PC for a given file:line. 156 // Assumes that `file` is normalized to lower case and '/' on Windows. 157 func FindFileLocation(p Process, filename string, lineno int) ([]uint64, error) { 158 // A single file:line can appear in multiple concrete functions, because of 159 // generics instantiation as well as multiple inlined calls into other 160 // concrete functions. 161 162 // 1. Find all instructions assigned in debug_line to filename:lineno. 163 164 bi := p.BinInfo() 165 166 fileFound := false 167 pcs := []line.PCStmt{} 168 for _, image := range bi.Images { 169 for _, cu := range image.compileUnits { 170 if cu.lineInfo == nil || cu.lineInfo.Lookup[filename] == nil { 171 continue 172 } 173 174 fileFound = true 175 pcs = append(pcs, cu.lineInfo.LineToPCs(filename, lineno)...) 176 } 177 } 178 179 if len(pcs) == 0 { 180 // Check if the line contained a call to a function that was inlined, in 181 // that case it's possible for the line itself to not appear in debug_line 182 // at all, but it will still be in debug_info as the call site for an 183 // inlined subroutine entry. 184 for _, pc := range bi.inlinedCallLines[fileLine{filename, lineno}] { 185 pcs = append(pcs, line.PCStmt{PC: pc, Stmt: true}) 186 } 187 } 188 189 if len(pcs) == 0 { 190 return nil, &ErrCouldNotFindLine{fileFound, filename, lineno} 191 } 192 193 // 2. assign all occurrences of filename:lineno to their containing function 194 195 pcByFunc := map[*Function][]line.PCStmt{} 196 sort.Slice(pcs, func(i, j int) bool { return pcs[i].PC < pcs[j].PC }) 197 var fn *Function 198 for _, pcstmt := range pcs { 199 if fn == nil || (pcstmt.PC < fn.Entry) || (pcstmt.PC >= fn.End) { 200 fn = p.BinInfo().PCToFunc(pcstmt.PC) 201 } 202 if fn != nil { 203 pcByFunc[fn] = append(pcByFunc[fn], pcstmt) 204 } 205 } 206 207 selectedPCs := []uint64{} 208 209 for fn, pcs := range pcByFunc { 210 211 // 3. for each concrete function split instruction between the inlined functions it contains 212 213 if strings.Contains(fn.Name, "·dwrap·") || fn.trampoline { 214 // skip autogenerated functions 215 continue 216 } 217 218 dwtree, err := fn.cu.image.getDwarfTree(fn.offset) 219 if err != nil { 220 return nil, fmt.Errorf("loading DWARF for %s@%#x: %v", fn.Name, fn.offset, err) 221 } 222 inlrngs := allInlineCallRanges(dwtree) 223 224 // findInlRng returns the DWARF offset of the inlined call containing pc. 225 // If multiple nested inlined calls contain pc the deepest one is returned 226 // (since allInlineCallRanges returns inlined call by decreasing depth 227 // this is the first matching entry of the slice). 228 findInlRng := func(pc uint64) dwarf.Offset { 229 for _, inlrng := range inlrngs { 230 if inlrng.rng[0] <= pc && pc < inlrng.rng[1] { 231 return inlrng.off 232 } 233 } 234 return fn.offset 235 } 236 237 pcsByOff := map[dwarf.Offset][]line.PCStmt{} 238 239 for _, pc := range pcs { 240 off := findInlRng(pc.PC) 241 pcsByOff[off] = append(pcsByOff[off], pc) 242 } 243 244 // 4. pick the first instruction with stmt set for each inlined call as 245 // well as the main body of the concrete function. If nothing has 246 // is_stmt set pick the first instruction instead. 247 248 for off, pcs := range pcsByOff { 249 sort.Slice(pcs, func(i, j int) bool { return pcs[i].PC < pcs[j].PC }) 250 251 var selectedPC uint64 252 for _, pc := range pcs { 253 if pc.Stmt { 254 selectedPC = pc.PC 255 break 256 } 257 } 258 259 if selectedPC == 0 && len(pcs) > 0 { 260 selectedPC = pcs[0].PC 261 } 262 263 if selectedPC == 0 { 264 continue 265 } 266 267 // 5. if we picked the entry point of the function, skip it 268 269 if off == fn.offset && fn.Entry == selectedPC { 270 selectedPC, _ = FirstPCAfterPrologue(p, fn, true) 271 } 272 273 selectedPCs = append(selectedPCs, selectedPC) 274 } 275 } 276 277 sort.Slice(selectedPCs, func(i, j int) bool { return selectedPCs[i] < selectedPCs[j] }) 278 279 return selectedPCs, nil 280 } 281 282 // inlRange is the range of an inlined call 283 type inlRange struct { 284 off dwarf.Offset 285 depth uint32 286 rng [2]uint64 287 } 288 289 // allInlineCallRanges returns all inlined calls contained inside 'tree' in 290 // reverse nesting order (i.e. the most nested calls are returned first). 291 // Note that a single inlined call might not have a continuous range of 292 // addresses and therefore appear multiple times in the returned slice. 293 func allInlineCallRanges(tree *godwarf.Tree) []inlRange { 294 r := []inlRange{} 295 296 var visit func(*godwarf.Tree, uint32) 297 visit = func(n *godwarf.Tree, depth uint32) { 298 if n.Tag == dwarf.TagInlinedSubroutine { 299 for _, rng := range n.Ranges { 300 r = append(r, inlRange{off: n.Offset, depth: depth, rng: rng}) 301 } 302 } 303 for _, child := range n.Children { 304 visit(child, depth+1) 305 } 306 } 307 visit(tree, 0) 308 309 sort.SliceStable(r, func(i, j int) bool { return r[i].depth > r[j].depth }) 310 return r 311 } 312 313 // FindFunction returns the functions with name funcName. 314 func (bi *BinaryInfo) FindFunction(funcName string) ([]*Function, error) { 315 if fns := bi.LookupFunc()[funcName]; fns != nil { 316 return fns, nil 317 } 318 fns := bi.LookupGenericFunc()[funcName] 319 if len(fns) == 0 { 320 return nil, &ErrFunctionNotFound{funcName} 321 } 322 return fns, nil 323 } 324 325 // FindFunctionLocation finds address of a function's line 326 // If lineOffset is passed FindFunctionLocation will return the address of that line 327 func FindFunctionLocation(p Process, funcName string, lineOffset int) ([]uint64, error) { 328 bi := p.BinInfo() 329 origfns, err := bi.FindFunction(funcName) 330 if err != nil { 331 return nil, err 332 } 333 334 if lineOffset > 0 { 335 fn := origfns[0] 336 filename, lineno := bi.EntryLineForFunc(fn) 337 return FindFileLocation(p, filename, lineno+lineOffset) 338 } 339 340 r := make([]uint64, 0, len(origfns[0].InlinedCalls)+len(origfns)) 341 342 for _, origfn := range origfns { 343 if origfn.Entry > 0 { 344 // add concrete implementation of the function 345 pc, err := FirstPCAfterPrologue(p, origfn, false) 346 if err != nil { 347 return nil, err 348 } 349 r = append(r, pc) 350 } 351 // add inlined calls to the function 352 for _, call := range origfn.InlinedCalls { 353 r = append(r, call.LowPC) 354 } 355 if len(r) == 0 { 356 return nil, &ErrFunctionNotFound{funcName} 357 } 358 } 359 sort.Slice(r, func(i, j int) bool { return r[i] < r[j] }) 360 return r, nil 361 } 362 363 // FirstPCAfterPrologue returns the address of the first 364 // instruction after the prologue for function fn. 365 // If sameline is set FirstPCAfterPrologue will always return an 366 // address associated with the same line as fn.Entry. 367 func FirstPCAfterPrologue(p Process, fn *Function, sameline bool) (uint64, error) { 368 if fn.cu.lineInfo != nil { 369 pc, _, line, ok := fn.cu.lineInfo.PrologueEndPC(fn.Entry, fn.End) 370 if ok { 371 if !sameline { 372 return pc, nil 373 } 374 _, entryLine := p.BinInfo().EntryLineForFunc(fn) 375 if entryLine == line { 376 return pc, nil 377 } 378 } 379 } 380 381 pc, err := firstPCAfterPrologueDisassembly(p, fn, sameline) 382 if err != nil { 383 return fn.Entry, err 384 } 385 386 if pc == fn.Entry && fn.cu.lineInfo != nil { 387 // Look for the first instruction with the stmt flag set, so that setting a 388 // breakpoint with file:line and with the function name always result on 389 // the same instruction being selected. 390 if pc2, _, _, ok := fn.cu.lineInfo.FirstStmtForLine(fn.Entry, fn.End); ok { 391 return pc2, nil 392 } 393 } 394 395 return pc, nil 396 } 397 398 func findRetPC(t *Target, name string) ([]uint64, error) { 399 fn := t.BinInfo().lookupOneFunc(name) 400 if fn == nil { 401 return nil, fmt.Errorf("could not find %s", name) 402 } 403 text, err := Disassemble(t.Memory(), nil, t.Breakpoints(), t.BinInfo(), fn.Entry, fn.End) 404 if err != nil { 405 return nil, err 406 } 407 r := []uint64{} 408 for _, instr := range text { 409 if instr.IsRet() { 410 r = append(r, instr.Loc.PC) 411 } 412 } 413 if len(r) == 0 { 414 return nil, fmt.Errorf("could not find return instruction in %s", name) 415 } 416 return r, nil 417 } 418 419 // cpuArch is a stringer interface representing CPU architectures. 420 type cpuArch interface { 421 String() string 422 } 423 424 // ErrUnsupportedArch is returned when attempting to debug a binary compiled for an unsupported architecture. 425 type ErrUnsupportedArch struct { 426 os string 427 cpuArch cpuArch 428 } 429 430 func (e *ErrUnsupportedArch) Error() string { 431 var supportArchs []cpuArch 432 switch e.os { 433 case "linux": 434 for linuxArch := range supportedLinuxArch { 435 supportArchs = append(supportArchs, linuxArch) 436 } 437 case "windows": 438 for windowArch := range supportedWindowsArch { 439 supportArchs = append(supportArchs, windowArch) 440 } 441 case "darwin": 442 for darwinArch := range supportedDarwinArch { 443 supportArchs = append(supportArchs, darwinArch) 444 } 445 } 446 447 errStr := "unsupported architecture of " + e.os + "/" + e.cpuArch.String() 448 errStr += " - only" 449 for _, arch := range supportArchs { 450 errStr += " " + e.os + "/" + arch.String() + " " 451 } 452 if len(supportArchs) == 1 { 453 errStr += "is supported" 454 } else { 455 errStr += "are supported" 456 } 457 458 return errStr 459 } 460 461 type compileUnit struct { 462 name string // univocal name for non-go compile units 463 Version uint8 // DWARF version of this compile unit 464 lowPC uint64 465 ranges [][2]uint64 466 467 entry *dwarf.Entry // debug_info entry describing this compile unit 468 isgo bool // true if this is the go compile unit 469 lineInfo *line.DebugLineInfo // debug_line segment associated with this compile unit 470 optimized bool // this compile unit is optimized 471 producer string // producer attribute 472 473 offset dwarf.Offset // offset of the entry describing the compile unit 474 475 image *Image // parent image of this compilation unit. 476 } 477 478 type fileLine struct { 479 file string 480 line int 481 } 482 483 // dwarfRef is a reference to a Debug Info Entry inside a shared object. 484 type dwarfRef struct { 485 imageIndex int 486 offset dwarf.Offset 487 } 488 489 // InlinedCall represents a concrete inlined call to a function. 490 type InlinedCall struct { 491 cu *compileUnit 492 LowPC, HighPC uint64 // Address range of the generated inlined instructions 493 } 494 495 // Function describes a function in the target program. 496 type Function struct { 497 Name string 498 Entry, End uint64 // same as DW_AT_lowpc and DW_AT_highpc 499 offset dwarf.Offset 500 cu *compileUnit 501 502 trampoline bool // DW_AT_trampoline attribute set to true 503 504 // InlinedCalls lists all inlined calls to this function 505 InlinedCalls []InlinedCall 506 // closureStructType is the cached struct type for closures for this function 507 closureStructTypeCached *godwarf.StructType 508 } 509 510 // instRange returns the indexes in fn.Name of the type parameter 511 // instantiation, which is the position of the outermost '[' and ']'. 512 // If fn is not an instantiated function both returned values will be len(fn.Name) 513 func (fn *Function) instRange() [2]int { 514 d := len(fn.Name) 515 inst := [2]int{d, d} 516 if strings.HasPrefix(fn.Name, "type..") { 517 return inst 518 } 519 inst[0] = strings.Index(fn.Name, "[") 520 if inst[0] < 0 { 521 inst[0] = d 522 return inst 523 } 524 inst[1] = strings.LastIndex(fn.Name, "]") 525 if inst[1] < 0 { 526 inst[0] = d 527 inst[1] = d 528 return inst 529 } 530 return inst 531 } 532 533 // PackageName returns the package part of the symbol name, 534 // or the empty string if there is none. 535 // Borrowed from $GOROOT/debug/gosym/symtab.go 536 func (fn *Function) PackageName() string { 537 inst := fn.instRange() 538 return packageName(fn.Name[:inst[0]]) 539 } 540 541 func packageName(name string) string { 542 pathend := strings.LastIndex(name, "/") 543 if pathend < 0 { 544 pathend = 0 545 } 546 547 if i := strings.Index(name[pathend:], "."); i != -1 { 548 return name[:pathend+i] 549 } 550 return "" 551 } 552 553 // ReceiverName returns the receiver type name of this symbol, 554 // or the empty string if there is none. 555 // Borrowed from $GOROOT/debug/gosym/symtab.go 556 func (fn *Function) ReceiverName() string { 557 inst := fn.instRange() 558 pathend := strings.LastIndex(fn.Name[:inst[0]], "/") 559 if pathend < 0 { 560 pathend = 0 561 } 562 l := strings.Index(fn.Name[pathend:], ".") 563 if l == -1 { 564 return "" 565 } 566 if r := strings.LastIndex(fn.Name[inst[1]:], "."); r != -1 && pathend+l != inst[1]+r { 567 return fn.Name[pathend+l+1 : inst[1]+r] 568 } else if r := strings.LastIndex(fn.Name[pathend:inst[0]], "."); r != -1 && l != r { 569 return fn.Name[pathend+l+1 : pathend+r] 570 } 571 return "" 572 } 573 574 // BaseName returns the symbol name without the package or receiver name. 575 // Borrowed from $GOROOT/debug/gosym/symtab.go 576 func (fn *Function) BaseName() string { 577 inst := fn.instRange() 578 if i := strings.LastIndex(fn.Name[inst[1]:], "."); i != -1 { 579 return fn.Name[inst[1]+i+1:] 580 } else if i := strings.LastIndex(fn.Name[:inst[0]], "."); i != -1 { 581 return fn.Name[i+1:] 582 } 583 return fn.Name 584 } 585 586 // NameWithoutTypeParams returns the function name without instantiation parameters 587 func (fn *Function) NameWithoutTypeParams() string { 588 inst := fn.instRange() 589 if inst[0] == inst[1] { 590 return fn.Name 591 } 592 return fn.Name[:inst[0]] + fn.Name[inst[1]+1:] 593 } 594 595 // Optimized returns true if the function was optimized by the compiler. 596 func (fn *Function) Optimized() bool { 597 return fn.cu.optimized 598 } 599 600 // PrologueEndPC returns the PC just after the function prologue 601 func (fn *Function) PrologueEndPC() uint64 { 602 pc, _, _, ok := fn.cu.lineInfo.PrologueEndPC(fn.Entry, fn.End) 603 if !ok { 604 return fn.Entry 605 } 606 return pc 607 } 608 609 func (fn *Function) AllPCs(excludeFile string, excludeLine int) ([]uint64, error) { 610 if !fn.cu.image.Stripped() { 611 return fn.cu.lineInfo.AllPCsBetween(fn.Entry, fn.End-1, excludeFile, excludeLine) 612 } 613 var pcs []uint64 614 fnFile, lastLine, _ := fn.cu.image.symTable.PCToLine(fn.Entry - fn.cu.image.StaticBase) 615 for pc := fn.Entry - fn.cu.image.StaticBase; pc < fn.End-fn.cu.image.StaticBase; pc++ { 616 f, line, pcfn := fn.cu.image.symTable.PCToLine(pc) 617 if pcfn == nil { 618 continue 619 } 620 if f == fnFile && line > lastLine { 621 lastLine = line 622 pcs = append(pcs, pc+fn.cu.image.StaticBase) 623 } 624 } 625 return pcs, nil 626 } 627 628 // From $GOROOT/src/runtime/traceback.go:597 629 // exportedRuntime reports whether the function is an exported runtime function. 630 // It is only for runtime functions, so ASCII A-Z is fine. 631 func (fn *Function) exportedRuntime() bool { 632 name := fn.Name 633 const n = len("runtime.") 634 return len(name) > n && name[:n] == "runtime." && 'A' <= name[n] && name[n] <= 'Z' 635 } 636 637 // unexportedRuntime reports whether the function is a private runtime function. 638 func (fn *Function) privateRuntime() bool { 639 name := fn.Name 640 const n = len("runtime.") 641 return len(name) > n && name[:n] == "runtime." && !('A' <= name[n] && name[n] <= 'Z') 642 } 643 644 func (fn *Function) closureStructType(bi *BinaryInfo) *godwarf.StructType { 645 if fn.closureStructTypeCached != nil { 646 return fn.closureStructTypeCached 647 } 648 dwarfTree, err := fn.cu.image.getDwarfTree(fn.offset) 649 if err != nil { 650 return nil 651 } 652 st := &godwarf.StructType{ 653 Kind: "struct", 654 } 655 vars := reader.Variables(dwarfTree, 0, 0, reader.VariablesNoDeclLineCheck|reader.VariablesSkipInlinedSubroutines) 656 for _, v := range vars { 657 off, ok := v.Val(godwarf.AttrGoClosureOffset).(int64) 658 if ok { 659 n, _ := v.Val(dwarf.AttrName).(string) 660 typ, err := v.Type(fn.cu.image.dwarf, fn.cu.image.index, fn.cu.image.typeCache) 661 if err == nil { 662 sz := typ.Common().ByteSize 663 st.Field = append(st.Field, &godwarf.StructField{ 664 Name: n, 665 Type: typ, 666 ByteOffset: off, 667 ByteSize: sz, 668 BitOffset: off * 8, 669 BitSize: sz * 8, 670 }) 671 } 672 } 673 } 674 675 if len(st.Field) > 0 { 676 lf := st.Field[len(st.Field)-1] 677 st.ByteSize = lf.ByteOffset + lf.Type.Common().ByteSize 678 } 679 fn.closureStructTypeCached = st 680 return st 681 } 682 683 type constantsMap map[dwarfRef]*constantType 684 685 type constantType struct { 686 initialized bool 687 values []constantValue 688 } 689 690 type constantValue struct { 691 name string 692 fullName string 693 value int64 694 singleBit bool 695 } 696 697 // packageVar represents a package-level variable (or a C global variable). 698 // If a global variable does not have an address (for example it's stored in 699 // a register, or non-contiguously) addr will be 0. 700 type packageVar struct { 701 name string 702 cu *compileUnit 703 offset dwarf.Offset 704 addr uint64 705 } 706 707 type buildIDHeader struct { 708 Namesz uint32 709 Descsz uint32 710 Type uint32 711 } 712 713 // ElfDynamicSection describes the .dynamic section of an ELF executable. 714 type ElfDynamicSection struct { 715 Addr uint64 // relocated address of where the .dynamic section is mapped in memory 716 Size uint64 // size of the .dynamic section of the executable 717 } 718 719 // NewBinaryInfo returns an initialized but unloaded BinaryInfo struct. 720 func NewBinaryInfo(goos, goarch string) *BinaryInfo { 721 r := &BinaryInfo{GOOS: goos, logger: logflags.DebuggerLogger()} 722 723 // TODO: find better way to determine proc arch (perhaps use executable file info). 724 switch goarch { 725 case "386": 726 r.Arch = I386Arch(goos) 727 case "amd64": 728 r.Arch = AMD64Arch(goos) 729 case "arm64": 730 r.Arch = ARM64Arch(goos) 731 case "ppc64le": 732 r.Arch = PPC64LEArch(goos) 733 } 734 return r 735 } 736 737 // LoadBinaryInfo will load and store the information from the binary at 'path'. 738 func (bi *BinaryInfo) LoadBinaryInfo(path string, entryPoint uint64, debugInfoDirs []string) error { 739 fi, err := os.Stat(path) 740 if err == nil { 741 bi.lastModified = fi.ModTime() 742 } 743 744 bi.DebugInfoDirectories = debugInfoDirs 745 746 return bi.AddImage(path, entryPoint) 747 } 748 749 func loadBinaryInfo(bi *BinaryInfo, image *Image, path string, entryPoint uint64) error { 750 var wg sync.WaitGroup 751 defer wg.Wait() 752 753 switch bi.GOOS { 754 case "linux", "freebsd": 755 return loadBinaryInfoElf(bi, image, path, entryPoint, &wg) 756 case "windows": 757 return loadBinaryInfoPE(bi, image, path, entryPoint, &wg) 758 case "darwin": 759 return loadBinaryInfoMacho(bi, image, path, entryPoint, &wg) 760 } 761 return errors.New("unsupported operating system") 762 } 763 764 // GStructOffset returns the offset of the G 765 // struct in thread local storage. 766 func (bi *BinaryInfo) GStructOffset(mem MemoryReadWriter) (uint64, error) { 767 offset := bi.gStructOffset 768 if bi.gStructOffsetIsPtr { 769 // The G struct offset from the TLS section is a pointer 770 // and the address must be dereferenced to find to actual G struct offset. 771 var err error 772 offset, err = readUintRaw(mem, offset, int64(bi.Arch.PtrSize())) 773 if err != nil { 774 return 0, err 775 } 776 } 777 return offset, nil 778 } 779 780 // LastModified returns the last modified time of the binary. 781 func (bi *BinaryInfo) LastModified() time.Time { 782 return bi.lastModified 783 } 784 785 // DwarfReader returns a reader for the dwarf data 786 func (so *Image) DwarfReader() *reader.Reader { 787 if so.dwarf == nil { 788 return nil 789 } 790 return reader.New(so.dwarf) 791 } 792 793 // Types returns list of types present in the debugged program. 794 func (bi *BinaryInfo) Types() ([]string, error) { 795 types := make([]string, 0, len(bi.types)) 796 for k := range bi.types { 797 types = append(types, k) 798 } 799 return types, nil 800 } 801 802 func (bi *BinaryInfo) EntryLineForFunc(fn *Function) (string, int) { 803 return bi.pcToLine(fn, fn.Entry) 804 } 805 806 func (bi *BinaryInfo) pcToLine(fn *Function, pc uint64) (string, int) { 807 if fn.cu.lineInfo == nil { 808 f, l, _ := fn.cu.image.symTable.PCToLine(pc - fn.cu.image.StaticBase) 809 return f, l 810 } 811 f, l := fn.cu.lineInfo.PCToLine(fn.Entry, pc) 812 return f, l 813 } 814 815 // PCToLine converts an instruction address to a file/line/function. 816 func (bi *BinaryInfo) PCToLine(pc uint64) (string, int, *Function) { 817 fn := bi.PCToFunc(pc) 818 if fn == nil { 819 return "", 0, nil 820 } 821 f, ln := bi.pcToLine(fn, pc) 822 return f, ln, fn 823 } 824 825 type ErrCouldNotFindLine struct { 826 fileFound bool 827 filename string 828 lineno int 829 } 830 831 func (err *ErrCouldNotFindLine) Error() string { 832 if err.fileFound { 833 return fmt.Sprintf("could not find statement at %s:%d, please use a line with a statement", err.filename, err.lineno) 834 } 835 return fmt.Sprintf("could not find file %s", err.filename) 836 } 837 838 // AllPCsForFileLines returns a map providing all PC addresses for filename and each line in linenos 839 func (bi *BinaryInfo) AllPCsForFileLines(filename string, linenos []int) map[int][]uint64 { 840 r := make(map[int][]uint64) 841 for _, line := range linenos { 842 r[line] = make([]uint64, 0, 1) 843 } 844 for _, image := range bi.Images { 845 for _, cu := range image.compileUnits { 846 if cu.lineInfo != nil && cu.lineInfo.Lookup[filename] != nil { 847 cu.lineInfo.AllPCsForFileLines(filename, r) 848 } 849 } 850 } 851 return r 852 } 853 854 // PCToFunc returns the concrete function containing the given PC address. 855 // If the PC address belongs to an inlined call it will return the containing function. 856 func (bi *BinaryInfo) PCToFunc(pc uint64) *Function { 857 i := sort.Search(len(bi.Functions), func(i int) bool { 858 fn := bi.Functions[i] 859 return pc <= fn.Entry || (fn.Entry <= pc && pc < fn.End) 860 }) 861 if i != len(bi.Functions) { 862 fn := &bi.Functions[i] 863 if fn.Entry <= pc && pc < fn.End { 864 return fn 865 } 866 } 867 return nil 868 } 869 870 // PCToImage returns the image containing the given PC address. 871 func (bi *BinaryInfo) PCToImage(pc uint64) *Image { 872 fn := bi.PCToFunc(pc) 873 return bi.funcToImage(fn) 874 } 875 876 // Image represents a loaded library file (shared object on linux, DLL on windows). 877 type Image struct { 878 Path string 879 StaticBase uint64 880 addr uint64 881 882 index int // index of this object in BinaryInfo.SharedObjects 883 884 closer io.Closer 885 sepDebugCloser io.Closer 886 887 dwarf *dwarf.Data 888 dwarfReader *dwarf.Reader 889 loclist2 *loclist.Dwarf2Reader 890 loclist5 *loclist.Dwarf5Reader 891 debugAddr *godwarf.DebugAddrSection 892 debugLineStr []byte 893 894 symTable *gosym.Table 895 896 typeCache map[dwarf.Offset]godwarf.Type 897 898 compileUnits []*compileUnit // compileUnits is sorted by increasing DWARF offset 899 900 dwarfTreeCache *simplelru.LRU 901 workaroundCache map[dwarf.Offset]*godwarf.Tree 902 903 // runtimeTypeToDIE maps between the offset of a runtime._type in 904 // runtime.moduledata.types and the offset of the DIE in debug_info. This 905 // map is filled by using the extended attribute godwarf.AttrGoRuntimeType 906 // which was added in go 1.11. 907 runtimeTypeToDIE map[uint64]runtimeTypeDIE 908 909 loadErrMu sync.Mutex 910 loadErr error 911 } 912 913 func (image *Image) registerRuntimeTypeToDIE(entry *dwarf.Entry, ardr *reader.Reader) { 914 if off, ok := entry.Val(godwarf.AttrGoRuntimeType).(uint64); ok { 915 if _, ok := image.runtimeTypeToDIE[off]; !ok { 916 image.runtimeTypeToDIE[off] = runtimeTypeDIE{entry.Offset, -1} 917 } 918 } 919 } 920 921 func (image *Image) Stripped() bool { 922 return image.dwarf == nil 923 } 924 925 // AddImage adds the specified image to bi, loading data asynchronously. 926 // Addr is the relocated entry point for the executable and staticBase (i.e. 927 // the relocation offset) for all other images. 928 // The first image added must be the executable file. 929 func (bi *BinaryInfo) AddImage(path string, addr uint64) error { 930 // Check if the image is already present. 931 if len(bi.Images) > 0 && !strings.HasPrefix(path, "/") { 932 return nil 933 } 934 for _, image := range bi.Images { 935 if image.Path == path && image.addr == addr { 936 return nil 937 } 938 } 939 940 // Actually add the image. 941 image := &Image{Path: path, addr: addr, typeCache: make(map[dwarf.Offset]godwarf.Type)} 942 image.dwarfTreeCache, _ = simplelru.NewLRU(dwarfTreeCacheSize, nil) 943 944 // add Image regardless of error so that we don't attempt to re-add it every time we stop 945 image.index = len(bi.Images) 946 bi.Images = append(bi.Images, image) 947 err := loadBinaryInfo(bi, image, path, addr) 948 if err != nil { 949 bi.Images[len(bi.Images)-1].loadErr = err 950 } 951 bi.macOSDebugFrameBugWorkaround() 952 return err 953 } 954 955 // moduleDataToImage finds the image corresponding to the given module data object. 956 func (bi *BinaryInfo) moduleDataToImage(md *moduleData) *Image { 957 fn := bi.PCToFunc(md.text) 958 if fn != nil { 959 return bi.funcToImage(fn) 960 } 961 // Try searching for the image with the closest address preceding md.text 962 var so *Image 963 for i := range bi.Images { 964 if int64(bi.Images[i].StaticBase) > int64(md.text) { 965 continue 966 } 967 if so == nil || int64(bi.Images[i].StaticBase) > int64(so.StaticBase) { 968 so = bi.Images[i] 969 } 970 } 971 return so 972 } 973 974 // imageToModuleData finds the module data in mds corresponding to the given image. 975 func (bi *BinaryInfo) imageToModuleData(image *Image, mds []moduleData) *moduleData { 976 for _, md := range mds { 977 im2 := bi.moduleDataToImage(&md) 978 if im2 != nil && im2.index == image.index { 979 return &md 980 } 981 } 982 return nil 983 } 984 985 // typeToImage returns the image containing the give type. 986 func (bi *BinaryInfo) typeToImage(typ godwarf.Type) *Image { 987 return bi.Images[typ.Common().Index] 988 } 989 990 func (bi *BinaryInfo) runtimeTypeTypename() string { 991 if goversion.ProducerAfterOrEqual(bi.Producer(), 1, 21) { 992 return "internal/abi.Type" 993 } 994 return "runtime._type" 995 } 996 997 var errBinaryInfoClose = errors.New("multiple errors closing executable files") 998 999 // Close closes all internal readers. 1000 func (bi *BinaryInfo) Close() error { 1001 var errs []error 1002 for _, image := range bi.Images { 1003 if err := image.Close(); err != nil { 1004 errs = append(errs, err) 1005 } 1006 } 1007 switch len(errs) { 1008 case 0: 1009 return nil 1010 case 1: 1011 return errs[0] 1012 default: 1013 return errBinaryInfoClose 1014 } 1015 } 1016 1017 func (image *Image) Close() error { 1018 var err1, err2 error 1019 if image.sepDebugCloser != nil { 1020 err := image.sepDebugCloser.Close() 1021 if err != nil { 1022 err1 = fmt.Errorf("closing shared object %q (split dwarf): %v", image.Path, err) 1023 } 1024 } 1025 if image.closer != nil { 1026 err := image.closer.Close() 1027 if err != nil { 1028 err2 = fmt.Errorf("closing shared object %q: %v", image.Path, err) 1029 } 1030 } 1031 if err1 != nil && err2 != nil { 1032 return errBinaryInfoClose 1033 } 1034 if err1 != nil { 1035 return err1 1036 } 1037 return err2 1038 } 1039 1040 func (image *Image) setLoadError(logger logflags.Logger, fmtstr string, args ...interface{}) { 1041 image.loadErrMu.Lock() 1042 image.loadErr = fmt.Errorf(fmtstr, args...) 1043 image.loadErrMu.Unlock() 1044 if logger != nil { 1045 logger.Errorf("error loading binary %q: %v", image.Path, image.loadErr) 1046 } 1047 } 1048 1049 // LoadError returns any error incurred while loading this image. 1050 func (image *Image) LoadError() error { 1051 return image.loadErr 1052 } 1053 1054 func (image *Image) getDwarfTree(off dwarf.Offset) (*godwarf.Tree, error) { 1055 if image.workaroundCache[off] != nil { 1056 return image.workaroundCache[off], nil 1057 } 1058 if r, ok := image.dwarfTreeCache.Get(off); ok { 1059 return r.(*godwarf.Tree), nil 1060 } 1061 r, err := godwarf.LoadTree(off, image.dwarf, image.StaticBase) 1062 if err != nil { 1063 return nil, err 1064 } 1065 image.dwarfTreeCache.Add(off, r) 1066 return r, nil 1067 } 1068 1069 type nilCloser struct{} 1070 1071 func (c *nilCloser) Close() error { return nil } 1072 1073 // LoadImageFromData creates a new Image, using the specified data, and adds it to bi. 1074 // This is used for debugging BinaryInfo, you should use LoadBinary instead. 1075 func (bi *BinaryInfo) LoadImageFromData(dwdata *dwarf.Data, debugFrameBytes, debugLineBytes, debugLocBytes []byte) { 1076 image := &Image{} 1077 image.closer = (*nilCloser)(nil) 1078 image.sepDebugCloser = (*nilCloser)(nil) 1079 image.dwarf = dwdata 1080 image.typeCache = make(map[dwarf.Offset]godwarf.Type) 1081 image.dwarfTreeCache, _ = simplelru.NewLRU(dwarfTreeCacheSize, nil) 1082 1083 if debugFrameBytes != nil { 1084 bi.frameEntries, _ = frame.Parse(debugFrameBytes, frame.DwarfEndian(debugFrameBytes), 0, bi.Arch.PtrSize(), 0) 1085 } 1086 1087 image.loclist2 = loclist.NewDwarf2Reader(debugLocBytes, bi.Arch.PtrSize()) 1088 1089 bi.loadDebugInfoMaps(image, nil, debugLineBytes, nil, nil) 1090 1091 bi.Images = append(bi.Images, image) 1092 } 1093 1094 func (bi *BinaryInfo) locationExpr(entry godwarf.Entry, attr dwarf.Attr, pc uint64) ([]byte, *locationExpr, error) { 1095 //TODO(aarzilli): handle DW_FORM_loclistx attribute form new in DWARFv5 1096 a := entry.Val(attr) 1097 if a == nil { 1098 return nil, nil, fmt.Errorf("no location attribute %s", attr) 1099 } 1100 if instr, ok := a.([]byte); ok { 1101 return instr, &locationExpr{isBlock: true, instr: instr, regnumToName: bi.Arch.RegnumToString}, nil 1102 } 1103 off, ok := a.(int64) 1104 if !ok { 1105 return nil, nil, fmt.Errorf("could not interpret location attribute %s", attr) 1106 } 1107 instr := bi.loclistEntry(off, pc) 1108 if instr == nil { 1109 return nil, nil, fmt.Errorf("could not find loclist entry at %#x for address %#x", off, pc) 1110 } 1111 return instr, &locationExpr{pc: pc, off: off, instr: instr, regnumToName: bi.Arch.RegnumToString}, nil 1112 } 1113 1114 type locationExpr struct { 1115 isBlock bool 1116 isEscaped bool 1117 off int64 1118 pc uint64 1119 instr []byte 1120 1121 regnumToName func(uint64) string 1122 } 1123 1124 func (le *locationExpr) String() string { 1125 if le == nil { 1126 return "" 1127 } 1128 var descr bytes.Buffer 1129 1130 if le.isBlock { 1131 fmt.Fprintf(&descr, "[block] ") 1132 op.PrettyPrint(&descr, le.instr, le.regnumToName) 1133 } else { 1134 fmt.Fprintf(&descr, "[%#x:%#x] ", le.off, le.pc) 1135 op.PrettyPrint(&descr, le.instr, le.regnumToName) 1136 } 1137 1138 if le.isEscaped { 1139 fmt.Fprintf(&descr, " (escaped)") 1140 } 1141 return descr.String() 1142 } 1143 1144 // LocationCovers returns the list of PC addresses that is covered by the 1145 // location attribute 'attr' of entry 'entry'. 1146 func (bi *BinaryInfo) LocationCovers(entry *dwarf.Entry, attr dwarf.Attr) ([][2]uint64, error) { 1147 a := entry.Val(attr) 1148 if a == nil { 1149 return nil, fmt.Errorf("attribute %s not found", attr) 1150 } 1151 if _, isblock := a.([]byte); isblock { 1152 return [][2]uint64{{0, ^uint64(0)}}, nil 1153 } 1154 1155 off, ok := a.(int64) 1156 if !ok { 1157 return nil, fmt.Errorf("attribute %s of unsupported type %T", attr, a) 1158 } 1159 cu := bi.Images[0].findCompileUnitForOffset(entry.Offset) 1160 if cu == nil { 1161 return nil, errors.New("could not find compile unit") 1162 } 1163 if cu.Version >= 5 && cu.image.loclist5 != nil { 1164 return nil, errors.New("LocationCovers does not support DWARFv5") 1165 } 1166 1167 image := cu.image 1168 base := cu.lowPC 1169 if image == nil || image.loclist2.Empty() { 1170 return nil, errors.New("malformed executable") 1171 } 1172 1173 r := [][2]uint64{} 1174 var e loclist.Entry 1175 image.loclist2.Seek(int(off)) 1176 for image.loclist2.Next(&e) { 1177 if e.BaseAddressSelection() { 1178 base = e.HighPC 1179 continue 1180 } 1181 r = append(r, [2]uint64{e.LowPC + base, e.HighPC + base}) 1182 } 1183 return r, nil 1184 } 1185 1186 // Location returns the location described by attribute attr of entry. 1187 // This will either be an int64 address or a slice of Pieces for locations 1188 // that don't correspond to a single memory address (registers, composite 1189 // locations). 1190 func (bi *BinaryInfo) Location(entry godwarf.Entry, attr dwarf.Attr, pc uint64, regs op.DwarfRegisters, mem MemoryReadWriter) (int64, []op.Piece, *locationExpr, error) { 1191 instr, descr, err := bi.locationExpr(entry, attr, pc) 1192 if err != nil { 1193 return 0, nil, nil, err 1194 } 1195 readMemory := op.ReadMemoryFunc(nil) 1196 if mem != nil { 1197 readMemory = mem.ReadMemory 1198 } 1199 addr, pieces, err := op.ExecuteStackProgram(regs, instr, bi.Arch.PtrSize(), readMemory) 1200 return addr, pieces, descr, err 1201 } 1202 1203 // loclistEntry returns the loclist entry in the loclist starting at off, 1204 // for address pc. 1205 func (bi *BinaryInfo) loclistEntry(off int64, pc uint64) []byte { 1206 var base uint64 1207 image := bi.Images[0] 1208 cu := bi.findCompileUnit(pc) 1209 if cu != nil { 1210 base = cu.lowPC 1211 image = cu.image 1212 } 1213 if image == nil { 1214 return nil 1215 } 1216 1217 var loclist loclist.Reader = image.loclist2 1218 var debugAddr *godwarf.DebugAddr 1219 if cu != nil && cu.Version >= 5 && image.loclist5 != nil { 1220 loclist = image.loclist5 1221 if addrBase, ok := cu.entry.Val(dwarfAttrAddrBase).(int64); ok { 1222 debugAddr = image.debugAddr.GetSubsection(uint64(addrBase)) 1223 } 1224 } 1225 1226 if loclist.Empty() { 1227 return nil 1228 } 1229 1230 e, err := loclist.Find(int(off), image.StaticBase, base, pc, debugAddr) 1231 if err != nil { 1232 bi.logger.Errorf("error reading loclist section: %v", err) 1233 return nil 1234 } 1235 if e != nil { 1236 return e.Instr 1237 } 1238 1239 return nil 1240 } 1241 1242 // findCompileUnit returns the compile unit containing address pc. 1243 func (bi *BinaryInfo) findCompileUnit(pc uint64) *compileUnit { 1244 for _, image := range bi.Images { 1245 for _, cu := range image.compileUnits { 1246 for _, rng := range cu.ranges { 1247 if pc >= rng[0] && pc < rng[1] { 1248 return cu 1249 } 1250 } 1251 } 1252 } 1253 return nil 1254 } 1255 1256 func (bi *Image) findCompileUnitForOffset(off dwarf.Offset) *compileUnit { 1257 i := sort.Search(len(bi.compileUnits), func(i int) bool { 1258 return bi.compileUnits[i].offset >= off 1259 }) 1260 if i > 0 { 1261 i-- 1262 } 1263 return bi.compileUnits[i] 1264 } 1265 1266 // Producer returns the value of DW_AT_producer. 1267 func (bi *BinaryInfo) Producer() string { 1268 for _, cu := range bi.Images[0].compileUnits { 1269 if cu.isgo && cu.producer != "" { 1270 return cu.producer 1271 } 1272 } 1273 return "" 1274 } 1275 1276 // Type returns the Dwarf type entry at `offset`. 1277 func (image *Image) Type(offset dwarf.Offset) (godwarf.Type, error) { 1278 return godwarf.ReadType(image.dwarf, image.index, offset, image.typeCache) 1279 } 1280 1281 // funcToImage returns the Image containing function fn, or the 1282 // executable file as a fallback. 1283 func (bi *BinaryInfo) funcToImage(fn *Function) *Image { 1284 if fn == nil { 1285 return bi.Images[0] 1286 } 1287 return fn.cu.image 1288 } 1289 1290 // parseDebugFrameGeneral parses a debug_frame and a eh_frame section. 1291 // At least one of the two must be present and parsed correctly, if 1292 // debug_frame is present it must be parsable correctly. 1293 func (bi *BinaryInfo) parseDebugFrameGeneral(image *Image, debugFrameBytes []byte, debugFrameName string, debugFrameErr error, ehFrameBytes []byte, ehFrameAddr uint64, ehFrameName string, byteOrder binary.ByteOrder) { 1294 if debugFrameBytes == nil && ehFrameBytes == nil { 1295 image.setLoadError(bi.logger, "could not get %s section: %v", debugFrameName, debugFrameErr) 1296 return 1297 } 1298 1299 if debugFrameBytes != nil { 1300 fe, err := frame.Parse(debugFrameBytes, byteOrder, image.StaticBase, bi.Arch.PtrSize(), 0) 1301 if err != nil { 1302 image.setLoadError(bi.logger, "could not parse %s section: %v", debugFrameName, err) 1303 return 1304 } 1305 bi.frameEntries = bi.frameEntries.Append(fe) 1306 } 1307 1308 if ehFrameBytes != nil && ehFrameAddr > 0 { 1309 fe, err := frame.Parse(ehFrameBytes, byteOrder, image.StaticBase, bi.Arch.PtrSize(), ehFrameAddr) 1310 if err != nil { 1311 if debugFrameBytes == nil { 1312 image.setLoadError(bi.logger, "could not parse %s section: %v", ehFrameName, err) 1313 return 1314 } 1315 bi.logger.Warnf("could not parse %s section: %v", ehFrameName, err) 1316 return 1317 } 1318 bi.frameEntries = bi.frameEntries.Append(fe) 1319 } 1320 } 1321 1322 // ELF /////////////////////////////////////////////////////////////// 1323 1324 // openSeparateDebugInfo searches for a file containing the separate 1325 // debug info for the binary using the "build ID" method as described 1326 // in GDB's documentation [1], and if found returns two handles, one 1327 // for the bare file, and another for its corresponding elf.File. 1328 // [1] https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html 1329 // 1330 // Alternatively, if the debug file cannot be found be the build-id, Delve 1331 // will look in directories specified by the debug-info-directories config value. 1332 func (bi *BinaryInfo) openSeparateDebugInfo(image *Image, exe *elf.File, debugInfoDirectories []string) (*os.File, *elf.File, error) { 1333 exePath := image.Path 1334 exeName := filepath.Base(image.Path) 1335 if strings.HasPrefix(image.Path, "/proc") { 1336 var err error 1337 exePath, err = filepath.EvalSymlinks(image.Path) 1338 if err == nil { 1339 exeName = filepath.Base(exePath) 1340 } 1341 } 1342 1343 var debugFilePath string 1344 1345 check := func(potentialDebugFilePath string) bool { 1346 _, err := os.Stat(potentialDebugFilePath) 1347 if err == nil { 1348 debugFilePath = potentialDebugFilePath 1349 return true 1350 } 1351 return false 1352 } 1353 1354 find := func(f func(string) bool, suffix string) { 1355 for _, dir := range debugInfoDirectories { 1356 if f != nil && !f(dir) { 1357 continue 1358 } 1359 if check(fmt.Sprintf("%s/%s", dir, suffix)) { 1360 break 1361 } 1362 } 1363 } 1364 1365 if debugFilePath == "" && len(bi.BuildID) > 2 { 1366 // Build ID method: look for a file named .build-id/nn/nnnnnnnn.debug in 1367 // every debug info directory. 1368 find(nil, fmt.Sprintf(".build-id/%s/%s.debug", bi.BuildID[:2], bi.BuildID[2:])) 1369 } 1370 1371 if debugFilePath == "" { 1372 // Debug link: method if the executable contains a .gnu_debuglink section 1373 // it will look for the file named in the same directory of the 1374 // executable, then in a subdirectory named .debug and finally in each 1375 // debug info directory in a subdirectory with the same path as the 1376 // directory of the executable 1377 debugLink, crc := bi.getDebugLink(exe) 1378 1379 if debugLink != "" { 1380 check(filepath.Join(filepath.Dir(exePath), debugLink)) 1381 if debugFilePath == "" { 1382 check(filepath.Join(filepath.Dir(exePath), ".debug", debugLink)) 1383 } 1384 if debugFilePath == "" { 1385 suffix := filepath.Join(filepath.Dir(exePath)[1:], debugLink) 1386 find(nil, suffix) 1387 } 1388 if debugFilePath == "" { 1389 bi.logger.Warnf("gnu_debuglink link %q not found in any debug info directory", debugLink) 1390 } 1391 } 1392 1393 if debugFilePath != "" { 1394 // CRC check 1395 buf, err := os.ReadFile(debugFilePath) 1396 if err == nil { 1397 computedCRC := crc32.ChecksumIEEE(buf) 1398 if crc != computedCRC { 1399 bi.logger.Errorf("gnu_debuglink CRC check failed for %s (want %x got %x)", debugFilePath, crc, computedCRC) 1400 debugFilePath = "" 1401 } 1402 1403 } 1404 } 1405 } 1406 1407 if debugFilePath == "" && len(bi.BuildID) > 2 { 1408 // Previous versions of delve looked for the build id in every debug info 1409 // directory that contained the build-id substring. This behavior deviates 1410 // from the ones specified by GDB but we keep it for backwards compatibility. 1411 find(func(dir string) bool { return strings.Contains(dir, "build-id") }, fmt.Sprintf("%s/%s.debug", bi.BuildID[:2], bi.BuildID[2:])) 1412 } 1413 1414 if debugFilePath == "" { 1415 // Previous versions of delve looked for the executable filename (with 1416 // .debug extension) in every debug info directory. This behavior also 1417 // deviates from the ones specified by GDB, but we keep it for backwards 1418 // compatibility. 1419 find(func(dir string) bool { return !strings.Contains(dir, "build-id") }, fmt.Sprintf("%s.debug", exeName)) 1420 } 1421 1422 // We cannot find the debug information locally on the system. Try and see if we're on a system that 1423 // has debuginfod so that we can use that in order to find any relevant debug information. 1424 if debugFilePath == "" { 1425 var err error 1426 debugFilePath, err = debuginfod.GetDebuginfo(bi.BuildID) 1427 if err != nil { 1428 return nil, nil, ErrNoDebugInfoFound 1429 } 1430 } 1431 1432 sepFile, err := os.OpenFile(debugFilePath, 0, os.ModePerm) 1433 if err != nil { 1434 return nil, nil, errors.New("can't open separate debug file: " + err.Error()) 1435 } 1436 1437 elfFile, err := elf.NewFile(sepFile) 1438 if err != nil { 1439 sepFile.Close() 1440 return nil, nil, fmt.Errorf("can't open separate debug file %q: %v", debugFilePath, err.Error()) 1441 } 1442 1443 if !supportedLinuxArch[elfFile.Machine] { 1444 sepFile.Close() 1445 return nil, nil, fmt.Errorf("can't open separate debug file %q: %v", debugFilePath, &ErrUnsupportedArch{os: "linux", cpuArch: elfFile.Machine}) 1446 } 1447 1448 return sepFile, elfFile, nil 1449 } 1450 1451 // loadBinaryInfoElf specifically loads information from an ELF binary. 1452 func loadBinaryInfoElf(bi *BinaryInfo, image *Image, path string, addr uint64, wg *sync.WaitGroup) error { 1453 exe, err := os.OpenFile(path, 0, os.ModePerm) 1454 if err != nil { 1455 return err 1456 } 1457 image.closer = exe 1458 elfFile, err := elf.NewFile(exe) 1459 if err != nil { 1460 return err 1461 } 1462 if !supportedLinuxArch[elfFile.Machine] { 1463 return &ErrUnsupportedArch{os: "linux", cpuArch: elfFile.Machine} 1464 } 1465 1466 if image.index == 0 { 1467 // adding executable file: 1468 // - addr is entryPoint therefore staticBase needs to be calculated by 1469 // subtracting the entry point specified in the executable file from addr. 1470 // - memory address of the .dynamic section needs to be recorded in 1471 // BinaryInfo so that we can find loaded libraries. 1472 if addr != 0 { 1473 image.StaticBase = addr - elfFile.Entry 1474 } else if elfFile.Type == elf.ET_DYN { 1475 return ErrCouldNotDetermineRelocation 1476 } 1477 if dynsec := elfFile.Section(".dynamic"); dynsec != nil { 1478 bi.ElfDynamicSection.Addr = dynsec.Addr + image.StaticBase 1479 bi.ElfDynamicSection.Size = dynsec.Size 1480 } 1481 } else { 1482 image.StaticBase = addr 1483 } 1484 1485 dwarfFile := elfFile 1486 1487 bi.loadBuildID(image, elfFile) 1488 var debugInfoBytes []byte 1489 var dwerr error 1490 image.dwarf, dwerr = elfFile.DWARF() 1491 if dwerr != nil { 1492 var sepFile *os.File 1493 var serr error 1494 sepFile, dwarfFile, serr = bi.openSeparateDebugInfo(image, elfFile, bi.DebugInfoDirectories) 1495 if serr != nil { 1496 if len(bi.Images) <= 1 { 1497 fmt.Fprintln(os.Stderr, "Warning: no debug info found, some functionality will be missing such as stack traces and variable evaluation.") 1498 } 1499 err := loadBinaryInfoGoRuntimeElf(bi, image, path, elfFile) 1500 if err != nil { 1501 return fmt.Errorf("could not read debug info (%v) and could not read go symbol table (%v)", dwerr, err) 1502 } 1503 return nil 1504 } 1505 image.sepDebugCloser = sepFile 1506 image.dwarf, err = dwarfFile.DWARF() 1507 if err != nil { 1508 return err 1509 } 1510 } 1511 1512 debugInfoBytes, err = godwarf.GetDebugSectionElf(dwarfFile, "info") 1513 if err != nil { 1514 return err 1515 } 1516 1517 image.dwarfReader = image.dwarf.Reader() 1518 1519 debugLineBytes, err := godwarf.GetDebugSectionElf(dwarfFile, "line") 1520 if err != nil { 1521 return err 1522 } 1523 debugLocBytes, _ := godwarf.GetDebugSectionElf(dwarfFile, "loc") 1524 image.loclist2 = loclist.NewDwarf2Reader(debugLocBytes, bi.Arch.PtrSize()) 1525 debugLoclistBytes, _ := godwarf.GetDebugSectionElf(dwarfFile, "loclists") 1526 image.loclist5 = loclist.NewDwarf5Reader(debugLoclistBytes) 1527 debugAddrBytes, _ := godwarf.GetDebugSectionElf(dwarfFile, "addr") 1528 image.debugAddr = godwarf.ParseAddr(debugAddrBytes) 1529 debugLineStrBytes, _ := godwarf.GetDebugSectionElf(dwarfFile, "line_str") 1530 image.debugLineStr = debugLineStrBytes 1531 1532 wg.Add(3) 1533 go bi.parseDebugFrameElf(image, dwarfFile, elfFile, debugInfoBytes, wg) 1534 go bi.loadDebugInfoMaps(image, debugInfoBytes, debugLineBytes, wg, nil) 1535 go bi.loadSymbolName(image, elfFile, wg) 1536 if image.index == 0 { 1537 // determine g struct offset only when loading the executable file 1538 wg.Add(1) 1539 go bi.setGStructOffsetElf(image, dwarfFile, wg) 1540 } 1541 return nil 1542 } 1543 1544 func findGoFuncVal(moduleData []byte, roDataAddr uint64, ptrsize int) (uint64, error) { 1545 buf := new(bytes.Buffer) 1546 err := binary.Write(buf, binary.LittleEndian, &roDataAddr) 1547 if err != nil { 1548 return 0, err 1549 } 1550 // Here we search for the value of `go.func.*` by searching through the raw bytes of the 1551 // runtime.moduledata structure. Since we don't know the value that we are looking for, 1552 // we use a known value, in this case the address of the .rodata section. 1553 // This is because in the layout of the struct, the rodata member is right next to 1554 // the value we need, making the math trivial once we find that member. 1555 // We use `bytes.LastIndex` specifically because the `types` struct member can also 1556 // contain the address of the .rodata section, so this pointer can appear multiple times 1557 // in the raw bytes. 1558 // Yes, this is very ill-advised low-level hackery but it works fine until 1559 // https://github.com/golang/go/issues/58474#issuecomment-1785681472 happens. 1560 // This code path also only runs in stripped binaries, so the whole implementation is 1561 // best effort anyways. 1562 rodata := bytes.LastIndex(moduleData, buf.Bytes()[:ptrsize]) 1563 if rodata == -1 { 1564 return 0, errors.New("could not find rodata struct member") 1565 } 1566 // Layout of struct members is: 1567 // type moduledata struct { 1568 // ... 1569 // rodata uintptr 1570 // gofunc uintptr 1571 // ... 1572 // } 1573 // So do some pointer arithmetic to get the value we need. 1574 gofuncval := binary.LittleEndian.Uint64(moduleData[rodata+(1*ptrsize) : rodata+(2*ptrsize)]) 1575 return gofuncval, nil 1576 } 1577 1578 func parseModuleData(dataSection []byte, tableAddr uint64) ([]byte, error) { 1579 buf := new(bytes.Buffer) 1580 err := binary.Write(buf, binary.LittleEndian, &tableAddr) 1581 if err != nil { 1582 return nil, err 1583 } 1584 off := bytes.Index(dataSection, buf.Bytes()[:4]) 1585 if off == -1 { 1586 return nil, errors.New("could not find moduledata") 1587 } 1588 return dataSection[off : off+0x300], nil 1589 } 1590 1591 // _STT_FUNC is a code object, see /usr/include/elf.h for a full definition. 1592 const _STT_FUNC = 2 1593 1594 func (bi *BinaryInfo) loadSymbolName(image *Image, file *elf.File, wg *sync.WaitGroup) { 1595 defer wg.Done() 1596 if bi.SymNames == nil { 1597 bi.SymNames = make(map[uint64]*elf.Symbol) 1598 } 1599 symSecs, _ := file.Symbols() 1600 for _, symSec := range symSecs { 1601 if symSec.Info == _STT_FUNC { // TODO(chainhelen), need to parse others types. 1602 s := symSec 1603 bi.SymNames[symSec.Value+image.StaticBase] = &s 1604 } 1605 } 1606 } 1607 1608 func (bi *BinaryInfo) loadBuildID(image *Image, file *elf.File) { 1609 buildid := file.Section(".note.gnu.build-id") 1610 if buildid == nil { 1611 return 1612 } 1613 1614 br := buildid.Open() 1615 bh := new(buildIDHeader) 1616 if err := binary.Read(br, binary.LittleEndian, bh); err != nil { 1617 bi.logger.Warnf("can't read build-id header: %v", err) 1618 return 1619 } 1620 1621 name := make([]byte, bh.Namesz) 1622 if err := binary.Read(br, binary.LittleEndian, name); err != nil { 1623 bi.logger.Warnf("can't read build-id name: %v", err) 1624 return 1625 } 1626 1627 if strings.TrimSpace(string(name)) != "GNU\x00" { 1628 bi.logger.Warn("invalid build-id signature") 1629 return 1630 } 1631 1632 descBinary := make([]byte, bh.Descsz) 1633 if err := binary.Read(br, binary.LittleEndian, descBinary); err != nil { 1634 bi.logger.Warnf("can't read build-id desc: %v", err) 1635 return 1636 } 1637 bi.BuildID = hex.EncodeToString(descBinary) 1638 } 1639 1640 func (bi *BinaryInfo) getDebugLink(exe *elf.File) (debugLink string, crc uint32) { 1641 gnuDebugLink := exe.Section(".gnu_debuglink") 1642 if gnuDebugLink == nil { 1643 return 1644 } 1645 1646 br := gnuDebugLink.Open() 1647 buf, err := io.ReadAll(br) 1648 if err != nil { 1649 bi.logger.Warnf("can't read .gnu_debuglink: %v", err) 1650 return 1651 } 1652 zero := bytes.Index(buf, []byte{0}) 1653 if zero <= 0 || len(buf[zero+1:]) < 4 { 1654 bi.logger.Warnf("wrong .gnu_debuglink format: %q", buf) 1655 return 1656 } 1657 debugLink = string(buf[:zero]) 1658 crc = binary.LittleEndian.Uint32(buf[len(buf)-4:]) 1659 return 1660 } 1661 1662 func (bi *BinaryInfo) parseDebugFrameElf(image *Image, dwarfFile, exeFile *elf.File, debugInfoBytes []byte, wg *sync.WaitGroup) { 1663 defer wg.Done() 1664 1665 debugFrameData, debugFrameErr := godwarf.GetDebugSectionElf(dwarfFile, "frame") 1666 ehFrameSection := exeFile.Section(".eh_frame") 1667 var ehFrameData []byte 1668 var ehFrameAddr uint64 1669 if ehFrameSection != nil { 1670 ehFrameAddr = ehFrameSection.Addr 1671 // Workaround for go.dev/cl/429601 1672 if ehFrameSection.Type == elf.SHT_NOBITS { 1673 ehFrameData = make([]byte, ehFrameSection.Size) 1674 } else { 1675 ehFrameData, _ = ehFrameSection.Data() 1676 } 1677 } 1678 1679 bi.parseDebugFrameGeneral(image, debugFrameData, ".debug_frame", debugFrameErr, ehFrameData, ehFrameAddr, ".eh_frame", frame.DwarfEndian(debugInfoBytes)) 1680 } 1681 1682 func (bi *BinaryInfo) setGStructOffsetElf(image *Image, exe *elf.File, wg *sync.WaitGroup) { 1683 defer wg.Done() 1684 1685 // This is a bit arcane. Essentially: 1686 // - If the program is pure Go, it can do whatever it wants, and puts the G 1687 // pointer at %fs-8 on 64 bit. 1688 // - %Gs is the index of private storage in GDT on 32 bit, and puts the G 1689 // pointer at -4(tls). 1690 // - Otherwise, Go asks the external linker to place the G pointer by 1691 // emitting runtime.tlsg, a TLS symbol, which is relocated to the chosen 1692 // offset in libc's TLS block. 1693 // - On ARM64 (but really, any architecture other than i386 and 86x64) the 1694 // offset is calculated using runtime.tls_g and the formula is different. 1695 1696 var tls *elf.Prog 1697 for _, prog := range exe.Progs { 1698 if prog.Type == elf.PT_TLS { 1699 tls = prog 1700 break 1701 } 1702 } 1703 1704 switch exe.Machine { 1705 case elf.EM_X86_64, elf.EM_386: 1706 tlsg := getSymbol(image, bi.logger, exe, "runtime.tlsg") 1707 if tlsg == nil || tls == nil { 1708 bi.gStructOffset = ^uint64(bi.Arch.PtrSize()) + 1 //-ptrSize 1709 return 1710 } 1711 1712 // According to https://reviews.llvm.org/D61824, linkers must pad the actual 1713 // size of the TLS segment to ensure that (tlsoffset%align) == (vaddr%align). 1714 // This formula, copied from the lld code, matches that. 1715 // https://github.com/llvm-mirror/lld/blob/9aef969544981d76bea8e4d1961d3a6980980ef9/ELF/InputSection.cpp#L643 1716 memsz := tls.Memsz + (-tls.Vaddr-tls.Memsz)&(tls.Align-1) 1717 1718 // The TLS register points to the end of the TLS block, which is 1719 // tls.Memsz long. runtime.tlsg is an offset from the beginning of that block. 1720 bi.gStructOffset = ^(memsz) + 1 + tlsg.Value // -tls.Memsz + tlsg.Value 1721 1722 case elf.EM_AARCH64: 1723 tlsg := getSymbol(image, bi.logger, exe, "runtime.tls_g") 1724 if tlsg == nil || tls == nil { 1725 bi.gStructOffset = 2 * uint64(bi.Arch.PtrSize()) 1726 return 1727 } 1728 1729 bi.gStructOffset = tlsg.Value + uint64(bi.Arch.PtrSize()*2) + ((tls.Vaddr - uint64(bi.Arch.PtrSize()*2)) & (tls.Align - 1)) 1730 1731 case elf.EM_PPC64: 1732 _ = getSymbol(image, bi.logger, exe, "runtime.tls_g") 1733 1734 default: 1735 // we should never get here 1736 panic("architecture not supported") 1737 } 1738 } 1739 1740 func getSymbol(image *Image, logger logflags.Logger, exe *elf.File, name string) *elf.Symbol { 1741 symbols, err := exe.Symbols() 1742 if err != nil { 1743 image.setLoadError(logger, "could not parse ELF symbols: %v", err) 1744 return nil 1745 } 1746 1747 for _, symbol := range symbols { 1748 if symbol.Name == name { 1749 s := symbol 1750 return &s 1751 } 1752 } 1753 return nil 1754 } 1755 1756 // PE //////////////////////////////////////////////////////////////// 1757 1758 const _IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE = 0x0040 1759 1760 // loadBinaryInfoPE specifically loads information from a PE binary. 1761 func loadBinaryInfoPE(bi *BinaryInfo, image *Image, path string, entryPoint uint64, wg *sync.WaitGroup) error { 1762 peFile, closer, err := openExecutablePathPE(path) 1763 if err != nil { 1764 return err 1765 } 1766 image.closer = closer 1767 cpuArch := _PEMachine(peFile.Machine) 1768 if !supportedWindowsArch[cpuArch] { 1769 return &ErrUnsupportedArch{os: "windows", cpuArch: cpuArch} 1770 } 1771 image.dwarf, err = peFile.DWARF() 1772 if err != nil { 1773 return err 1774 } 1775 debugInfoBytes, err := godwarf.GetDebugSectionPE(peFile, "info") 1776 if err != nil { 1777 return err 1778 } 1779 opth := peFile.OptionalHeader.(*pe.OptionalHeader64) 1780 if entryPoint != 0 { 1781 image.StaticBase = entryPoint - opth.ImageBase 1782 } else { 1783 if opth.DllCharacteristics&_IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE != 0 { 1784 return ErrCouldNotDetermineRelocation 1785 } 1786 } 1787 1788 image.dwarfReader = image.dwarf.Reader() 1789 1790 debugLineBytes, err := godwarf.GetDebugSectionPE(peFile, "line") 1791 if err != nil { 1792 return err 1793 } 1794 debugLocBytes, _ := godwarf.GetDebugSectionPE(peFile, "loc") 1795 image.loclist2 = loclist.NewDwarf2Reader(debugLocBytes, bi.Arch.PtrSize()) 1796 debugLoclistBytes, _ := godwarf.GetDebugSectionPE(peFile, "loclists") 1797 image.loclist5 = loclist.NewDwarf5Reader(debugLoclistBytes) 1798 debugAddrBytes, _ := godwarf.GetDebugSectionPE(peFile, "addr") 1799 image.debugAddr = godwarf.ParseAddr(debugAddrBytes) 1800 debugLineStrBytes, _ := godwarf.GetDebugSectionPE(peFile, "line_str") 1801 image.debugLineStr = debugLineStrBytes 1802 1803 wg.Add(2) 1804 go bi.parseDebugFramePE(image, peFile, debugInfoBytes, wg) 1805 go bi.loadDebugInfoMaps(image, debugInfoBytes, debugLineBytes, wg, func() { 1806 // setGStructOffsetPE requires the image compile units to be loaded, 1807 // so it can't be called concurrently with loadDebugInfoMaps. 1808 if image.index == 0 { 1809 // determine g struct offset only when loading the executable file. 1810 bi.setGStructOffsetPE(entryPoint, peFile) 1811 } 1812 }) 1813 return nil 1814 } 1815 1816 func (bi *BinaryInfo) setGStructOffsetPE(entryPoint uint64, peFile *pe.File) { 1817 readtls_g := func() uint64 { 1818 for _, s := range peFile.Symbols { 1819 if s.Name == "runtime.tls_g" { 1820 i := int(s.SectionNumber) - 1 1821 if 0 <= i && i < len(peFile.Sections) { 1822 sect := peFile.Sections[i] 1823 if s.Value < sect.VirtualSize { 1824 return entryPoint + uint64(sect.VirtualAddress) + uint64(s.Value) 1825 } 1826 } 1827 break 1828 } 1829 } 1830 return 0 1831 } 1832 switch _PEMachine(peFile.Machine) { 1833 case _IMAGE_FILE_MACHINE_AMD64: 1834 producer := bi.Producer() 1835 if producer != "" && goversion.ProducerAfterOrEqual(producer, 1, 20) { 1836 // Use runtime.tls_g as pointer to offset from GS to G struct: 1837 // https://golang.org/src/runtime/sys_windows_amd64.s 1838 bi.gStructOffset = readtls_g() 1839 bi.gStructOffsetIsPtr = true 1840 } else { 1841 // Use ArbitraryUserPointer (0x28) as pointer to pointer 1842 // to G struct per: 1843 // https://golang.org/src/runtime/cgo/gcc_windows_amd64.c 1844 bi.gStructOffset = 0x28 1845 } 1846 case _IMAGE_FILE_MACHINE_ARM64: 1847 // Use runtime.tls_g as pointer to offset from R18 to G struct: 1848 // https://golang.org/src/runtime/sys_windows_arm64.s 1849 bi.gStructOffset = readtls_g() 1850 bi.gStructOffsetIsPtr = true 1851 } 1852 } 1853 1854 func openExecutablePathPE(path string) (*pe.File, io.Closer, error) { 1855 f, err := os.OpenFile(path, 0, os.ModePerm) 1856 if err != nil { 1857 return nil, nil, err 1858 } 1859 peFile, err := pe.NewFile(f) 1860 if err != nil { 1861 f.Close() 1862 return nil, nil, err 1863 } 1864 return peFile, f, nil 1865 } 1866 1867 func (bi *BinaryInfo) parseDebugFramePE(image *Image, exe *pe.File, debugInfoBytes []byte, wg *sync.WaitGroup) { 1868 defer wg.Done() 1869 1870 debugFrameBytes, err := godwarf.GetDebugSectionPE(exe, "frame") 1871 bi.parseDebugFrameGeneral(image, debugFrameBytes, ".debug_frame", err, nil, 0, "", frame.DwarfEndian(debugInfoBytes)) 1872 } 1873 1874 // MACH-O //////////////////////////////////////////////////////////// 1875 1876 // loadBinaryInfoMacho specifically loads information from a Mach-O binary. 1877 func loadBinaryInfoMacho(bi *BinaryInfo, image *Image, path string, entryPoint uint64, wg *sync.WaitGroup) error { 1878 exe, err := macho.Open(path) 1879 1880 if err != nil { 1881 return err 1882 } 1883 1884 if entryPoint != 0 { 1885 machoOff := uint64(0x100000000) 1886 for _, ld := range exe.Loads { 1887 if seg, _ := ld.(*macho.Segment); seg != nil { 1888 if seg.Name == "__TEXT" { 1889 machoOff = seg.Addr 1890 break 1891 } 1892 } 1893 } 1894 logflags.DebuggerLogger().Debugf("entryPoint %#x machoOff %#x", entryPoint, machoOff) 1895 image.StaticBase = entryPoint - machoOff 1896 } 1897 1898 image.closer = exe 1899 if !supportedDarwinArch[exe.Cpu] { 1900 return &ErrUnsupportedArch{os: "darwin", cpuArch: exe.Cpu} 1901 } 1902 var dwerr error 1903 image.dwarf, dwerr = exe.DWARF() 1904 if dwerr != nil { 1905 if len(bi.Images) <= 1 { 1906 fmt.Fprintln(os.Stderr, "Warning: no debug info found, some functionality will be missing such as stack traces and variable evaluation.") 1907 } 1908 err := loadBinaryInfoGoRuntimeMacho(bi, image, path, exe) 1909 if err != nil { 1910 return fmt.Errorf("could not read debug info (%v) and could not read go symbol table (%v)", dwerr, err) 1911 } 1912 return nil 1913 } 1914 debugInfoBytes, err := godwarf.GetDebugSectionMacho(exe, "info") 1915 if err != nil { 1916 return err 1917 } 1918 1919 image.dwarfReader = image.dwarf.Reader() 1920 1921 debugLineBytes, err := godwarf.GetDebugSectionMacho(exe, "line") 1922 if err != nil { 1923 return err 1924 } 1925 debugLocBytes, _ := godwarf.GetDebugSectionMacho(exe, "loc") 1926 image.loclist2 = loclist.NewDwarf2Reader(debugLocBytes, bi.Arch.PtrSize()) 1927 debugLoclistBytes, _ := godwarf.GetDebugSectionMacho(exe, "loclists") 1928 image.loclist5 = loclist.NewDwarf5Reader(debugLoclistBytes) 1929 debugAddrBytes, _ := godwarf.GetDebugSectionMacho(exe, "addr") 1930 image.debugAddr = godwarf.ParseAddr(debugAddrBytes) 1931 debugLineStrBytes, _ := godwarf.GetDebugSectionMacho(exe, "line_str") 1932 image.debugLineStr = debugLineStrBytes 1933 1934 wg.Add(2) 1935 go bi.parseDebugFrameMacho(image, exe, debugInfoBytes, wg) 1936 go bi.loadDebugInfoMaps(image, debugInfoBytes, debugLineBytes, wg, bi.setGStructOffsetMacho) 1937 return nil 1938 } 1939 1940 func (bi *BinaryInfo) setGStructOffsetMacho() { 1941 // In go1.11 it's 0x30, before 0x8a0, see: 1942 // https://github.com/golang/go/issues/23617 1943 // and go commit b3a854c733257c5249c3435ffcee194f8439676a 1944 producer := bi.Producer() 1945 if producer != "" && goversion.ProducerAfterOrEqual(producer, 1, 11) { 1946 bi.gStructOffset = 0x30 1947 return 1948 } 1949 bi.gStructOffset = 0x8a0 1950 } 1951 1952 func (bi *BinaryInfo) parseDebugFrameMacho(image *Image, exe *macho.File, debugInfoBytes []byte, wg *sync.WaitGroup) { 1953 defer wg.Done() 1954 1955 debugFrameBytes, debugFrameErr := godwarf.GetDebugSectionMacho(exe, "frame") 1956 ehFrameSection := exe.Section("__eh_frame") 1957 var ehFrameBytes []byte 1958 var ehFrameAddr uint64 1959 if ehFrameSection != nil { 1960 ehFrameAddr = ehFrameSection.Addr 1961 ehFrameBytes, _ = ehFrameSection.Data() 1962 } 1963 1964 bi.parseDebugFrameGeneral(image, debugFrameBytes, "__debug_frame", debugFrameErr, ehFrameBytes, ehFrameAddr, "__eh_frame", frame.DwarfEndian(debugInfoBytes)) 1965 } 1966 1967 // macOSDebugFrameBugWorkaround applies a workaround for [golang/go#25841] 1968 // 1969 // It finds the Go function with the lowest entry point and the first 1970 // debug_frame FDE, calculates the difference between the start of the 1971 // function and the start of the FDE and sums it to all debug_frame FDEs. 1972 // A number of additional checks are performed to make sure we don't ruin 1973 // executables unaffected by this bug. 1974 // 1975 // [golang/go#25841]: https://github.com/golang/go/issues/25841 1976 func (bi *BinaryInfo) macOSDebugFrameBugWorkaround() { 1977 if bi.GOOS != "darwin" { 1978 return 1979 } 1980 if len(bi.Images) > 1 { 1981 // Only do this for the first executable, but it might work for plugins as 1982 // well if we had a way to distinguish where entries in bi.frameEntries 1983 // come from 1984 return 1985 } 1986 exe, ok := bi.Images[0].closer.(*macho.File) 1987 if !ok { 1988 return 1989 } 1990 if bi.Arch.Name == "arm64" { 1991 if exe.Flags&macho.FlagPIE == 0 { 1992 bi.logger.Infof("debug_frame workaround not needed: not a PIE (%#x)", exe.Flags) 1993 return 1994 } 1995 } else { 1996 prod := goversion.ParseProducer(bi.Producer()) 1997 if !prod.AfterOrEqual(goversion.GoVersion{Major: 1, Minor: 19, Rev: 3}) && !prod.IsDevel() { 1998 bi.logger.Infof("debug_frame workaround not needed (version %q on %s)", bi.Producer(), bi.Arch.Name) 1999 return 2000 } 2001 found := false 2002 for i := range bi.frameEntries { 2003 if bi.frameEntries[i].CIE.CIE_id == ^uint32(0) && bi.frameEntries[i].Begin() < 0x4000000 { 2004 found = true 2005 break 2006 } 2007 } 2008 if !found { 2009 bi.logger.Infof("debug_frame workaround not needed (all FDEs above 0x4000000)") 2010 return 2011 } 2012 } 2013 2014 // Find first Go function (first = lowest entry point) 2015 var fn *Function 2016 for i := range bi.Functions { 2017 if bi.Functions[i].cu.isgo && bi.Functions[i].Entry > 0 { 2018 fn = &bi.Functions[i] 2019 break 2020 } 2021 } 2022 if fn == nil { 2023 bi.logger.Warn("debug_frame workaround not applied: could not find a Go function") 2024 return 2025 } 2026 2027 if fde, _ := bi.frameEntries.FDEForPC(fn.Entry); fde != nil { 2028 // Function is covered, no need to apply workaround 2029 bi.logger.Warnf("debug_frame workaround not applied: function %s (at %#x) covered by %#x-%#x", fn.Name, fn.Entry, fde.Begin(), fde.End()) 2030 return 2031 } 2032 2033 // Find lowest FDE in debug_frame 2034 var fde *frame.FrameDescriptionEntry 2035 for i := range bi.frameEntries { 2036 if bi.frameEntries[i].CIE.CIE_id == ^uint32(0) { 2037 fde = bi.frameEntries[i] 2038 break 2039 } 2040 } 2041 2042 if fde == nil { 2043 bi.logger.Warnf("debug_frame workaround not applied because there are no debug_frame entries (%d)", len(bi.frameEntries)) 2044 return 2045 } 2046 2047 fnsize := fn.End - fn.Entry 2048 2049 if fde.End()-fde.Begin() != fnsize || fde.Begin() > fn.Entry { 2050 bi.logger.Warnf("debug_frame workaround not applied: function %s (at %#x-%#x) has a different size than the first FDE (%#x-%#x) (or the FDE starts after the function)", fn.Name, fn.Entry, fn.End, fde.Begin(), fde.End()) 2051 return 2052 } 2053 2054 delta := fn.Entry - fde.Begin() 2055 2056 bi.logger.Infof("applying debug_frame workaround +%#x: function %s (at %#x-%#x) and FDE %#x-%#x", delta, fn.Name, fn.Entry, fn.End, fde.Begin(), fde.End()) 2057 2058 for i := range bi.frameEntries { 2059 if bi.frameEntries[i].CIE.CIE_id == ^uint32(0) { 2060 bi.frameEntries[i].Translate(delta) 2061 } 2062 } 2063 } 2064 2065 // GO RUNTIME INFO //////////////////////////////////////////////////////////// 2066 2067 // loadBinaryInfoGoRuntimeElf loads information from the Go runtime sections 2068 // of an ELF binary, it is only called when debug info has been stripped. 2069 func loadBinaryInfoGoRuntimeElf(bi *BinaryInfo, image *Image, path string, elfFile *elf.File) (err error) { 2070 // This is a best-effort procedure, it can go wrong in unexpected ways, so 2071 // recover all panics. 2072 defer func() { 2073 ierr := recover() 2074 if ierr != nil { 2075 err = fmt.Errorf("error loading binary info from Go runtime: %v", ierr) 2076 } 2077 }() 2078 2079 cu := &compileUnit{} 2080 cu.image = image 2081 symTable, symTabAddr, err := readPcLnTableElf(elfFile, path) 2082 if err != nil { 2083 return err 2084 } 2085 image.symTable = symTable 2086 noPtrSectionData, err := elfFile.Section(".noptrdata").Data() 2087 if err != nil { 2088 return err 2089 } 2090 md, err := parseModuleData(noPtrSectionData, symTabAddr) 2091 if err != nil { 2092 return err 2093 } 2094 roDataAddr := elfFile.Section(".rodata").Addr 2095 goFuncVal, err := findGoFuncVal(md, roDataAddr, bi.Arch.ptrSize) 2096 if err != nil { 2097 return err 2098 } 2099 prog := gosym.ProgContaining(elfFile, goFuncVal) 2100 var progAddr uint64 2101 var progReaderAt io.ReaderAt 2102 if prog != nil { 2103 progAddr = prog.Vaddr 2104 progReaderAt = prog.ReaderAt 2105 } 2106 return loadBinaryInfoGoRuntimeCommon(bi, image, cu, goFuncVal, progAddr, progReaderAt) 2107 } 2108 2109 // loadBinaryInfoGoRuntimeMacho loads information from the Go runtime sections 2110 // of an Macho-o binary, it is only called when debug info has been stripped. 2111 func loadBinaryInfoGoRuntimeMacho(bi *BinaryInfo, image *Image, path string, exe *macho.File) (err error) { 2112 // This is a best-effort procedure, it can go wrong in unexpected ways, so 2113 // recover all panics. 2114 defer func() { 2115 ierr := recover() 2116 if ierr != nil { 2117 err = fmt.Errorf("error loading binary info from Go runtime: %v", ierr) 2118 } 2119 }() 2120 2121 cu := &compileUnit{} 2122 cu.image = image 2123 symTable, symTabAddr, err := readPcLnTableMacho(exe, path) 2124 if err != nil { 2125 return err 2126 } 2127 image.symTable = symTable 2128 noPtrSectionData, err := exe.Section("__noptrdata").Data() 2129 if err != nil { 2130 return err 2131 } 2132 md, err := parseModuleData(noPtrSectionData, symTabAddr) 2133 if err != nil { 2134 return err 2135 } 2136 roDataAddr := exe.Section("__rodata").Addr 2137 goFuncVal, err := findGoFuncVal(md, roDataAddr, bi.Arch.ptrSize) 2138 if err != nil { 2139 return err 2140 } 2141 seg := gosym.SegmentContaining(exe, goFuncVal) 2142 var segAddr uint64 2143 var segReaderAt io.ReaderAt 2144 if seg != nil { 2145 segAddr = seg.Addr 2146 segReaderAt = seg.ReaderAt 2147 } 2148 return loadBinaryInfoGoRuntimeCommon(bi, image, cu, goFuncVal, segAddr, segReaderAt) 2149 } 2150 2151 func loadBinaryInfoGoRuntimeCommon(bi *BinaryInfo, image *Image, cu *compileUnit, goFuncVal uint64, goFuncSegAddr uint64, goFuncReader io.ReaderAt) error { 2152 inlFuncs := make(map[string]*Function) 2153 for _, f := range image.symTable.Funcs { 2154 fnEntry := f.Entry + image.StaticBase 2155 if goFuncReader != nil { 2156 inlCalls, err := image.symTable.GetInlineTree(&f, goFuncVal, goFuncSegAddr, goFuncReader) 2157 if err != nil { 2158 return err 2159 } 2160 for _, inlfn := range inlCalls { 2161 newInlinedCall := InlinedCall{cu: cu, LowPC: fnEntry + uint64(inlfn.ParentPC)} 2162 if fn, ok := inlFuncs[inlfn.Name]; ok { 2163 fn.InlinedCalls = append(fn.InlinedCalls, newInlinedCall) 2164 continue 2165 } 2166 inlFuncs[inlfn.Name] = &Function{ 2167 Name: inlfn.Name, 2168 Entry: 0, End: 0, 2169 cu: cu, 2170 InlinedCalls: []InlinedCall{ 2171 newInlinedCall, 2172 }, 2173 } 2174 } 2175 } 2176 fn := Function{Name: f.Name, Entry: fnEntry, End: f.End + image.StaticBase, cu: cu} 2177 bi.Functions = append(bi.Functions, fn) 2178 } 2179 for i := range inlFuncs { 2180 bi.Functions = append(bi.Functions, *inlFuncs[i]) 2181 } 2182 sort.Sort(functionsDebugInfoByEntry(bi.Functions)) 2183 for f := range image.symTable.Files { 2184 bi.Sources = append(bi.Sources, f) 2185 } 2186 sort.Strings(bi.Sources) 2187 bi.Sources = uniq(bi.Sources) 2188 return nil 2189 } 2190 2191 // Do not call this function directly it isn't able to deal correctly with package paths 2192 func (bi *BinaryInfo) findType(name string) (godwarf.Type, error) { 2193 name = strings.ReplaceAll(name, "interface{", "interface {") 2194 name = strings.ReplaceAll(name, "struct{", "struct {") 2195 ref, found := bi.types[name] 2196 if !found { 2197 return nil, reader.ErrTypeNotFound 2198 } 2199 image := bi.Images[ref.imageIndex] 2200 return godwarf.ReadType(image.dwarf, ref.imageIndex, ref.offset, image.typeCache) 2201 } 2202 2203 func (bi *BinaryInfo) findTypeExpr(expr ast.Expr) (godwarf.Type, error) { 2204 if lit, islit := expr.(*ast.BasicLit); islit && lit.Kind == token.STRING { 2205 // Allow users to specify type names verbatim as quoted 2206 // string. Useful as a catch-all workaround for cases where we don't 2207 // parse/serialize types correctly or can not resolve package paths. 2208 typn, _ := strconv.Unquote(lit.Value) 2209 2210 // Check if the type in question is an array type, in which case we try to 2211 // fake it. 2212 if len(typn) > 0 && typn[0] == '[' { 2213 closedBrace := strings.Index(typn, "]") 2214 if closedBrace > 1 { 2215 n, err := strconv.Atoi(typn[1:closedBrace]) 2216 if err == nil { 2217 return bi.findArrayType(n, typn[closedBrace+1:]) 2218 } 2219 } 2220 } 2221 return bi.findType(typn) 2222 } 2223 bi.expandPackagesInType(expr) 2224 if snode, ok := expr.(*ast.StarExpr); ok { 2225 // Pointer types only appear in the dwarf information when 2226 // a pointer to the type is used in the target program, here 2227 // we create a pointer type on the fly so that the user can 2228 // specify a pointer to any variable used in the target program 2229 ptyp, err := bi.findTypeExpr(snode.X) 2230 if err != nil { 2231 return nil, err 2232 } 2233 return pointerTo(ptyp, bi.Arch), nil 2234 } 2235 if anode, ok := expr.(*ast.ArrayType); ok { 2236 // Array types (for example [N]byte) are only present in DWARF if they are 2237 // used by the program, but it's convenient to make all of them available 2238 // to the user for two reasons: 2239 // 1. to allow reading arbitrary memory byte-by-byte (by casting an 2240 // address to an array of bytes). 2241 // 2. to read the contents of a channel's buffer (we create fake array 2242 // types for them) 2243 2244 alen, litlen := anode.Len.(*ast.BasicLit) 2245 if litlen && alen.Kind == token.INT { 2246 n, _ := strconv.Atoi(alen.Value) 2247 return bi.findArrayType(n, exprToString(anode.Elt)) 2248 } 2249 } 2250 return bi.findType(exprToString(expr)) 2251 } 2252 2253 func (bi *BinaryInfo) findArrayType(n int, etyp string) (godwarf.Type, error) { 2254 switch etyp { 2255 case "byte", "uint8": 2256 etyp = "uint8" 2257 fallthrough 2258 default: 2259 btyp, err := bi.findType(etyp) 2260 if err != nil { 2261 return nil, err 2262 } 2263 return fakeArrayType(uint64(n), btyp), nil 2264 } 2265 } 2266 2267 func complexType(typename string) bool { 2268 for _, ch := range typename { 2269 switch ch { 2270 case '*', '[', '<', '{', '(', ' ': 2271 return true 2272 } 2273 } 2274 return false 2275 } 2276 2277 func (bi *BinaryInfo) registerTypeToPackageMap(entry *dwarf.Entry) { 2278 if entry.Tag != dwarf.TagTypedef && entry.Tag != dwarf.TagBaseType && entry.Tag != dwarf.TagClassType && entry.Tag != dwarf.TagStructType { 2279 return 2280 } 2281 2282 typename, ok := entry.Val(dwarf.AttrName).(string) 2283 if !ok || complexType(typename) { 2284 return 2285 } 2286 2287 dot := strings.LastIndex(typename, ".") 2288 if dot < 0 { 2289 return 2290 } 2291 path := typename[:dot] 2292 slash := strings.LastIndex(path, "/") 2293 if slash < 0 || slash+1 >= len(path) { 2294 return 2295 } 2296 name := path[slash+1:] 2297 bi.PackageMap[name] = []string{path} 2298 } 2299 2300 func (bi *BinaryInfo) loadDebugInfoMaps(image *Image, debugInfoBytes, debugLineBytes []byte, wg *sync.WaitGroup, cont func()) { 2301 if wg != nil { 2302 defer wg.Done() 2303 } 2304 2305 if bi.types == nil { 2306 bi.types = make(map[string]dwarfRef) 2307 } 2308 if bi.consts == nil { 2309 bi.consts = make(map[dwarfRef]*constantType) 2310 } 2311 if bi.PackageMap == nil { 2312 bi.PackageMap = make(map[string][]string) 2313 } 2314 if bi.inlinedCallLines == nil { 2315 bi.inlinedCallLines = make(map[fileLine][]uint64) 2316 } 2317 if bi.dwrapUnwrapCache == nil { 2318 bi.dwrapUnwrapCache = make(map[uint64]*Function) 2319 } 2320 2321 image.runtimeTypeToDIE = make(map[uint64]runtimeTypeDIE) 2322 2323 ctxt := newLoadDebugInfoMapsContext(bi, image, pdwarf.ReadUnitVersions(debugInfoBytes)) 2324 2325 reader := image.DwarfReader() 2326 2327 for { 2328 entry, err := reader.Next() 2329 if err != nil { 2330 image.setLoadError(bi.logger, "error reading debug_info: %v", err) 2331 break 2332 } 2333 if entry == nil { 2334 break 2335 } 2336 switch entry.Tag { 2337 case dwarf.TagCompileUnit: 2338 cu := &compileUnit{} 2339 cu.image = image 2340 cu.entry = entry 2341 cu.offset = entry.Offset 2342 cu.Version = ctxt.offsetToVersion[cu.offset] 2343 if lang, _ := entry.Val(dwarf.AttrLanguage).(int64); lang == dwarfGoLanguage { 2344 cu.isgo = true 2345 } 2346 cu.name, _ = entry.Val(dwarf.AttrName).(string) 2347 compdir, _ := entry.Val(dwarf.AttrCompDir).(string) 2348 if compdir != "" { 2349 cu.name = filepath.Join(compdir, cu.name) 2350 } 2351 cu.ranges, _ = image.dwarf.Ranges(entry) 2352 for i := range cu.ranges { 2353 cu.ranges[i][0] += image.StaticBase 2354 cu.ranges[i][1] += image.StaticBase 2355 } 2356 if len(cu.ranges) >= 1 { 2357 cu.lowPC = cu.ranges[0][0] 2358 } 2359 lineInfoOffset, hasLineInfo := entry.Val(dwarf.AttrStmtList).(int64) 2360 if hasLineInfo && lineInfoOffset >= 0 && lineInfoOffset < int64(len(debugLineBytes)) { 2361 var logfn func(string, ...interface{}) 2362 if logflags.DebugLineErrors() { 2363 logfn = logflags.DebugLineLogger().Debugf 2364 } 2365 cu.lineInfo = line.Parse(compdir, bytes.NewBuffer(debugLineBytes[lineInfoOffset:]), image.debugLineStr, logfn, image.StaticBase, bi.GOOS == "windows", bi.Arch.PtrSize()) 2366 } 2367 cu.producer, _ = entry.Val(dwarf.AttrProducer).(string) 2368 if cu.isgo && cu.producer != "" { 2369 semicolon := strings.Index(cu.producer, ";") 2370 if semicolon < 0 { 2371 cu.optimized = goversion.ProducerAfterOrEqual(cu.producer, 1, 10) 2372 } else { 2373 cu.optimized = !strings.Contains(cu.producer[semicolon:], "-N") || !strings.Contains(cu.producer[semicolon:], "-l") 2374 const regabi = " regabi" 2375 if i := strings.Index(cu.producer[semicolon:], regabi); i > 0 { 2376 i += semicolon 2377 if i+len(regabi) >= len(cu.producer) || cu.producer[i+len(regabi)] == ' ' { 2378 bi.regabi = true 2379 } 2380 } 2381 cu.producer = cu.producer[:semicolon] 2382 } 2383 } 2384 gopkg, _ := entry.Val(godwarf.AttrGoPackageName).(string) 2385 if cu.isgo && gopkg != "" { 2386 bi.PackageMap[gopkg] = append(bi.PackageMap[gopkg], escapePackagePath(strings.ReplaceAll(cu.name, "\\", "/"))) 2387 } 2388 image.compileUnits = append(image.compileUnits, cu) 2389 if entry.Children { 2390 bi.loadDebugInfoMapsCompileUnit(ctxt, image, reader, cu) 2391 } 2392 2393 case dwarf.TagPartialUnit: 2394 reader.SkipChildren() 2395 2396 default: 2397 // ignore unknown tags 2398 reader.SkipChildren() 2399 } 2400 } 2401 2402 sort.Sort(compileUnitsByOffset(image.compileUnits)) 2403 sort.Sort(functionsDebugInfoByEntry(bi.Functions)) 2404 sort.Sort(packageVarsByAddr(bi.packageVars)) 2405 2406 bi.lookupFunc = nil 2407 bi.lookupGenericFunc = nil 2408 2409 for _, cu := range image.compileUnits { 2410 if cu.lineInfo != nil { 2411 for _, fileEntry := range cu.lineInfo.FileNames { 2412 bi.Sources = append(bi.Sources, fileEntry.Path) 2413 } 2414 } 2415 } 2416 sort.Strings(bi.Sources) 2417 bi.Sources = uniq(bi.Sources) 2418 2419 if cont != nil { 2420 cont() 2421 } 2422 } 2423 2424 // LookupGenericFunc returns a map that allows searching for instantiations of generic function by specifying a function name without type parameters. 2425 // For example the key "pkg.(*Receiver).Amethod" will find all instantiations of Amethod: 2426 // - pkg.(*Receiver[.shape.int]).Amethod 2427 // - pkg.(*Receiver[.shape.*uint8]).Amethod 2428 // - etc. 2429 func (bi *BinaryInfo) LookupGenericFunc() map[string][]*Function { 2430 if bi.lookupGenericFunc == nil { 2431 bi.lookupGenericFunc = make(map[string][]*Function) 2432 for i := range bi.Functions { 2433 dn := bi.Functions[i].NameWithoutTypeParams() 2434 if dn != bi.Functions[i].Name { 2435 bi.lookupGenericFunc[dn] = append(bi.lookupGenericFunc[dn], &bi.Functions[i]) 2436 } 2437 } 2438 } 2439 return bi.lookupGenericFunc 2440 } 2441 2442 func (bi *BinaryInfo) LookupFunc() map[string][]*Function { 2443 if bi.lookupFunc == nil { 2444 bi.lookupFunc = make(map[string][]*Function) 2445 for i := range bi.Functions { 2446 name := bi.Functions[i].Name 2447 bi.lookupFunc[name] = append(bi.lookupFunc[name], &bi.Functions[i]) 2448 } 2449 } 2450 return bi.lookupFunc 2451 } 2452 2453 func (bi *BinaryInfo) lookupOneFunc(name string) *Function { 2454 fns := bi.LookupFunc()[name] 2455 if fns == nil { 2456 return nil 2457 } 2458 return fns[0] 2459 } 2460 2461 // loadDebugInfoMapsCompileUnit loads entry from a single compile unit. 2462 func (bi *BinaryInfo) loadDebugInfoMapsCompileUnit(ctxt *loadDebugInfoMapsContext, image *Image, reader *reader.Reader, cu *compileUnit) { 2463 hasAttrGoPkgName := goversion.ProducerAfterOrEqual(cu.producer, 1, 13) 2464 2465 depth := 0 2466 2467 for { 2468 entry, err := reader.Next() 2469 if err != nil { 2470 image.setLoadError(bi.logger, "error reading debug_info: %v", err) 2471 return 2472 } 2473 if entry == nil { 2474 break 2475 } 2476 switch entry.Tag { 2477 case 0: 2478 if depth == 0 { 2479 return 2480 } else { 2481 depth-- 2482 } 2483 case dwarf.TagImportedUnit: 2484 bi.loadDebugInfoMapsImportedUnit(entry, ctxt, image, cu) 2485 reader.SkipChildren() 2486 2487 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: 2488 if name, ok := entry.Val(dwarf.AttrName).(string); ok { 2489 if !cu.isgo { 2490 name = "C." + name 2491 } 2492 if _, exists := bi.types[name]; !exists { 2493 bi.types[name] = dwarfRef{image.index, entry.Offset} 2494 } 2495 } 2496 if cu != nil && cu.isgo && !hasAttrGoPkgName { 2497 bi.registerTypeToPackageMap(entry) 2498 } 2499 image.registerRuntimeTypeToDIE(entry, ctxt.ardr) 2500 reader.SkipChildren() 2501 2502 case dwarf.TagVariable: 2503 if n, ok := entry.Val(dwarf.AttrName).(string); ok { 2504 var addr uint64 2505 if loc, ok := entry.Val(dwarf.AttrLocation).([]byte); ok { 2506 if len(loc) == bi.Arch.PtrSize()+1 && op.Opcode(loc[0]) == op.DW_OP_addr { 2507 addr, _ = pdwarf.ReadUintRaw(bytes.NewReader(loc[1:]), binary.LittleEndian, bi.Arch.PtrSize()) 2508 } 2509 } 2510 if !cu.isgo { 2511 n = "C." + n 2512 } 2513 if _, known := ctxt.knownPackageVars[n]; !known { 2514 bi.packageVars = append(bi.packageVars, packageVar{n, cu, entry.Offset, addr + image.StaticBase}) 2515 } 2516 } 2517 reader.SkipChildren() 2518 2519 case dwarf.TagConstant: 2520 name, okName := entry.Val(dwarf.AttrName).(string) 2521 typ, okType := entry.Val(dwarf.AttrType).(dwarf.Offset) 2522 val, okVal := entry.Val(dwarf.AttrConstValue).(int64) 2523 if okName && okType && okVal { 2524 if !cu.isgo { 2525 name = "C." + name 2526 } 2527 ct := bi.consts[dwarfRef{image.index, typ}] 2528 if ct == nil { 2529 ct = &constantType{} 2530 bi.consts[dwarfRef{image.index, typ}] = ct 2531 } 2532 ct.values = append(ct.values, constantValue{name: name, fullName: name, value: val}) 2533 } 2534 reader.SkipChildren() 2535 2536 case dwarf.TagSubprogram: 2537 inlined := false 2538 if inval, ok := entry.Val(dwarf.AttrInline).(int64); ok { 2539 inlined = inval >= 1 2540 } 2541 2542 if inlined { 2543 bi.addAbstractSubprogram(entry, ctxt, reader, image, cu) 2544 } else { 2545 originOffset, hasAbstractOrigin := entry.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset) 2546 if hasAbstractOrigin { 2547 bi.addConcreteInlinedSubprogram(entry, originOffset, ctxt, reader, cu) 2548 } else { 2549 bi.addConcreteSubprogram(entry, ctxt, reader, cu) 2550 } 2551 } 2552 2553 default: 2554 if entry.Children { 2555 depth++ 2556 } 2557 } 2558 } 2559 } 2560 2561 // loadDebugInfoMapsImportedUnit loads entries into cu from the partial unit 2562 // referenced in a DW_TAG_imported_unit entry. 2563 func (bi *BinaryInfo) loadDebugInfoMapsImportedUnit(entry *dwarf.Entry, ctxt *loadDebugInfoMapsContext, image *Image, cu *compileUnit) { 2564 off, ok := entry.Val(dwarf.AttrImport).(dwarf.Offset) 2565 if !ok { 2566 return 2567 } 2568 reader := image.DwarfReader() 2569 reader.Seek(off) 2570 imentry, err := reader.Next() 2571 if err != nil { 2572 return 2573 } 2574 if imentry.Tag != dwarf.TagPartialUnit { 2575 return 2576 } 2577 bi.loadDebugInfoMapsCompileUnit(ctxt, image, reader, cu) 2578 } 2579 2580 // addAbstractSubprogram adds the abstract entry for an inlined function. 2581 func (bi *BinaryInfo) addAbstractSubprogram(entry *dwarf.Entry, ctxt *loadDebugInfoMapsContext, reader *reader.Reader, image *Image, cu *compileUnit) { 2582 name, ok := subprogramEntryName(entry, cu) 2583 if !ok { 2584 bi.logger.Warnf("reading debug_info: abstract subprogram without name at %#x", entry.Offset) 2585 // In some cases clang produces abstract subprograms that do not have a 2586 // name, but we should process them anyway. 2587 } 2588 2589 if entry.Children { 2590 bi.loadDebugInfoMapsInlinedCalls(ctxt, reader, cu) 2591 } 2592 2593 originIdx := ctxt.lookupAbstractOrigin(bi, entry.Offset) 2594 fn := &bi.Functions[originIdx] 2595 fn.Name = name 2596 fn.offset = entry.Offset 2597 fn.cu = cu 2598 } 2599 2600 // addConcreteInlinedSubprogram adds the concrete entry of a subprogram that was also inlined. 2601 func (bi *BinaryInfo) addConcreteInlinedSubprogram(entry *dwarf.Entry, originOffset dwarf.Offset, ctxt *loadDebugInfoMapsContext, reader *reader.Reader, cu *compileUnit) { 2602 lowpc, highpc, ok := subprogramEntryRange(entry, cu.image) 2603 if !ok { 2604 bi.logger.Warnf("reading debug_info: concrete inlined subprogram without address range at %#x", entry.Offset) 2605 if entry.Children { 2606 reader.SkipChildren() 2607 } 2608 return 2609 } 2610 2611 originIdx := ctxt.lookupAbstractOrigin(bi, originOffset) 2612 fn := &bi.Functions[originIdx] 2613 fn.offset = entry.Offset 2614 fn.Entry = lowpc 2615 fn.End = highpc 2616 fn.cu = cu 2617 2618 if entry.Children { 2619 bi.loadDebugInfoMapsInlinedCalls(ctxt, reader, cu) 2620 } 2621 } 2622 2623 // addConcreteSubprogram adds a concrete subprogram (a normal subprogram 2624 // that doesn't have abstract or inlined entries) 2625 func (bi *BinaryInfo) addConcreteSubprogram(entry *dwarf.Entry, ctxt *loadDebugInfoMapsContext, reader *reader.Reader, cu *compileUnit) { 2626 lowpc, highpc, ok := subprogramEntryRange(entry, cu.image) 2627 if !ok { 2628 bi.logger.Warnf("reading debug_info: concrete subprogram without address range at %#x", entry.Offset) 2629 // When clang inlines a function, in some cases, it produces a concrete 2630 // subprogram without address range and then inlined calls that reference 2631 // it, instead of producing an abstract subprogram. 2632 // It is unclear if this behavior is standard. 2633 } 2634 2635 name, ok := subprogramEntryName(entry, cu) 2636 if !ok { 2637 bi.logger.Warnf("reading debug_info: concrete subprogram without name at %#x", entry.Offset) 2638 } 2639 2640 trampoline, _ := entry.Val(dwarf.AttrTrampoline).(bool) 2641 2642 originIdx := ctxt.lookupAbstractOrigin(bi, entry.Offset) 2643 fn := &bi.Functions[originIdx] 2644 2645 fn.Name = name 2646 fn.Entry = lowpc 2647 fn.End = highpc 2648 fn.offset = entry.Offset 2649 fn.cu = cu 2650 fn.trampoline = trampoline 2651 2652 if entry.Children { 2653 bi.loadDebugInfoMapsInlinedCalls(ctxt, reader, cu) 2654 } 2655 } 2656 2657 func subprogramEntryName(entry *dwarf.Entry, cu *compileUnit) (string, bool) { 2658 name, ok := entry.Val(dwarf.AttrName).(string) 2659 if !ok { 2660 return "", false 2661 } 2662 if !cu.isgo { 2663 name = "C." + name 2664 } 2665 return name, true 2666 } 2667 2668 func subprogramEntryRange(entry *dwarf.Entry, image *Image) (lowpc, highpc uint64, ok bool) { 2669 ok = false 2670 if ranges, _ := image.dwarf.Ranges(entry); len(ranges) >= 1 { 2671 ok = true 2672 lowpc = ranges[0][0] + image.StaticBase 2673 highpc = ranges[0][1] + image.StaticBase 2674 } 2675 return lowpc, highpc, ok 2676 } 2677 2678 func (bi *BinaryInfo) loadDebugInfoMapsInlinedCalls(ctxt *loadDebugInfoMapsContext, reader *reader.Reader, cu *compileUnit) { 2679 for { 2680 entry, err := reader.Next() 2681 if err != nil { 2682 cu.image.setLoadError(bi.logger, "error reading debug_info: %v", err) 2683 return 2684 } 2685 switch entry.Tag { 2686 case 0: 2687 return 2688 case dwarf.TagInlinedSubroutine: 2689 originOffset, ok := entry.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset) 2690 if !ok { 2691 bi.logger.Warnf("reading debug_info: inlined call without origin offset at %#x", entry.Offset) 2692 reader.SkipChildren() 2693 continue 2694 } 2695 2696 lowpc, highpc, ok := subprogramEntryRange(entry, cu.image) 2697 if !ok { 2698 bi.logger.Warnf("reading debug_info: inlined call without address range at %#x", entry.Offset) 2699 reader.SkipChildren() 2700 continue 2701 } 2702 2703 callfileidx, ok1 := entry.Val(dwarf.AttrCallFile).(int64) 2704 callline, ok2 := entry.Val(dwarf.AttrCallLine).(int64) 2705 if !ok1 || !ok2 { 2706 bi.logger.Warnf("reading debug_info: inlined call without CallFile/CallLine at %#x", entry.Offset) 2707 reader.SkipChildren() 2708 continue 2709 } 2710 callfile, cferr := cu.filePath(int(callfileidx), entry) 2711 if cferr != nil { 2712 bi.logger.Warnf("%v", cferr) 2713 reader.SkipChildren() 2714 continue 2715 } 2716 2717 originIdx := ctxt.lookupAbstractOrigin(bi, originOffset) 2718 fn := &bi.Functions[originIdx] 2719 2720 fn.InlinedCalls = append(fn.InlinedCalls, InlinedCall{ 2721 cu: cu, 2722 LowPC: lowpc, 2723 HighPC: highpc, 2724 }) 2725 2726 if fn.cu == nil { 2727 fn.cu = cu 2728 } 2729 2730 fl := fileLine{callfile, int(callline)} 2731 bi.inlinedCallLines[fl] = append(bi.inlinedCallLines[fl], lowpc) 2732 2733 if entry.Children { 2734 bi.loadDebugInfoMapsInlinedCalls(ctxt, reader, cu) 2735 } 2736 } 2737 reader.SkipChildren() 2738 } 2739 } 2740 2741 func uniq(s []string) []string { 2742 if len(s) == 0 { 2743 return s 2744 } 2745 src, dst := 1, 1 2746 for src < len(s) { 2747 if s[src] != s[dst-1] { 2748 s[dst] = s[src] 2749 dst++ 2750 } 2751 src++ 2752 } 2753 return s[:dst] 2754 } 2755 2756 func (bi *BinaryInfo) expandPackagesInType(expr ast.Expr) { 2757 switch e := expr.(type) { 2758 case *ast.ArrayType: 2759 bi.expandPackagesInType(e.Elt) 2760 case *ast.ChanType: 2761 bi.expandPackagesInType(e.Value) 2762 case *ast.FuncType: 2763 for i := range e.Params.List { 2764 bi.expandPackagesInType(e.Params.List[i].Type) 2765 } 2766 if e.Results != nil { 2767 for i := range e.Results.List { 2768 bi.expandPackagesInType(e.Results.List[i].Type) 2769 } 2770 } 2771 case *ast.MapType: 2772 bi.expandPackagesInType(e.Key) 2773 bi.expandPackagesInType(e.Value) 2774 case *ast.ParenExpr: 2775 bi.expandPackagesInType(e.X) 2776 case *ast.SelectorExpr: 2777 switch x := e.X.(type) { 2778 case *ast.Ident: 2779 if len(bi.PackageMap[x.Name]) > 0 { 2780 // There's no particular reason to expect the first entry to be the 2781 // correct one if the package name is ambiguous, but trying all possible 2782 // expansions of all types mentioned in the expression is complicated 2783 // and, besides type assertions, users can always specify the type they 2784 // want exactly, using a string. 2785 x.Name = bi.PackageMap[x.Name][0] 2786 } 2787 default: 2788 bi.expandPackagesInType(e.X) 2789 } 2790 case *ast.StarExpr: 2791 bi.expandPackagesInType(e.X) 2792 default: 2793 // nothing to do 2794 } 2795 } 2796 2797 // escapePackagePath returns pkg with '.' replaced with '%2e' (in all 2798 // elements of the path except the first one) like Go does in variable and 2799 // type names. 2800 func escapePackagePath(pkg string) string { 2801 slash := strings.Index(pkg, "/") 2802 if slash < 0 { 2803 slash = 0 2804 } 2805 return pkg[:slash] + strings.ReplaceAll(pkg[slash:], ".", "%2e") 2806 } 2807 2808 // Looks up symbol (either functions or global variables) at address addr. 2809 // Used by disassembly formatter. 2810 func (bi *BinaryInfo) symLookup(addr uint64) (string, uint64) { 2811 fn := bi.PCToFunc(addr) 2812 if fn != nil { 2813 if fn.Entry == addr { 2814 // only report the function name if it's the exact address because it's 2815 // easier to read the absolute address than function_name+offset. 2816 return fn.Name, fn.Entry 2817 } 2818 return "", 0 2819 } 2820 if sym, ok := bi.SymNames[addr]; ok { 2821 return sym.Name, addr 2822 } 2823 i := sort.Search(len(bi.packageVars), func(i int) bool { 2824 return bi.packageVars[i].addr >= addr 2825 }) 2826 if i >= len(bi.packageVars) { 2827 return "", 0 2828 } 2829 if bi.packageVars[i].addr > addr { 2830 // report previous variable + offset if i-th variable starts after addr 2831 i-- 2832 } 2833 if i >= 0 && bi.packageVars[i].addr != 0 { 2834 return bi.packageVars[i].name, bi.packageVars[i].addr 2835 } 2836 return "", 0 2837 } 2838 2839 type PackageBuildInfo struct { 2840 ImportPath string 2841 DirectoryPath string 2842 Files map[string]struct{} 2843 } 2844 2845 // ListPackagesBuildInfo returns the list of packages used by the program along with 2846 // the directory where each package was compiled and optionally the list of 2847 // files constituting the package. 2848 func (bi *BinaryInfo) ListPackagesBuildInfo(includeFiles bool) []*PackageBuildInfo { 2849 m := make(map[string]*PackageBuildInfo) 2850 for _, cu := range bi.Images[0].compileUnits { 2851 if cu.image != bi.Images[0] || !cu.isgo || cu.lineInfo == nil { 2852 //TODO(aarzilli): what's the correct thing to do for plugins? 2853 continue 2854 } 2855 2856 ip := strings.ReplaceAll(cu.name, "\\", "/") 2857 if _, ok := m[ip]; !ok { 2858 path := cu.lineInfo.FirstFile() 2859 if ext := filepath.Ext(path); ext != ".go" && ext != ".s" { 2860 continue 2861 } 2862 dp := filepath.Dir(path) 2863 m[ip] = &PackageBuildInfo{ 2864 ImportPath: ip, 2865 DirectoryPath: dp, 2866 Files: make(map[string]struct{}), 2867 } 2868 } 2869 2870 if includeFiles { 2871 pbi := m[ip] 2872 2873 for _, file := range cu.lineInfo.FileNames { 2874 pbi.Files[file.Path] = struct{}{} 2875 } 2876 } 2877 } 2878 2879 r := make([]*PackageBuildInfo, 0, len(m)) 2880 for _, pbi := range m { 2881 r = append(r, pbi) 2882 } 2883 2884 sort.Slice(r, func(i, j int) bool { return r[i].ImportPath < r[j].ImportPath }) 2885 return r 2886 } 2887 2888 // cuFilePath takes a compilation unit "cu" and a file index reference 2889 // "fileidx" and returns the corresponding file name entry from the 2890 // DWARF line table associated with the unit; "entry" is the offset of 2891 // the attribute where the file reference originated, for logging 2892 // purposes. Return value is the file string and an error value; error 2893 // will be non-nil if the file could not be recovered, perhaps due to 2894 // malformed DWARF. 2895 func (cu *compileUnit) filePath(fileidx int, entry *dwarf.Entry) (string, error) { 2896 if cu.lineInfo == nil { 2897 return "", fmt.Errorf("reading debug_info: file reference within a compilation unit without debug_line section at %#x", entry.Offset) 2898 } 2899 // File numbering is slightly different before and after DWARF 5; 2900 // account for this here. See section 6.2.4 of the DWARF 5 spec. 2901 if cu.Version < 5 { 2902 fileidx-- 2903 } 2904 if fileidx < 0 || fileidx >= len(cu.lineInfo.FileNames) { 2905 return "", fmt.Errorf("reading debug_info: file index (%d) out of range in compile unit file table at %#x", fileidx, entry.Offset) 2906 } 2907 return cu.lineInfo.FileNames[fileidx].Path, nil 2908 }