Skip to content

Commit

Permalink
feat: support resolve symbols in mini debug info(".gnu_debugdata") (#…
Browse files Browse the repository at this point in the history
…3590)

* Support resolve symbols in mini debug info

* Combine dynsym and mini debug info together

* Add test for mini debug info
  • Loading branch information
zmj64351508 authored Oct 1, 2024
1 parent 2959c0d commit fba3a07
Show file tree
Hide file tree
Showing 10 changed files with 310 additions and 92 deletions.
1 change: 1 addition & 0 deletions ebpf/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ require (
github.com/prometheus/prometheus v0.51.2
github.com/samber/lo v1.38.1
github.com/stretchr/testify v1.9.0
github.com/ulikunitz/xz v0.5.12
golang.org/x/sys v0.25.0
)

Expand Down
2 changes: 2 additions & 0 deletions ebpf/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ github.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM=
github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA=
Expand Down
25 changes: 23 additions & 2 deletions ebpf/symtab/elf.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,9 +176,30 @@ func (et *ElfTable) createSymbolTable(me *elf2.MMapedElfFile) (SymbolNameResolve
symbolOptions.FilterFrom = goTable.Index.Entry.Get(0)
symbolOptions.FilterTo = goTable.Index.End
}
symTable, symErr := me.NewSymbolTable(&symbolOptions)
origSymTable, origErr := me.NewSymbolTable(&symbolOptions)

var symTable elf2.SymbolTableInterface
var symErr error
if origErr == nil && origSymTable.HasSection(elf.SHT_SYMTAB) {
symTable = origSymTable
symErr = nil
} else {
miniSymTable, miniErr := me.NewMiniDebugInfoSymbolTable(&symbolOptions)
if origErr != nil && miniErr != nil {
symTable = nil
symErr = fmt.Errorf("o: %s m: %s", origErr.Error(), miniErr.Error())
} else {
tab := &elf2.SymbolTableWithMiniDebugInfo{
Primary: origSymTable,
MiniDebug: miniSymTable,
}
symTable = tab
symErr = nil
}
}

if symErr != nil && goErr != nil {
return nil, fmt.Errorf("s: %s g: %s", symErr.Error(), goErr.Error())
return nil, fmt.Errorf("s: {%s} g: {%s}", symErr.Error(), goErr.Error())
}
if symErr == nil && goErr == nil {
return &elf2.GoTableWithFallback{
Expand Down
6 changes: 3 additions & 3 deletions ebpf/symtab/elf/elf_sym.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ type SymbolsOptions struct {
}

// todo consider using ReaderAt here, same as in gopcln
func (f *MMapedElfFile) getSymbols(typ elf.SectionType, opt *SymbolsOptions) ([]SymbolIndex, uint32, error) {
func (f *InMemElfFile) getSymbols(typ elf.SectionType, opt *SymbolsOptions) ([]SymbolIndex, uint32, error) {
switch f.Class {
case elf.ELFCLASS64:
return f.getSymbols64(typ, opt)
Expand All @@ -51,7 +51,7 @@ func (f *MMapedElfFile) getSymbols(typ elf.SectionType, opt *SymbolsOptions) ([]
// if there is no such section in the File.
var ErrNoSymbols = errors.New("no symbol section")

func (f *MMapedElfFile) getSymbols64(typ elf.SectionType, opt *SymbolsOptions) ([]SymbolIndex, uint32, error) {
func (f *InMemElfFile) getSymbols64(typ elf.SectionType, opt *SymbolsOptions) ([]SymbolIndex, uint32, error) {
symtabSection := f.sectionByType(typ)
if symtabSection == nil {
return nil, 0, ErrNoSymbols
Expand Down Expand Up @@ -108,7 +108,7 @@ func (f *MMapedElfFile) getSymbols64(typ elf.SectionType, opt *SymbolsOptions) (
return symbols[:i], symtabSection.Link, nil
}

func (f *MMapedElfFile) getSymbols32(typ elf.SectionType, opt *SymbolsOptions) ([]SymbolIndex, uint32, error) {
func (f *InMemElfFile) getSymbols32(typ elf.SectionType, opt *SymbolsOptions) ([]SymbolIndex, uint32, error) {
symtabSection := f.sectionByType(typ)
if symtabSection == nil {
return nil, 0, ErrNoSymbols
Expand Down
114 changes: 114 additions & 0 deletions ebpf/symtab/elf/elfinmem.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package elf

import (
"bytes"
"debug/elf"
"io"
"strings"

"github.com/ianlancetaylor/demangle"
)

type ElfSymbolReader interface {
getString(start int, demangleOptions []demangle.Option) (string, bool)
}

type InMemElfFile struct {
elf.FileHeader
Sections []elf.SectionHeader
Progs []elf.ProgHeader
stringCache map[int]string

reader io.ReaderAt
}

func NewInMemElfFile(r io.ReaderAt) (*InMemElfFile, error) {
res := &InMemElfFile{
reader: r,
}
elfFile, err := elf.NewFile(res.reader)
if err != nil {
return nil, err
}
progs := make([]elf.ProgHeader, 0, len(elfFile.Progs))
sections := make([]elf.SectionHeader, 0, len(elfFile.Sections))
for i := range elfFile.Progs {
progs = append(progs, elfFile.Progs[i].ProgHeader)
}
for i := range elfFile.Sections {
sections = append(sections, elfFile.Sections[i].SectionHeader)
}
res.FileHeader = elfFile.FileHeader
res.Progs = progs
res.Sections = sections
return res, nil
}

func (f *InMemElfFile) Clear() {
f.stringCache = nil
f.Sections = nil
}

func (f *InMemElfFile) resetReader(r io.ReaderAt) {
f.reader = r
}

func (f *InMemElfFile) Section(name string) *elf.SectionHeader {
for i := range f.Sections {
s := &f.Sections[i]
if s.Name == name {
return s
}
}
return nil
}

func (f *InMemElfFile) sectionByType(typ elf.SectionType) *elf.SectionHeader {
for i := range f.Sections {
s := &f.Sections[i]
if s.Type == typ {
return s
}
}
return nil
}

func (f *InMemElfFile) SectionData(s *elf.SectionHeader) ([]byte, error) {
res := make([]byte, s.Size)
if _, err := f.reader.ReadAt(res, int64(s.Offset)); err != nil {
return nil, err
}
return res, nil
}

// getString extracts a string from an ELF string table.
func (f *InMemElfFile) getString(start int, demangleOptions []demangle.Option) (string, bool) {
if s, ok := f.stringCache[start]; ok {
return s, true
}
const tmpBufSize = 128
var tmpBuf [tmpBufSize]byte
sb := strings.Builder{}
for i := 0; i < 10; i++ {
_, err := f.reader.ReadAt(tmpBuf[:], int64(start+i*tmpBufSize))
if err != nil {
return "", false
}
idx := bytes.IndexByte(tmpBuf[:], 0)
if idx >= 0 {
sb.Write(tmpBuf[:idx])
s := sb.String()
if len(demangleOptions) > 0 {
s = demangle.Filter(s, demangleOptions...)
}
if f.stringCache == nil {
f.stringCache = make(map[int]string)
}
f.stringCache[start] = s
return s, true
} else {
sb.Write(tmpBuf[:])
}
}
return "", false
}
84 changes: 7 additions & 77 deletions ebpf/symtab/elf/elfmmap.go
Original file line number Diff line number Diff line change
@@ -1,26 +1,19 @@
package elf

import (
"bytes"
"debug/elf"
"fmt"
"os"
"runtime"
"strings"

"github.com/ianlancetaylor/demangle"
)

type MMapedElfFile struct {
elf.FileHeader
Sections []elf.SectionHeader
Progs []elf.ProgHeader

InMemElfFile
fpath string
err error
fd *os.File

stringCache map[int]string
}

func NewMMapedElfFile(fpath string) (*MMapedElfFile, error) {
Expand All @@ -32,47 +25,15 @@ func NewMMapedElfFile(fpath string) (*MMapedElfFile, error) {
res.Close()
return nil, err
}
elfFile, err := elf.NewFile(res.fd)
f, err := NewInMemElfFile(res.fd)
if err != nil {
res.Close()
return nil, err
}
progs := make([]elf.ProgHeader, 0, len(elfFile.Progs))
sections := make([]elf.SectionHeader, 0, len(elfFile.Sections))
for i := range elfFile.Progs {
progs = append(progs, elfFile.Progs[i].ProgHeader)
}
for i := range elfFile.Sections {
sections = append(sections, elfFile.Sections[i].SectionHeader)
}
res.FileHeader = elfFile.FileHeader
res.Progs = progs
res.Sections = sections

res.InMemElfFile = *f
runtime.SetFinalizer(res, (*MMapedElfFile).Finalize)
return res, nil
}

func (f *MMapedElfFile) Section(name string) *elf.SectionHeader {
for i := range f.Sections {
s := &f.Sections[i]
if s.Name == name {
return s
}
}
return nil
}

func (f *MMapedElfFile) sectionByType(typ elf.SectionType) *elf.SectionHeader {
for i := range f.Sections {
s := &f.Sections[i]
if s.Type == typ {
return s
}
}
return nil
}

func (f *MMapedElfFile) ensureOpen() error {
if f.fd != nil {
return nil
Expand All @@ -91,8 +52,7 @@ func (f *MMapedElfFile) Close() {
f.fd.Close()
f.fd = nil
}
f.stringCache = nil
f.Sections = nil
f.InMemElfFile.Clear()
}
func (f *MMapedElfFile) open() error {
if f.err != nil {
Expand All @@ -104,18 +64,15 @@ func (f *MMapedElfFile) open() error {
return fmt.Errorf("open elf file %s %w", f.fpath, err)
}
f.fd = fd
f.InMemElfFile.resetReader(f.fd)
return nil
}

func (f *MMapedElfFile) SectionData(s *elf.SectionHeader) ([]byte, error) {
if err := f.ensureOpen(); err != nil {
return nil, err
}
res := make([]byte, s.Size)
if _, err := f.fd.ReadAt(res, int64(s.Offset)); err != nil {
return nil, err
}
return res, nil
return f.InMemElfFile.SectionData(s)
}

func (f *MMapedElfFile) FilePath() string {
Expand All @@ -127,32 +84,5 @@ func (f *MMapedElfFile) getString(start int, demangleOptions []demangle.Option)
if err := f.ensureOpen(); err != nil {
return "", false
}
if s, ok := f.stringCache[start]; ok {
return s, true
}
const tmpBufSize = 128
var tmpBuf [tmpBufSize]byte
sb := strings.Builder{}
for i := 0; i < 10; i++ {
_, err := f.fd.ReadAt(tmpBuf[:], int64(start+i*tmpBufSize))
if err != nil {
return "", false
}
idx := bytes.IndexByte(tmpBuf[:], 0)
if idx >= 0 {
sb.Write(tmpBuf[:idx])
s := sb.String()
if len(demangleOptions) > 0 {
s = demangle.Filter(s, demangleOptions...)
}
if f.stringCache == nil {
f.stringCache = make(map[int]string)
}
f.stringCache[start] = s
return s, true
} else {
sb.Write(tmpBuf[:])
}
}
return "", false
return f.InMemElfFile.getString(start, demangleOptions)
}
Loading

0 comments on commit fba3a07

Please sign in to comment.