Source file src/pkg/cmd/internal/buildid/buildid.go
1
2
3
4
5 package buildid
6
7 import (
8 "bytes"
9 "debug/elf"
10 "fmt"
11 "internal/xcoff"
12 "io"
13 "os"
14 "strconv"
15 "strings"
16 )
17
18 var (
19 errBuildIDToolchain = fmt.Errorf("build ID only supported in gc toolchain")
20 errBuildIDMalformed = fmt.Errorf("malformed object file")
21 errBuildIDUnknown = fmt.Errorf("lost build ID")
22 )
23
24 var (
25 bangArch = []byte("!<arch>")
26 pkgdef = []byte("__.PKGDEF")
27 goobject = []byte("go object ")
28 buildid = []byte("build id ")
29 )
30
31
32 func ReadFile(name string) (id string, err error) {
33 f, err := os.Open(name)
34 if err != nil {
35 return "", err
36 }
37 defer f.Close()
38
39 buf := make([]byte, 8)
40 if _, err := f.ReadAt(buf, 0); err != nil {
41 return "", err
42 }
43 if string(buf) != "!<arch>\n" {
44 if string(buf) == "<bigaf>\n" {
45 return readGccgoBigArchive(name, f)
46 }
47 return readBinary(name, f)
48 }
49
50
51
52
53
54
55
56
57
58
59
60 data := make([]byte, 1024)
61 n, err := io.ReadFull(f, data)
62 if err != nil && n == 0 {
63 return "", err
64 }
65
66 tryGccgo := func() (string, error) {
67 return readGccgoArchive(name, f)
68 }
69
70
71 for i := 0; ; i++ {
72 j := bytes.IndexByte(data, '\n')
73 if j < 0 {
74 return tryGccgo()
75 }
76 line := data[:j]
77 data = data[j+1:]
78 switch i {
79 case 0:
80 if !bytes.Equal(line, bangArch) {
81 return tryGccgo()
82 }
83 case 1:
84 if !bytes.HasPrefix(line, pkgdef) {
85 return tryGccgo()
86 }
87 case 2:
88 if !bytes.HasPrefix(line, goobject) {
89 return tryGccgo()
90 }
91 case 3:
92 if !bytes.HasPrefix(line, buildid) {
93
94
95 return "", nil
96 }
97 id, err := strconv.Unquote(string(line[len(buildid):]))
98 if err != nil {
99 return tryGccgo()
100 }
101 return id, nil
102 }
103 }
104 }
105
106
107
108
109
110 func readGccgoArchive(name string, f *os.File) (string, error) {
111 bad := func() (string, error) {
112 return "", &os.PathError{Op: "parse", Path: name, Err: errBuildIDMalformed}
113 }
114
115 off := int64(8)
116 for {
117 if _, err := f.Seek(off, io.SeekStart); err != nil {
118 return "", err
119 }
120
121
122
123 var hdr [60]byte
124 if _, err := io.ReadFull(f, hdr[:]); err != nil {
125 if err == io.EOF {
126
127 return "", nil
128 }
129 return "", err
130 }
131 off += 60
132
133 sizeStr := strings.TrimSpace(string(hdr[48:58]))
134 size, err := strconv.ParseInt(sizeStr, 0, 64)
135 if err != nil {
136 return bad()
137 }
138
139 name := strings.TrimSpace(string(hdr[:16]))
140 if name == "_buildid.o/" {
141 sr := io.NewSectionReader(f, off, size)
142 e, err := elf.NewFile(sr)
143 if err != nil {
144 return bad()
145 }
146 s := e.Section(".go.buildid")
147 if s == nil {
148 return bad()
149 }
150 data, err := s.Data()
151 if err != nil {
152 return bad()
153 }
154 return string(data), nil
155 }
156
157 off += size
158 if off&1 != 0 {
159 off++
160 }
161 }
162 }
163
164
165
166
167
168 func readGccgoBigArchive(name string, f *os.File) (string, error) {
169 bad := func() (string, error) {
170 return "", &os.PathError{Op: "parse", Path: name, Err: errBuildIDMalformed}
171 }
172
173
174 if _, err := f.Seek(0, io.SeekStart); err != nil {
175 return "", err
176 }
177 var flhdr [128]byte
178 if _, err := io.ReadFull(f, flhdr[:]); err != nil {
179 return "", err
180 }
181
182 offStr := strings.TrimSpace(string(flhdr[68:88]))
183 off, err := strconv.ParseInt(offStr, 10, 64)
184 if err != nil {
185 return bad()
186 }
187 for {
188 if off == 0 {
189
190 return "", nil
191 }
192 if _, err := f.Seek(off, io.SeekStart); err != nil {
193 return "", err
194 }
195
196 var hdr [112]byte
197 if _, err := io.ReadFull(f, hdr[:]); err != nil {
198 return "", err
199 }
200
201 namLenStr := strings.TrimSpace(string(hdr[108:112]))
202 namLen, err := strconv.ParseInt(namLenStr, 10, 32)
203 if err != nil {
204 return bad()
205 }
206 if namLen == 10 {
207 var nam [10]byte
208 if _, err := io.ReadFull(f, nam[:]); err != nil {
209 return "", err
210 }
211 if string(nam[:]) == "_buildid.o" {
212 sizeStr := strings.TrimSpace(string(hdr[0:20]))
213 size, err := strconv.ParseInt(sizeStr, 10, 64)
214 if err != nil {
215 return bad()
216 }
217 off += int64(len(hdr)) + namLen + 2
218 if off&1 != 0 {
219 off++
220 }
221 sr := io.NewSectionReader(f, off, size)
222 x, err := xcoff.NewFile(sr)
223 if err != nil {
224 return bad()
225 }
226 data := x.CSect(".go.buildid")
227 if data == nil {
228 return bad()
229 }
230 return string(data), nil
231 }
232 }
233
234
235 offStr = strings.TrimSpace(string(hdr[20:40]))
236 off, err = strconv.ParseInt(offStr, 10, 64)
237 if err != nil {
238 return bad()
239 }
240 }
241 }
242
243 var (
244 goBuildPrefix = []byte("\xff Go build ID: \"")
245 goBuildEnd = []byte("\"\n \xff")
246
247 elfPrefix = []byte("\x7fELF")
248
249 machoPrefixes = [][]byte{
250 {0xfe, 0xed, 0xfa, 0xce},
251 {0xfe, 0xed, 0xfa, 0xcf},
252 {0xce, 0xfa, 0xed, 0xfe},
253 {0xcf, 0xfa, 0xed, 0xfe},
254 }
255 )
256
257 var readSize = 32 * 1024
258
259
260
261
262
263
264
265
266
267
268 func readBinary(name string, f *os.File) (id string, err error) {
269
270
271
272
273
274
275
276
277
278
279
280
281
282 data := make([]byte, readSize)
283 _, err = io.ReadFull(f, data)
284 if err == io.ErrUnexpectedEOF {
285 err = nil
286 }
287 if err != nil {
288 return "", err
289 }
290
291 if bytes.HasPrefix(data, elfPrefix) {
292 return readELF(name, f, data)
293 }
294 for _, m := range machoPrefixes {
295 if bytes.HasPrefix(data, m) {
296 return readMacho(name, f, data)
297 }
298 }
299 return readRaw(name, data)
300 }
301
302
303 func readRaw(name string, data []byte) (id string, err error) {
304 i := bytes.Index(data, goBuildPrefix)
305 if i < 0 {
306
307 return "", nil
308 }
309
310 j := bytes.Index(data[i+len(goBuildPrefix):], goBuildEnd)
311 if j < 0 {
312 return "", &os.PathError{Op: "parse", Path: name, Err: errBuildIDMalformed}
313 }
314
315 quoted := data[i+len(goBuildPrefix)-1 : i+len(goBuildPrefix)+j+1]
316 id, err = strconv.Unquote(string(quoted))
317 if err != nil {
318 return "", &os.PathError{Op: "parse", Path: name, Err: errBuildIDMalformed}
319 }
320 return id, nil
321 }
322
View as plain text