Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
25 changes: 25 additions & 0 deletions libpf/pfelf/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,15 @@ package pfelf // import "go.opentelemetry.io/ebpf-profiler/libpf/pfelf"

import (
"bytes"
"debug/buildinfo"
"debug/elf"
"errors"
"fmt"
"hash/crc32"
"io"
"os"
"path/filepath"
"runtime/debug"
"sort"
"syscall"
"unsafe"
Expand Down Expand Up @@ -119,6 +121,9 @@ type File struct {
debuglinkPath string
// Whether we have checked for a debuglink
debuglinkChecked bool

// Contains the Go build information if present
goBuildInfo *debug.BuildInfo
}

var _ libpf.SymbolFinder = &File{}
Expand Down Expand Up @@ -489,6 +494,26 @@ func (f *File) GetBuildID() (string, error) {
return getBuildIDFromNotes(data)
}

// GoVersion returns the Go version if present and empty string otherwise. This will delegate
// to buildinfo.Read for any binaries where IsGolang is true which will scan the binary with
// debug/elf. This will incur additional CPU/IO overhead but the libpf.readbufat buffer and
// OS file buffers should ameliorate most of that.
func (f *File) GoVersion() (string, error) {
if f.goBuildInfo != nil {
return f.goBuildInfo.GoVersion, nil
}
if !f.IsGolang() {
return "", nil
}
bi, err := buildinfo.Read(f.elfReader)
if err != nil {
return "", err
}
f.goBuildInfo = bi

return bi.GoVersion, nil
}

// DebuglinkFileName returns the debug file linked by .gnu_debuglink if any
func (f *File) DebuglinkFileName(elfFilePath string, elfOpener ELFOpener) string {
if f.debuglinkChecked {
Expand Down
17 changes: 17 additions & 0 deletions libpf/pfelf/file_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
package pfelf

import (
"go/version"
"os"
"runtime"
"testing"

"github.com/stretchr/testify/assert"
Expand Down Expand Up @@ -78,3 +80,18 @@ func TestPFELFIsGolang(t *testing.T) {
testPFELFIsGolang(t, "testdata/go-binary", true)
testPFELFIsGolang(t, "testdata/without-debug-syms", false)
}

func TestGoVersion(t *testing.T) {
ef := getPFELF("testdata/go-binary", t)
defer ef.Close()

vers, err := ef.GoVersion()
require.NoError(t, err)
assert.GreaterOrEqual(t, version.Compare(vers, "go1.23.6"), 0)

testEF := getPFELF("/proc/self/exe", t)
defer testEF.Close()
testVersion, err := testEF.GoVersion()
require.NoError(t, err)
assert.Equal(t, runtime.Version(), testVersion)
}
5 changes: 2 additions & 3 deletions libpf/pfelf/testdata/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ kernel-image: test.c
ubuntu-kernel-image: test.c
$(CC) $< -s -o $@ -DLINUX_VERSION="\"Linux version 1.2.3 (Ubuntu 4.5.6)\\n\""

# A fake go binary (with a .gopclntab section)
go-binary: without-debug-syms
$(OBJCOPY) --add-section .gopclntab=/dev/null $< $@
go-binary: gotest.go
go build -o go-binary -ldflags "-w -s" gotest.go
Comment thread
rockdaboot marked this conversation as resolved.

4 changes: 4 additions & 0 deletions libpf/pfelf/testdata/gotest.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package main

func main() {
}