Source file src/pkg/cmd/internal/buildid/note.go
1
2
3
4
5 package buildid
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, ¬eType)
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
76
77
78 func readELF(name string, f *os.File, data []byte) (buildid string, err error) {
79
80
81
82
83
84
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
112
113
114
115
116
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
161
162 if gnu != "" {
163 return gnu, nil
164 }
165
166
167 return "", nil
168 }
169
170
171
172
173
174 func readMacho(name string, f *os.File, data []byte) (buildid string, err error) {
175
176
177
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
190 return "", &os.PathError{Path: name, Op: "parse", Err: fmt.Errorf("cannot find __text section")}
191 }
192
193
194
195
196
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 }
208
View as plain text