Source file src/pkg/cmd/vendor/github.com/google/pprof/internal/elfexec/elfexec.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package elfexec
17
18 import (
19 "bufio"
20 "debug/elf"
21 "encoding/binary"
22 "fmt"
23 "io"
24 )
25
26 const (
27 maxNoteSize = 1 << 20
28 noteTypeGNUBuildID = 3
29 )
30
31
32 type elfNote struct {
33 Name string
34 Desc []byte
35 Type uint32
36 }
37
38
39 func parseNotes(reader io.Reader, alignment int, order binary.ByteOrder) ([]elfNote, error) {
40 r := bufio.NewReader(reader)
41
42
43
44 padding := func(size int) int {
45 return ((size + (alignment - 1)) &^ (alignment - 1)) - size
46 }
47
48 var notes []elfNote
49 for {
50 noteHeader := make([]byte, 12)
51 if _, err := io.ReadFull(r, noteHeader); err == io.EOF {
52 break
53 } else if err != nil {
54 return nil, err
55 }
56 namesz := order.Uint32(noteHeader[0:4])
57 descsz := order.Uint32(noteHeader[4:8])
58 typ := order.Uint32(noteHeader[8:12])
59
60 if uint64(namesz) > uint64(maxNoteSize) {
61 return nil, fmt.Errorf("note name too long (%d bytes)", namesz)
62 }
63 var name string
64 if namesz > 0 {
65
66
67
68 var err error
69 name, err = r.ReadString('\x00')
70 if err == io.EOF {
71 return nil, fmt.Errorf("missing note name (want %d bytes)", namesz)
72 } else if err != nil {
73 return nil, err
74 }
75 namesz = uint32(len(name))
76 name = name[:len(name)-1]
77 }
78
79
80 for n := padding(len(noteHeader) + int(namesz)); n > 0; n-- {
81 if _, err := r.ReadByte(); err == io.EOF {
82 return nil, fmt.Errorf(
83 "missing %d bytes of padding after note name", n)
84 } else if err != nil {
85 return nil, err
86 }
87 }
88
89 if uint64(descsz) > uint64(maxNoteSize) {
90 return nil, fmt.Errorf("note desc too long (%d bytes)", descsz)
91 }
92 desc := make([]byte, int(descsz))
93 if _, err := io.ReadFull(r, desc); err == io.EOF {
94 return nil, fmt.Errorf("missing desc (want %d bytes)", len(desc))
95 } else if err != nil {
96 return nil, err
97 }
98
99 notes = append(notes, elfNote{Name: name, Desc: desc, Type: typ})
100
101
102
103 for n := padding(len(desc)); n > 0; n-- {
104 if _, err := r.ReadByte(); err == io.EOF {
105
106
107
108 break
109 } else if err != nil {
110 return nil, err
111 }
112 }
113 }
114 return notes, nil
115 }
116
117
118
119
120
121 func GetBuildID(binary io.ReaderAt) ([]byte, error) {
122 f, err := elf.NewFile(binary)
123 if err != nil {
124 return nil, err
125 }
126
127 findBuildID := func(notes []elfNote) ([]byte, error) {
128 var buildID []byte
129 for _, note := range notes {
130 if note.Name == "GNU" && note.Type == noteTypeGNUBuildID {
131 if buildID == nil {
132 buildID = note.Desc
133 } else {
134 return nil, fmt.Errorf("multiple build ids found, don't know which to use")
135 }
136 }
137 }
138 return buildID, nil
139 }
140
141 for _, p := range f.Progs {
142 if p.Type != elf.PT_NOTE {
143 continue
144 }
145 notes, err := parseNotes(p.Open(), int(p.Align), f.ByteOrder)
146 if err != nil {
147 return nil, err
148 }
149 if b, err := findBuildID(notes); b != nil || err != nil {
150 return b, err
151 }
152 }
153 for _, s := range f.Sections {
154 if s.Type != elf.SHT_NOTE {
155 continue
156 }
157 notes, err := parseNotes(s.Open(), int(s.Addralign), f.ByteOrder)
158 if err != nil {
159 return nil, err
160 }
161 if b, err := findBuildID(notes); b != nil || err != nil {
162 return b, err
163 }
164 }
165 return nil, nil
166 }
167
168
169
170
171
172
173
174 func GetBase(fh *elf.FileHeader, loadSegment *elf.ProgHeader, stextOffset *uint64, start, limit, offset uint64) (uint64, error) {
175 const (
176 pageSize = 4096
177
178 pageOffsetPpc64 = 0xc000000000000000
179 )
180
181 if start == 0 && offset == 0 && (limit == ^uint64(0) || limit == 0) {
182
183
184
185 return 0, nil
186 }
187
188 switch fh.Type {
189 case elf.ET_EXEC:
190 if loadSegment == nil {
191
192 return 0, nil
193 }
194 if stextOffset == nil && start > 0 && start < 0x8000000000000000 {
195
196
197
198
199
200
201
202
203 return start - offset + loadSegment.Off - loadSegment.Vaddr, nil
204 }
205
206 if loadSegment.Vaddr == start-offset {
207 return offset, nil
208 }
209 if start == 0 && limit != 0 {
210
211
212
213
214 if stextOffset != nil {
215 return -*stextOffset, nil
216 }
217 return -loadSegment.Vaddr, nil
218 }
219 if start >= loadSegment.Vaddr && limit > start && (offset == 0 || offset == pageOffsetPpc64 || offset == start) {
220
221
222
223
224
225
226
227 if stextOffset != nil && (start%pageSize) == (*stextOffset%pageSize) {
228
229
230
231 return start - *stextOffset, nil
232 }
233
234 return start - loadSegment.Vaddr, nil
235 } else if start%pageSize != 0 && stextOffset != nil && *stextOffset%pageSize == start%pageSize {
236
237
238
239
240
241 return start - *stextOffset, nil
242 }
243
244 return 0, fmt.Errorf("don't know how to handle EXEC segment: %v start=0x%x limit=0x%x offset=0x%x", *loadSegment, start, limit, offset)
245 case elf.ET_REL:
246 if offset != 0 {
247 return 0, fmt.Errorf("don't know how to handle mapping.Offset")
248 }
249 return start, nil
250 case elf.ET_DYN:
251
252
253
254
255 if loadSegment == nil {
256 return start - offset, nil
257 }
258
259
260
261
262
263
264
265
266 return start - offset + loadSegment.Off - loadSegment.Vaddr, nil
267 }
268 return 0, fmt.Errorf("don't know how to handle FileHeader.Type %v", fh.Type)
269 }
270
271
272
273 func FindTextProgHeader(f *elf.File) *elf.ProgHeader {
274 for _, s := range f.Sections {
275 if s.Name == ".text" {
276
277 for _, p := range f.Progs {
278 if p.Type == elf.PT_LOAD && p.Flags&elf.PF_X != 0 && s.Addr >= p.Vaddr && s.Addr < p.Vaddr+p.Memsz {
279 return &p.ProgHeader
280 }
281 }
282 }
283 }
284 return nil
285 }
286
View as plain text