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  }