Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ebpf: fix elf base for unaligned PT_LOAD offset, make build id error not fatal #2983

Merged
merged 9 commits into from
Feb 5, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 13 additions & 5 deletions ebpf/symtab/elf.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,12 @@ func (et *ElfTable) findBase(e *elf2.MMapedElfFile) bool {
et.base = m.StartAddr - prog.Vaddr
return true
}
alignedProgOffset := uint64(prog.Off) & 0xfffffffffffff000
if uint64(m.Offset) == alignedProgOffset {
d := prog.Off - alignedProgOffset
et.base = m.StartAddr + d - prog.Vaddr
return true
}
}
}
return false
Expand All @@ -98,13 +104,12 @@ func (et *ElfTable) load() {
defer me.Close() // todo do not close if it is the selected elf

if !et.findBase(me) {
et.err = errElfBaseNotFound
et.onLoadError(errElfBaseNotFound)
return
}
buildID, err := me.BuildID()
if err != nil && !errors.Is(err, elf2.ErrNoBuildIDSection) {
et.onLoadError(err)
return
if err != nil {
level.Error(et.logger).Log("msg", "failed to get build id", "err", err, "f", et.elfFilePath, "fs", et.fs)
}

symbols := et.options.ElfCache.GetSymbolsByBuildID(buildID)
Expand Down Expand Up @@ -145,7 +150,6 @@ func (et *ElfTable) load() {
}

symbols, err = et.createSymbolTable(me)
level.Debug(et.logger).Log("msg", "create symbol table", "f", me.FilePath())
if err != nil {
et.onLoadError(err)
return
Expand All @@ -160,6 +164,7 @@ func (et *ElfTable) load() {
}

func (et *ElfTable) createSymbolTable(me *elf2.MMapedElfFile) (SymbolNameResolver, error) {
level.Debug(et.logger).Log("msg", "create symbol table", "f", me.FilePath())
korniltsev marked this conversation as resolved.
Show resolved Hide resolved
goTable, goErr := me.NewGoTable()
if !et.options.SymbolOptions.GoTableFallback && goErr == nil {
return goTable, nil
Expand Down Expand Up @@ -351,5 +356,8 @@ func errorType(err error) string {
if errors.Is(err, os.ErrInvalid) {
return "ErrInvalid"
}
if errors.Is(err, errElfBaseNotFound) {
return "ElfBaseNotFound"
}
return "Other"
}
6 changes: 3 additions & 3 deletions ebpf/symtab/elf/buildid.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,14 +87,14 @@ func (f *MMapedElfFile) GNUBuildID() (BuildID, error) {
return BuildID{}, fmt.Errorf("reading .note.gnu.build-id %w", err)
}
if len(data) < 16 {
return BuildID{}, fmt.Errorf(".note.gnu.build-id is too small")
return BuildID{}, fmt.Errorf(".note.gnu.build-id is too small : %s", hex.EncodeToString(data))
}
if !bytes.Equal([]byte("GNU"), data[12:15]) {
return BuildID{}, fmt.Errorf(".note.gnu.build-id is not a GNU build-id")
return BuildID{}, fmt.Errorf(".note.gnu.build-id is not a GNU build-id : %s", hex.EncodeToString(data))
}
rawBuildID := data[16:]
if len(rawBuildID) != 20 && len(rawBuildID) != 8 { // 8 is xxhash, for example in Container-Optimized OS
return BuildID{}, fmt.Errorf(".note.gnu.build-id has wrong size %s", f.fpath)
return BuildID{}, fmt.Errorf(".note.gnu.build-id has wrong size %s : %s ", f.fpath, hex.EncodeToString(data))
}
buildIDHex := hex.EncodeToString(rawBuildID)
return GNUBuildID(buildIDHex), nil
Expand Down
64 changes: 64 additions & 0 deletions ebpf/symtab/elf_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package symtab

import (
elf2 "debug/elf"
"testing"

"github.com/grafana/pyroscope/ebpf/metrics"
"github.com/grafana/pyroscope/ebpf/symtab/elf"
"github.com/grafana/pyroscope/ebpf/util"
"github.com/stretchr/testify/assert"

"github.com/stretchr/testify/require"
)
Expand Down Expand Up @@ -102,3 +104,65 @@ func TestGoTableFallbackDisabled(t *testing.T) {
}

}

func TestFindBaseExec(t *testing.T) {
et := ElfTable{}
ef := elf.MMapedElfFile{}
ef.FileHeader.Type = elf2.ET_EXEC
assert.True(t, et.findBase(&ef))
assert.Equal(t, uint64(0), et.base)
}

func TestFindBaseAlignedNoSeparateCode(t *testing.T) {
//559f9d29f000-559f9d2ab000 r-xp 00000000 fc:01 53049243 /FibProfilingTest
//559f9d2ab000-559f9d2ac000 r--p 0000b000 fc:01 53049243 /FibProfilingTest
//559f9d2ac000-559f9d2ad000 rw-p 0000c000 fc:01 53049243 /FibProfilingTest

//Program Headers:
//Type Offset VirtAddr PhysAddr
// FileSiz MemSiz Flags Align
//LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000
// 0x000000000000b1f4 0x000000000000b1f4 R E 0x1000
// LOAD 0x000000000000bd90 0x000000000000cd90 0x000000000000cd90
// 0x0000000000000370 0x00000000000003f8 RW 0x1000

et := ElfTable{}
et.procMap = &ProcMap{StartAddr: 0x559f9d29f000, EndAddr: 0x559f9d2ab000, Perms: &ProcMapPermissions{Execute: true, Read: true}, Offset: 0}

ef := elf.MMapedElfFile{}
ef.FileHeader.Type = elf2.ET_DYN
ef.Progs = []elf2.ProgHeader{
{Type: elf2.PT_LOAD, Flags: elf2.PF_X | elf2.PF_R, Off: 0, Vaddr: 0, Filesz: 0xb1f4, Memsz: 0xb1f4},
}
assert.True(t, et.findBase(&ef))
assert.Equal(t, uint64(0x559f9d29f000), et.base)
}

func TestFindBaseUnalignedSeparateCode(t *testing.T) {
//555e3d192000-555e3d1ac000 r--p 00000000 00:3e 1824988 /smoketest
//555e3d1ac000-555e3d212000 r-xp 00019000 00:3e 1824988 /smoketest
//555e3d212000-555e3d218000 r--p 0007e000 00:3e 1824988 /smoketest
//555e3d218000-555e3d219000 rw-p 00083000 00:3e 1824988 /smoketest

//Type Offset VirtAddr PhysAddr
// FileSiz MemSiz Flags Align
//LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000
// 0x0000000000019194 0x0000000000019194 R 0x1000
//LOAD 0x00000000000191a0 0x000000000001a1a0 0x000000000001a1a0
// 0x0000000000064ee0 0x0000000000064ee0 R E 0x1000
//LOAD 0x000000000007e080 0x0000000000080080 0x0000000000080080
// 0x0000000000005928 0x0000000000005928 RW 0x1000
//LOAD 0x00000000000839b0 0x00000000000869b0 0x00000000000869b0
// 0x0000000000000300 0x000000000020a764 RW 0x1000

et := ElfTable{}
et.procMap = &ProcMap{StartAddr: 0x555e3d1ac000, EndAddr: 0x555e3d212000, Perms: &ProcMapPermissions{Execute: true, Read: true}, Offset: 0x00019000}

ef := elf.MMapedElfFile{}
ef.FileHeader.Type = elf2.ET_DYN
ef.Progs = []elf2.ProgHeader{
{Type: elf2.PT_LOAD, Flags: elf2.PF_X | elf2.PF_R, Off: 0x191a0, Vaddr: 0x1a1a0, Filesz: 0x64ee0, Memsz: 0x64ee0},
}
assert.True(t, et.findBase(&ef))
assert.Equal(t, uint64(0x555e3d192000), et.base)
}
Loading