github.com/pyroscope-io/pyroscope@v0.37.3-0.20230725203016-5f6947968bd0/pkg/agent/ebpfspy/symtab/gosym.go (about) 1 package symtab 2 3 import ( 4 "debug/elf" 5 "debug/gosym" 6 "encoding/binary" 7 "errors" 8 "fmt" 9 "os" 10 ) 11 12 type GoSymbolTable struct { 13 file string 14 tab *SimpleSymbolTable 15 // for non go symbols 16 fallback *func() SymbolTable 17 fallbackTable SymbolTable 18 } 19 20 func NewGoSymbolTable(file string, fallback *func() SymbolTable) (*GoSymbolTable, error) { 21 f, err := os.Open(file) 22 if err != nil { 23 return nil, err 24 } 25 defer func() { 26 _ = f.Close() 27 }() 28 29 var gosymtab, pclntab []byte 30 31 obj, err := elf.NewFile(f) 32 33 text := obj.Section(".text") 34 if text == nil { 35 return nil, errors.New("empty .text") 36 } 37 if sect := obj.Section(".gosymtab"); sect != nil { 38 if gosymtab, err = sect.Data(); err != nil { 39 return nil, err 40 } 41 } 42 if sect := obj.Section(".gopclntab"); sect != nil { 43 if pclntab, err = sect.Data(); err != nil { 44 return nil, err 45 } 46 } else { 47 return nil, errors.New("empty .gopclntab") 48 } 49 50 textStart := parseRuntimeTextFromPclntab18(pclntab) 51 if textStart == 0 { 52 // for older versions text.Addr is enough 53 // https://github.com/golang/go/commit/b38ab0ac5f78ac03a38052018ff629c03e36b864 54 textStart = text.Addr 55 } 56 if textStart < text.Addr || textStart >= text.Addr+text.Size { 57 return nil, fmt.Errorf(" runtime.text out of .text bounds %d %d %d", textStart, text.Addr, text.Size) 58 } 59 pcln := gosym.NewLineTable(pclntab, textStart) 60 table, err := gosym.NewTable(gosymtab, pcln) 61 if err != nil { 62 return nil, err 63 } 64 if len(table.Funcs) == 0 { 65 return nil, errors.New("gosymtab: no symbols found") 66 } 67 68 es := make([]SimpleSymbolTableEntry, 0, len(table.Funcs)) 69 for _, fun := range table.Funcs { 70 es = append(es, SimpleSymbolTableEntry{Entry: fun.Entry, End: fun.End, Name: fun.Name}) 71 } 72 res := &GoSymbolTable{ 73 file: file, 74 tab: NewSimpleSymbolTable(es), 75 fallback: fallback, 76 } 77 return res, err 78 } 79 80 func (g *GoSymbolTable) Resolve(addr uint64, refresh bool) Symbol { 81 sym := g.tab.Resolve(addr) 82 83 if sym != "" { 84 return Symbol{Name: sym, Module: g.file, Offset: addr} 85 } 86 if g.fallback == nil { 87 return Symbol{"", "", addr} 88 } 89 if g.fallbackTable == nil { 90 g.fallbackTable = (*g.fallback)() 91 } 92 return g.fallbackTable.Resolve(addr, refresh) 93 } 94 95 func (g *GoSymbolTable) Close() { 96 if g.fallbackTable != nil { 97 g.fallbackTable.Close() 98 } 99 } 100 101 func parseRuntimeTextFromPclntab18(pclntab []byte) uint64 { 102 if len(pclntab) < 64 { 103 return 0 104 } 105 magic := binary.LittleEndian.Uint32(pclntab[0:4]) 106 if magic == 0xFFFFFFF0 { 107 // https://github.com/golang/go/blob/go1.18/src/runtime/symtab.go#L395 108 //type pcHeader struct { 109 // magic uint32 // 0xFFFFFFF0 110 // pad1, pad2 uint8 // 0,0 111 // minLC uint8 // min instruction size 112 // ptrSize uint8 // size of a ptr in bytes 113 // nfunc int // number of functions in the module 114 // nfiles uint // number of entries in the file tab 115 // textStart uintptr // base for function entry PC offsets in this module, equal to moduledata.text 116 // funcnameOffset uintptr // offset to the funcnametab variable from pcHeader 117 // cuOffset uintptr // offset to the cutab variable from pcHeader 118 // filetabOffset uintptr // offset to the filetab variable from pcHeader 119 // pctabOffset uintptr // offset to the pctab variable from pcHeader 120 // pclnOffset uintptr // offset to the pclntab variable from pcHeader 121 //} 122 textStart := binary.LittleEndian.Uint64(pclntab[24:32]) 123 return textStart 124 } 125 return 0 126 }