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 }