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 func kernelBase(loadSegment *elf.ProgHeader, stextOffset *uint64, start, limit, offset uint64) (uint64, bool) {
173 const (
174
175 pageOffsetPpc64 = 0xc000000000000000
176 pageSize = 4096
177 )
178
179 if loadSegment.Vaddr == start-offset {
180 return offset, true
181 }
182 if start == 0 && limit != 0 && stextOffset != nil {
183
184
185
186
187 return start - *stextOffset, true
188 }
189 if start >= loadSegment.Vaddr && limit > start && (offset == 0 || offset == pageOffsetPpc64 || offset == start) {
190
191
192
193
194
195
196
197 if stextOffset != nil && (start%pageSize) == (*stextOffset%pageSize) {
198
199
200
201 return start - *stextOffset, true
202 }
203
204 return start - loadSegment.Vaddr, true
205 }
206 if start%pageSize != 0 && stextOffset != nil && *stextOffset%pageSize == start%pageSize {
207
208
209
210
211
212 return start - *stextOffset, true
213 }
214 return 0, false
215 }
216
217
218
219
220
221 func GetBase(fh *elf.FileHeader, loadSegment *elf.ProgHeader, stextOffset *uint64, start, limit, offset uint64) (uint64, error) {
222
223 if start == 0 && offset == 0 && (limit == ^uint64(0) || limit == 0) {
224
225
226
227 return 0, nil
228 }
229
230 switch fh.Type {
231 case elf.ET_EXEC:
232 if loadSegment == nil {
233
234 return 0, nil
235 }
236 if stextOffset == nil && start > 0 && start < 0x8000000000000000 {
237
238
239
240
241
242
243
244
245 return start - offset + loadSegment.Off - loadSegment.Vaddr, nil
246 }
247
248 if base, match := kernelBase(loadSegment, stextOffset, start, limit, offset); match {
249 return base, nil
250 }
251
252
253
254 if start == 0 && limit != 0 && stextOffset == nil {
255 return start - loadSegment.Vaddr, nil
256 }
257
258 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)
259 case elf.ET_REL:
260 if offset != 0 {
261 return 0, fmt.Errorf("don't know how to handle mapping.Offset")
262 }
263 return start, nil
264 case elf.ET_DYN:
265
266
267
268
269 if loadSegment == nil {
270 return start - offset, nil
271 }
272
273
274 if base, match := kernelBase(loadSegment, stextOffset, start, limit, offset); match {
275 return base, nil
276 }
277
278
279
280
281
282
283
284
285 return start - offset + loadSegment.Off - loadSegment.Vaddr, nil
286 }
287 return 0, fmt.Errorf("don't know how to handle FileHeader.Type %v", fh.Type)
288 }
289
290
291
292 func FindTextProgHeader(f *elf.File) *elf.ProgHeader {
293 for _, s := range f.Sections {
294 if s.Name == ".text" {
295
296 for _, p := range f.Progs {
297 if p.Type == elf.PT_LOAD && p.Flags&elf.PF_X != 0 && s.Addr >= p.Vaddr && s.Addr < p.Vaddr+p.Memsz {
298 return &p.ProgHeader
299 }
300 }
301 }
302 }
303 return nil
304 }
305
306
307
308
309
310
311
312
313
314
315 func ProgramHeadersForMapping(phdrs []elf.ProgHeader, mapOff, mapSz uint64) []*elf.ProgHeader {
316 const (
317
318
319
320
321
322
323
324 pageSize = 4096
325 pageOffsetMask = pageSize - 1
326 )
327 mapLimit := mapOff + mapSz
328 var headers []*elf.ProgHeader
329 for i := range phdrs {
330 p := &phdrs[i]
331
332
333 if p.Filesz == 0 {
334 continue
335 }
336 segLimit := p.Off + p.Memsz
337
338 if p.Type == elf.PT_LOAD && mapOff < segLimit && p.Off < mapLimit {
339
340
341
342 alignedSegOffset := uint64(0)
343 if p.Off > (p.Vaddr & pageOffsetMask) {
344 alignedSegOffset = p.Off - (p.Vaddr & pageOffsetMask)
345 }
346 if mapOff < alignedSegOffset {
347 continue
348 }
349
350
351
352 if mapOff > p.Off && (segLimit < mapOff+pageSize) && (mapLimit >= segLimit+pageSize) {
353 continue
354 }
355 headers = append(headers, p)
356 }
357 }
358 return headers
359 }
360
361
362
363
364 func HeaderForFileOffset(headers []*elf.ProgHeader, fileOffset uint64) (*elf.ProgHeader, error) {
365 var ph *elf.ProgHeader
366 for _, h := range headers {
367 if fileOffset >= h.Off && fileOffset < h.Off+h.Memsz {
368 if ph != nil {
369
370
371
372
373
374 return nil, fmt.Errorf("found second program header (%#v) that matches file offset %x, first program header is %#v. Is this a stripped binary, or does the first program segment contain uninitialized data?", *h, fileOffset, *ph)
375 }
376 ph = h
377 }
378 }
379 if ph == nil {
380 return nil, fmt.Errorf("no program header matches file offset %x", fileOffset)
381 }
382 return ph, nil
383 }
384
View as plain text