Skip to content

Commit

Permalink
pkg/symbol/elfutils: Fix handling of stripped Go binaries
Browse files Browse the repository at this point in the history
Go binaries that are stripped, for example built with:

```
go build -ldflags "-w -s" main.go
```

Have an empty symbol table section, which some additional build steps
may strip, since it's empty.

Before this patch, those Go binaries would run into an error case when
attempting to load the symbol table.

Since the only truly necessary section for symbolization is the
`.gopclntab` section, this patch proposes to only check for this
section.
  • Loading branch information
brancz committed Nov 9, 2022
1 parent a6cae59 commit 9f0ceef
Show file tree
Hide file tree
Showing 2 changed files with 4 additions and 37 deletions.
35 changes: 3 additions & 32 deletions pkg/symbol/elfutils/elfutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,45 +73,16 @@ func readableDWARFSections(f *elf.File) (map[string]struct{}, error) {

// IsSymbolizableGoObjFile checks whether the specified executable or library file is generated by Go toolchain
// and has necessary symbol information attached.
func IsSymbolizableGoObjFile(f *elf.File) (bool, error) {
// Checks ".note.go.buildid" section and symtab better to keep those sections in object file.
isGo := false
for _, s := range f.Sections {
if s.Name == ".note.go.buildid" {
isGo = true
}
}

// In case ".note.go.buildid" section is stripped, check for symbols.
if !isGo {
syms, err := f.Symbols()
if err != nil {
return false, fmt.Errorf("failed to read symbols: %w", err)
}
for _, sym := range syms {
name := sym.Name
if name == "runtime.main" || name == "main.main" {
isGo = true
}
if name == "runtime.buildVersion" {
isGo = true
}
}
}

if !isGo {
return false, nil
}

func IsSymbolizableGoObjFile(f *elf.File) bool {
// Check if the Go binary symbolizable.
// Go binaries has a special case. They use ".gopclntab" section to symbolize addresses.
if sec := f.Section(".gopclntab"); sec != nil {
if sec.Type == elf.SHT_PROGBITS {
return true, nil
return true
}
}

return false, errors.New("failed to detect .gopclntab section or section has no bits")
return false
}

// HasSymbols reports whether the specified executable or library file contains symbols (both.symtab and .dynsym).
Expand Down
6 changes: 1 addition & 5 deletions pkg/symbol/symbol.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,11 +242,7 @@ func (s *Symbolizer) newLiner(buildID, path string) (liner, error) {

// Go binaries has a special case. They use ".gopclntab" section to symbolize addresses.
// Keep that section and other identifying sections in the debug information file.
isGo, err := elfutils.IsSymbolizableGoObjFile(f)
if err != nil {
level.Debug(logger).Log("msg", "failed to determine if binary is a Go binary", "err", err)
}
if isGo {
if elfutils.IsSymbolizableGoObjFile(f) {
// Right now, this uses "debug/gosym" package, and it won't work for inlined functions,
// so this is just a best-effort implementation, in case we don't have DWARF.
lnr, err := addr2line.Go(logger, f)
Expand Down

0 comments on commit 9f0ceef

Please sign in to comment.