github.com/nextlinux/gosbom@v0.81.1-0.20230627115839-1ff50c281391/gosbom/pkg/cataloger/golang/scan_binary.go (about)

     1  package golang
     2  
     3  import (
     4  	"debug/buildinfo"
     5  	"fmt"
     6  	"io"
     7  	"runtime/debug"
     8  
     9  	"github.com/nextlinux/gosbom/gosbom/pkg/cataloger/internal/unionreader"
    10  	"github.com/nextlinux/gosbom/internal/log"
    11  )
    12  
    13  // scanFile scans file to try to report the Go and module versions.
    14  func scanFile(reader unionreader.UnionReader, filename string) ([]*debug.BuildInfo, []string) {
    15  	// NOTE: multiple readers are returned to cover universal binaries, which are files
    16  	// with more than one binary
    17  	readers, err := unionreader.GetReaders(reader)
    18  	if err != nil {
    19  		log.WithFields("error", err).Warnf("failed to open a golang binary")
    20  		return nil, nil
    21  	}
    22  
    23  	var builds []*debug.BuildInfo
    24  	for _, r := range readers {
    25  		bi, err := getBuildInfo(r)
    26  		if err != nil {
    27  			log.WithFields("file", filename, "error", err).Trace("unable to read golang buildinfo")
    28  			continue
    29  		}
    30  		if bi == nil {
    31  			continue
    32  		}
    33  		builds = append(builds, bi)
    34  	}
    35  
    36  	archs := getArchs(readers, builds)
    37  
    38  	return builds, archs
    39  }
    40  
    41  func getBuildInfo(r io.ReaderAt) (bi *debug.BuildInfo, err error) {
    42  	defer func() {
    43  		if r := recover(); r != nil {
    44  			// this can happen in cases where a malformed binary is passed in can be initially parsed, but not
    45  			// used without error later down the line. This is the case with :
    46  			// https://github.com/llvm/llvm-project/blob/llvmorg-15.0.6/llvm/test/Object/Inputs/macho-invalid-dysymtab-bad-size
    47  			err = fmt.Errorf("recovered from panic: %v", r)
    48  		}
    49  	}()
    50  	bi, err = buildinfo.Read(r)
    51  
    52  	// note: the stdlib does not export the error we need to check for
    53  	if err != nil {
    54  		if err.Error() == "not a Go executable" {
    55  			// since the cataloger can only select executables and not distinguish if they are a go-compiled
    56  			// binary, we should not show warnings/logs in this case. For this reason we nil-out err here.
    57  			err = nil
    58  			return
    59  		}
    60  		// in this case we could not read the or parse the file, but not explicitly because it is not a
    61  		// go-compiled binary (though it still might be).
    62  		return
    63  	}
    64  	return
    65  }