.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / honnef.co / go / tools@v0.1.1 / go / loader / buildid.go
1 // Copyright 2017 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package loader
6
7 import (
8         "bytes"
9         "debug/elf"
10         "errors"
11         "fmt"
12         "io"
13         "os"
14         "strconv"
15         "strings"
16 )
17
18 var errBuildIDMalformed = fmt.Errorf("malformed object file")
19
20 var (
21         bangArch = []byte("!<arch>")
22         pkgdef   = []byte("__.PKGDEF")
23         goobject = []byte("go object ")
24         buildid  = []byte("build id ")
25 )
26
27 // ReadFile reads the build ID from an archive or executable file.
28 func ReadFile(name string) (id string, err error) {
29         f, err := os.Open(name)
30         if err != nil {
31                 return "", err
32         }
33         defer f.Close()
34
35         buf := make([]byte, 8)
36         if _, err := f.ReadAt(buf, 0); err != nil {
37                 return "", err
38         }
39         if string(buf) != "!<arch>\n" {
40                 if string(buf) == "<bigaf>\n" {
41                         return "", errors.New("unsupported")
42                 }
43                 return readBinary(name, f)
44         }
45
46         // Read just enough of the target to fetch the build ID.
47         // The archive is expected to look like:
48         //
49         //      !<arch>
50         //      __.PKGDEF       0           0     0     644     7955      `
51         //      go object darwin amd64 devel X:none
52         //      build id "b41e5c45250e25c9fd5e9f9a1de7857ea0d41224"
53         //
54         // The variable-sized strings are GOOS, GOARCH, and the experiment list (X:none).
55         // Reading the first 1024 bytes should be plenty.
56         data := make([]byte, 1024)
57         n, err := io.ReadFull(f, data)
58         if err != nil && n == 0 {
59                 return "", err
60         }
61
62         tryGccgo := func() (string, error) {
63                 return readGccgoArchive(name, f)
64         }
65
66         // Archive header.
67         for i := 0; ; i++ { // returns during i==3
68                 j := bytes.IndexByte(data, '\n')
69                 if j < 0 {
70                         return tryGccgo()
71                 }
72                 line := data[:j]
73                 data = data[j+1:]
74                 switch i {
75                 case 0:
76                         if !bytes.Equal(line, bangArch) {
77                                 return tryGccgo()
78                         }
79                 case 1:
80                         if !bytes.HasPrefix(line, pkgdef) {
81                                 return tryGccgo()
82                         }
83                 case 2:
84                         if !bytes.HasPrefix(line, goobject) {
85                                 return tryGccgo()
86                         }
87                 case 3:
88                         if !bytes.HasPrefix(line, buildid) {
89                                 // Found the object header, just doesn't have a build id line.
90                                 // Treat as successful, with empty build id.
91                                 return "", nil
92                         }
93                         id, err := strconv.Unquote(string(line[len(buildid):]))
94                         if err != nil {
95                                 return tryGccgo()
96                         }
97                         return id, nil
98                 }
99         }
100 }
101
102 // readGccgoArchive tries to parse the archive as a standard Unix
103 // archive file, and fetch the build ID from the _buildid.o entry.
104 // The _buildid.o entry is written by (*Builder).gccgoBuildIDELFFile
105 // in cmd/go/internal/work/exec.go.
106 func readGccgoArchive(name string, f *os.File) (string, error) {
107         bad := func() (string, error) {
108                 return "", &os.PathError{Op: "parse", Path: name, Err: errBuildIDMalformed}
109         }
110
111         off := int64(8)
112         for {
113                 if _, err := f.Seek(off, io.SeekStart); err != nil {
114                         return "", err
115                 }
116
117                 // TODO(iant): Make a debug/ar package, and use it
118                 // here and in cmd/link.
119                 var hdr [60]byte
120                 if _, err := io.ReadFull(f, hdr[:]); err != nil {
121                         if err == io.EOF {
122                                 // No more entries, no build ID.
123                                 return "", nil
124                         }
125                         return "", err
126                 }
127                 off += 60
128
129                 sizeStr := strings.TrimSpace(string(hdr[48:58]))
130                 size, err := strconv.ParseInt(sizeStr, 0, 64)
131                 if err != nil {
132                         return bad()
133                 }
134
135                 name := strings.TrimSpace(string(hdr[:16]))
136                 if name == "_buildid.o/" {
137                         sr := io.NewSectionReader(f, off, size)
138                         e, err := elf.NewFile(sr)
139                         if err != nil {
140                                 return bad()
141                         }
142                         s := e.Section(".go.buildid")
143                         if s == nil {
144                                 return bad()
145                         }
146                         data, err := s.Data()
147                         if err != nil {
148                                 return bad()
149                         }
150                         return string(data), nil
151                 }
152
153                 off += size
154                 if off&1 != 0 {
155                         off++
156                 }
157         }
158 }
159
160 var (
161         goBuildPrefix = []byte("\xff Go build ID: \"")
162         goBuildEnd    = []byte("\"\n \xff")
163
164         elfPrefix = []byte("\x7fELF")
165
166         machoPrefixes = [][]byte{
167                 {0xfe, 0xed, 0xfa, 0xce},
168                 {0xfe, 0xed, 0xfa, 0xcf},
169                 {0xce, 0xfa, 0xed, 0xfe},
170                 {0xcf, 0xfa, 0xed, 0xfe},
171         }
172 )
173
174 var readSize = 32 * 1024 // changed for testing
175
176 // readBinary reads the build ID from a binary.
177 //
178 // ELF binaries store the build ID in a proper PT_NOTE section.
179 //
180 // Other binary formats are not so flexible. For those, the linker
181 // stores the build ID as non-instruction bytes at the very beginning
182 // of the text segment, which should appear near the beginning
183 // of the file. This is clumsy but fairly portable. Custom locations
184 // can be added for other binary types as needed, like we did for ELF.
185 func readBinary(name string, f *os.File) (id string, err error) {
186         // Read the first 32 kB of the binary file.
187         // That should be enough to find the build ID.
188         // In ELF files, the build ID is in the leading headers,
189         // which are typically less than 4 kB, not to mention 32 kB.
190         // In Mach-O files, there's no limit, so we have to parse the file.
191         // On other systems, we're trying to read enough that
192         // we get the beginning of the text segment in the read.
193         // The offset where the text segment begins in a hello
194         // world compiled for each different object format today:
195         //
196         //      Plan 9: 0x20
197         //      Windows: 0x600
198         //
199         data := make([]byte, readSize)
200         _, err = io.ReadFull(f, data)
201         if err == io.ErrUnexpectedEOF {
202                 err = nil
203         }
204         if err != nil {
205                 return "", err
206         }
207
208         if bytes.HasPrefix(data, elfPrefix) {
209                 return readELF(name, f, data)
210         }
211         for _, m := range machoPrefixes {
212                 if bytes.HasPrefix(data, m) {
213                         return readMacho(name, f, data)
214                 }
215         }
216         return readRaw(name, data)
217 }
218
219 // readRaw finds the raw build ID stored in text segment data.
220 func readRaw(name string, data []byte) (id string, err error) {
221         i := bytes.Index(data, goBuildPrefix)
222         if i < 0 {
223                 // Missing. Treat as successful but build ID empty.
224                 return "", nil
225         }
226
227         j := bytes.Index(data[i+len(goBuildPrefix):], goBuildEnd)
228         if j < 0 {
229                 return "", &os.PathError{Op: "parse", Path: name, Err: errBuildIDMalformed}
230         }
231
232         quoted := data[i+len(goBuildPrefix)-1 : i+len(goBuildPrefix)+j+1]
233         id, err = strconv.Unquote(string(quoted))
234         if err != nil {
235                 return "", &os.PathError{Op: "parse", Path: name, Err: errBuildIDMalformed}
236         }
237         return id, nil
238 }