From 62fb83468a2237625c5e3be029a7c588602c9f56 Mon Sep 17 00:00:00 2001 From: Rafael Roquetto Date: Sat, 21 Dec 2024 15:43:36 -0600 Subject: [PATCH] Treat go binaries without offsets as generic (1.9 branch patch) (#1477) * Treat go binaries without offsets as generic There is a small corner case in which we detect no go offsets in a target executable, but if that executable happens to have a ".gosyms" section, we will end up classifying it as a Go executable, causing issues. We attach an error in this case to have the attacher treat it as a generic executable. * Fixes panic on Go instrumentation On ELF parse error, we return the list of missing fields, assuming that we would stop the instrumentation. There is an edge case error on which the missing fields set is empty so Beyla keeps with the instrumentation, but the returned list of fields is nil. * Treat Go proxies as generic binaries In the case in which we have found offsets, but these are related to a go proxy, we must set the error so that the attacher will treat this as a regular binary. --------- Co-authored-by: Mario Macias --- pkg/internal/discover/attacher.go | 4 +++- pkg/internal/discover/typer.go | 17 +++++++++++++++-- pkg/internal/goexec/structmembers.go | 2 +- 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/pkg/internal/discover/attacher.go b/pkg/internal/discover/attacher.go index 23fc892fd..b6d8632d0 100644 --- a/pkg/internal/discover/attacher.go +++ b/pkg/internal/discover/attacher.go @@ -134,9 +134,11 @@ func (ta *TraceAttacher) getTracer(ie *ebpf.Instrumentable) bool { case svc.InstrumentableGolang: // gets all the possible supported tracers for a go program, and filters out // those whose symbols are not present in the ELF functions list - if ta.Cfg.Discovery.SkipGoSpecificTracers || ta.Cfg.Discovery.SystemWide || ie.InstrumentationError != nil { + if ta.Cfg.Discovery.SkipGoSpecificTracers || ta.Cfg.Discovery.SystemWide || ie.InstrumentationError != nil || ie.Offsets == nil { if ie.InstrumentationError != nil { ta.log.Warn("Unsupported Go program detected, using generic instrumentation", "error", ie.InstrumentationError) + } else if ie.Offsets == nil { + ta.log.Warn("Go program with null offsets detected, using generic instrumentation") } if ta.reusableTracer != nil { // We need to do more than monitor PIDs. It's possible that this new diff --git a/pkg/internal/discover/typer.go b/pkg/internal/discover/typer.go index f6f79e8a1..dbe4957a5 100644 --- a/pkg/internal/discover/typer.go +++ b/pkg/internal/discover/typer.go @@ -1,6 +1,7 @@ package discover import ( + "fmt" "log/slog" "strings" @@ -124,6 +125,11 @@ func (t *typer) asInstrumentable(execElf *exec.FileInfo) ebpf.Instrumentable { instrumentableCache.Add(execElf.Ino, InstrumentedExecutable{Type: svc.InstrumentableGolang, Offsets: offsets}) return ebpf.Instrumentable{Type: svc.InstrumentableGolang, FileInfo: execElf, Offsets: offsets} } + + if err == nil { + err = fmt.Errorf("identified as a Go proxy") + } + log.Debug("identified as a Go proxy") } else { log.Debug("identified as a generic, non-Go executable") @@ -146,12 +152,19 @@ func (t *typer) asInstrumentable(execElf *exec.FileInfo) ebpf.Instrumentable { detectedType := exec.FindProcLanguage(execElf.Pid, execElf.ELF, execElf.CmdExePath) + if detectedType == svc.InstrumentableGolang && err == nil { + log.Warn("ELF binary appears to be a Go program, but no offsets were found", + "comm", execElf.CmdExePath, "pid", execElf.Pid) + + err = fmt.Errorf("could not find any Go offsets in Go binary %s", execElf.CmdExePath) + } + log.Debug("instrumented", "comm", execElf.CmdExePath, "pid", execElf.Pid, "child", child, "language", detectedType.String()) // Return the instrumentable without offsets, as it is identified as a generic // (or non-instrumentable Go proxy) executable - instrumentableCache.Add(execElf.Ino, InstrumentedExecutable{Type: detectedType, Offsets: offsets, InstrumentationError: err}) - return ebpf.Instrumentable{Type: detectedType, FileInfo: execElf, ChildPids: child, InstrumentationError: err} + instrumentableCache.Add(execElf.Ino, InstrumentedExecutable{Type: detectedType, Offsets: nil, InstrumentationError: err}) + return ebpf.Instrumentable{Type: detectedType, Offsets: nil, FileInfo: execElf, ChildPids: child, InstrumentationError: err} } func (t *typer) inspectOffsets(execElf *exec.FileInfo) (*goexec.Offsets, bool, error) { diff --git a/pkg/internal/goexec/structmembers.go b/pkg/internal/goexec/structmembers.go index 224176d6d..6beb90d15 100644 --- a/pkg/internal/goexec/structmembers.go +++ b/pkg/internal/goexec/structmembers.go @@ -391,7 +391,7 @@ func structMemberOffsetsFromDwarf(data *dwarf.Data) (FieldOffsets, map[GoOffset] log.Debug("inspecting fields for struct type", "type", typeName) if err := readMembers(reader, structMember.fields, expectedReturns, fieldOffsets); err != nil { log.Debug("error reading DWARF info", "type", typeName, "error", err) - return nil, expectedReturns + return fieldOffsets, expectedReturns } } }