.gitignore added
[dotfiles/.git] / .config / coc / extensions / coc-go-data / tools / pkg / mod / honnef.co / go / tools@v0.1.1 / go / loader / note.go
1 // Copyright 2015 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         "debug/macho"
11         "encoding/binary"
12         "fmt"
13         "io"
14         "os"
15 )
16
17 func readAligned4(r io.Reader, sz int32) ([]byte, error) {
18         full := (sz + 3) &^ 3
19         data := make([]byte, full)
20         _, err := io.ReadFull(r, data)
21         if err != nil {
22                 return nil, err
23         }
24         data = data[:sz]
25         return data, nil
26 }
27
28 func ReadELFNote(filename, name string, typ int32) ([]byte, error) {
29         f, err := elf.Open(filename)
30         if err != nil {
31                 return nil, err
32         }
33         defer f.Close()
34         for _, sect := range f.Sections {
35                 if sect.Type != elf.SHT_NOTE {
36                         continue
37                 }
38                 r := sect.Open()
39                 for {
40                         var namesize, descsize, noteType int32
41                         err = binary.Read(r, f.ByteOrder, &namesize)
42                         if err != nil {
43                                 if err == io.EOF {
44                                         break
45                                 }
46                                 return nil, fmt.Errorf("read namesize failed: %v", err)
47                         }
48                         err = binary.Read(r, f.ByteOrder, &descsize)
49                         if err != nil {
50                                 return nil, fmt.Errorf("read descsize failed: %v", err)
51                         }
52                         err = binary.Read(r, f.ByteOrder, &noteType)
53                         if err != nil {
54                                 return nil, fmt.Errorf("read type failed: %v", err)
55                         }
56                         noteName, err := readAligned4(r, namesize)
57                         if err != nil {
58                                 return nil, fmt.Errorf("read name failed: %v", err)
59                         }
60                         desc, err := readAligned4(r, descsize)
61                         if err != nil {
62                                 return nil, fmt.Errorf("read desc failed: %v", err)
63                         }
64                         if name == string(noteName) && typ == noteType {
65                                 return desc, nil
66                         }
67                 }
68         }
69         return nil, nil
70 }
71
72 var elfGoNote = []byte("Go\x00\x00")
73 var elfGNUNote = []byte("GNU\x00")
74
75 // The Go build ID is stored in a note described by an ELF PT_NOTE prog
76 // header. The caller has already opened filename, to get f, and read
77 // at least 4 kB out, in data.
78 func readELF(name string, f *os.File, data []byte) (buildid string, err error) {
79         // Assume the note content is in the data, already read.
80         // Rewrite the ELF header to set shnum to 0, so that we can pass
81         // the data to elf.NewFile and it will decode the Prog list but not
82         // try to read the section headers and the string table from disk.
83         // That's a waste of I/O when all we care about is the Prog list
84         // and the one ELF note.
85         switch elf.Class(data[elf.EI_CLASS]) {
86         case elf.ELFCLASS32:
87                 data[48] = 0
88                 data[49] = 0
89         case elf.ELFCLASS64:
90                 data[60] = 0
91                 data[61] = 0
92         }
93
94         const elfGoBuildIDTag = 4
95         const gnuBuildIDTag = 3
96
97         ef, err := elf.NewFile(bytes.NewReader(data))
98         if err != nil {
99                 return "", &os.PathError{Path: name, Op: "parse", Err: err}
100         }
101         var gnu string
102         for _, p := range ef.Progs {
103                 if p.Type != elf.PT_NOTE || p.Filesz < 16 {
104                         continue
105                 }
106
107                 var note []byte
108                 if p.Off+p.Filesz < uint64(len(data)) {
109                         note = data[p.Off : p.Off+p.Filesz]
110                 } else {
111                         // For some linkers, such as the Solaris linker,
112                         // the buildid may not be found in data (which
113                         // likely contains the first 16kB of the file)
114                         // or even the first few megabytes of the file
115                         // due to differences in note segment placement;
116                         // in that case, extract the note data manually.
117                         _, err = f.Seek(int64(p.Off), io.SeekStart)
118                         if err != nil {
119                                 return "", err
120                         }
121
122                         note = make([]byte, p.Filesz)
123                         _, err = io.ReadFull(f, note)
124                         if err != nil {
125                                 return "", err
126                         }
127                 }
128
129                 filesz := p.Filesz
130                 off := p.Off
131                 for filesz >= 16 {
132                         nameSize := ef.ByteOrder.Uint32(note)
133                         valSize := ef.ByteOrder.Uint32(note[4:])
134                         tag := ef.ByteOrder.Uint32(note[8:])
135                         nname := note[12:16]
136                         if nameSize == 4 && 16+valSize <= uint32(len(note)) && tag == elfGoBuildIDTag && bytes.Equal(nname, elfGoNote) {
137                                 return string(note[16 : 16+valSize]), nil
138                         }
139
140                         if nameSize == 4 && 16+valSize <= uint32(len(note)) && tag == gnuBuildIDTag && bytes.Equal(nname, elfGNUNote) {
141                                 gnu = string(note[16 : 16+valSize])
142                         }
143
144                         nameSize = (nameSize + 3) &^ 3
145                         valSize = (valSize + 3) &^ 3
146                         notesz := uint64(12 + nameSize + valSize)
147                         if filesz <= notesz {
148                                 break
149                         }
150                         off += notesz
151                         align := p.Align
152                         alignedOff := (off + align - 1) &^ (align - 1)
153                         notesz += alignedOff - off
154                         off = alignedOff
155                         filesz -= notesz
156                         note = note[notesz:]
157                 }
158         }
159
160         // If we didn't find a Go note, use a GNU note if available.
161         // This is what gccgo uses.
162         if gnu != "" {
163                 return gnu, nil
164         }
165
166         // No note. Treat as successful but build ID empty.
167         return "", nil
168 }
169
170 // The Go build ID is stored at the beginning of the Mach-O __text segment.
171 // The caller has already opened filename, to get f, and read a few kB out, in data.
172 // Sadly, that's not guaranteed to hold the note, because there is an arbitrary amount
173 // of other junk placed in the file ahead of the main text.
174 func readMacho(name string, f *os.File, data []byte) (buildid string, err error) {
175         // If the data we want has already been read, don't worry about Mach-O parsing.
176         // This is both an optimization and a hedge against the Mach-O parsing failing
177         // in the future due to, for example, the name of the __text section changing.
178         if b, err := readRaw(name, data); b != "" && err == nil {
179                 return b, err
180         }
181
182         mf, err := macho.NewFile(f)
183         if err != nil {
184                 return "", &os.PathError{Path: name, Op: "parse", Err: err}
185         }
186
187         sect := mf.Section("__text")
188         if sect == nil {
189                 // Every binary has a __text section. Something is wrong.
190                 return "", &os.PathError{Path: name, Op: "parse", Err: fmt.Errorf("cannot find __text section")}
191         }
192
193         // It should be in the first few bytes, but read a lot just in case,
194         // especially given our past problems on OS X with the build ID moving.
195         // There shouldn't be much difference between reading 4kB and 32kB:
196         // the hard part is getting to the data, not transferring it.
197         n := sect.Size
198         if n > uint64(readSize) {
199                 n = uint64(readSize)
200         }
201         buf := make([]byte, n)
202         if _, err := f.ReadAt(buf, int64(sect.Offset)); err != nil {
203                 return "", err
204         }
205
206         return readRaw(name, buf)
207 }