github.com/vchain-us/vcn@v0.9.11-0.20210921212052-a2484d23c0b3/pkg/bom/golang/from_exe.go (about)

     1  /*
     2   * Copyright (c) 2021 CodeNotary, Inc. All Rights Reserved.
     3   * This software is released under GPL3.
     4   * The full license information can be found under:
     5   * https://www.gnu.org/licenses/gpl-3.0.en.html
     6   *
     7   */
     8  
     9  package golang
    10  
    11  import (
    12  	"bytes"
    13  	"encoding/binary"
    14  	"errors"
    15  	"fmt"
    16  	"strings"
    17  
    18  	"github.com/vchain-us/vcn/pkg/bom/artifact"
    19  )
    20  
    21  // goArtifactFromExe implements Artifact interface
    22  type goArtifactFromExe struct {
    23  	goArtifact
    24  	file exe
    25  }
    26  
    27  // The logic is copied from 'go version' utility source: https://golang.org/src/cmd/go/internal/version/version.go
    28  
    29  // The build info blob left by the linker is identified by
    30  // a 16-byte header, consisting of buildInfoMagic (14 bytes),
    31  // the binary's pointer size (1 byte),
    32  // and whether the binary is big endian (1 byte).
    33  var buildInfoMagic = []byte("\xff Go buildinf:")
    34  
    35  // Dependencies returns list of Go dependencies used during the build
    36  func (a *goArtifactFromExe) ResolveDependencies(output artifact.OutputOptions) ([]artifact.Dependency, error) {
    37  	if a.Deps != nil {
    38  		return a.Deps, nil
    39  	}
    40  	// Read the first 64kB of text to find the build info blob.
    41  	text := a.file.DataStart()
    42  	data, err := a.file.ReadData(text, 64*1024)
    43  	if err != nil {
    44  		return nil, err
    45  	}
    46  	for ; !bytes.HasPrefix(data, buildInfoMagic); data = data[32:] {
    47  		if len(data) < 32 {
    48  			return nil, err
    49  		}
    50  	}
    51  	// find where build info actually starts
    52  	for ; !bytes.HasPrefix(data, buildInfoMagic); data = data[32:] {
    53  		if len(data) < 32 {
    54  			return nil, errors.New("no build info found")
    55  		}
    56  	}
    57  
    58  	// Decode the blob.
    59  	ptrSize := int(data[14])
    60  	bigEndian := data[15] != 0
    61  	var bo binary.ByteOrder
    62  	if bigEndian {
    63  		bo = binary.BigEndian
    64  	} else {
    65  		bo = binary.LittleEndian
    66  	}
    67  	var readPtr func([]byte) uint64
    68  	if ptrSize == 4 {
    69  		readPtr = func(b []byte) uint64 { return uint64(bo.Uint32(b)) }
    70  	} else {
    71  		readPtr = bo.Uint64
    72  	}
    73  
    74  	mod := readString(a.file, ptrSize, readPtr, readPtr(data[16+ptrSize:]))
    75  	if len(mod) >= 33 && mod[len(mod)-17] == '\n' {
    76  		// Strip module framing.
    77  		mod = mod[16 : len(mod)-16]
    78  	} else {
    79  		return nil, errors.New("no build info found")
    80  	}
    81  
    82  	lines := strings.Split(mod, "\n")
    83  	res := make([]artifact.Dependency, 0, len(lines))
    84  	for _, line := range lines {
    85  		fields := strings.Split(line, "\t")
    86  		if (fields[0] == "dep" || fields[0] == "=>") && len(fields) == 4 {
    87  			var dep artifact.Dependency
    88  			dep.Hash, dep.HashType, err = ModHash(fields[3])
    89  			if err != nil {
    90  				return nil, fmt.Errorf("cannot decode hash: %w", err)
    91  			}
    92  			dep.Version = fields[2]
    93  			dep.Name = fields[1]
    94  			res = append(res, dep)
    95  			if output == artifact.Debug {
    96  				fmt.Printf("%s@%s (%s)\n", dep.Name, dep.Version, dep.Hash)
    97  			}
    98  		}
    99  	}
   100  
   101  	a.file.Close()
   102  
   103  	err = AddGithubLicenses(res, output)
   104  	if err != nil {
   105  		return nil, err
   106  	}
   107  
   108  	a.Deps = res
   109  	return res, nil
   110  }
   111  
   112  // readString returns the string at address addr in the executable x.
   113  func readString(x exe, ptrSize int, readPtr func([]byte) uint64, addr uint64) string {
   114  	hdr, err := x.ReadData(addr, uint64(2*ptrSize))
   115  	if err != nil || len(hdr) < 2*ptrSize {
   116  		return ""
   117  	}
   118  	dataAddr := readPtr(hdr)
   119  	dataLen := readPtr(hdr[ptrSize:])
   120  	data, err := x.ReadData(dataAddr, dataLen)
   121  	if err != nil || uint64(len(data)) < dataLen {
   122  		return ""
   123  	}
   124  	return string(data)
   125  }