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(f *elf.File) ([]byte, error) {
122 findBuildID := func(notes []elfNote) ([]byte, error) {
123 var buildID []byte
124 for _, note := range notes {
125 if note.Name == "GNU" && note.Type == noteTypeGNUBuildID {
126 if buildID == nil {
127 buildID = note.Desc
128 } else {
129 return nil, fmt.Errorf("multiple build ids found, don't know which to use")
130 }
131 }
132 }
133 return buildID, nil
134 }
135
136 for _, p := range f.Progs {
137 if p.Type != elf.PT_NOTE {
138 continue
139 }
140 notes, err := parseNotes(p.Open(), int(p.Align), f.ByteOrder)
141 if err != nil {
142 return nil, err
143 }
144 if b, err := findBuildID(notes); b != nil || err != nil {
145 return b, err
146 }
147 }
148 for _, s := range f.Sections {
149 if s.Type != elf.SHT_NOTE {
150 continue
151 }
152 notes, err := parseNotes(s.Open(), int(s.Addralign), f.ByteOrder)
153 if err != nil {
154 return nil, err
155 }
156 if b, err := findBuildID(notes); b != nil || err != nil {
157 return b, err
158 }
159 }
160 return nil, nil
161 }
162
163
164
165
166
167 func kernelBase(loadSegment *elf.ProgHeader, stextOffset *uint64, start, limit, offset uint64) (uint64, bool) {
168 const (
169
170 pageOffsetPpc64 = 0xc000000000000000
171 pageSize = 4096
172 )
173
174 if loadSegment.Vaddr == start-offset {
175 return offset, true
176 }
177 if start == 0 && limit != 0 && stextOffset != nil {
178
179
180
181
182 return start - *stextOffset, true
183 }
184 if start >= 0x8000000000000000 && limit > start && (offset == 0 || offset == pageOffsetPpc64 || offset == start) {
185
186
187
188
189
190
191
192 if stextOffset != nil && (start%pageSize) == (*stextOffset%pageSize) {
193
194
195
196 return start - *stextOffset, true
197 }
198
199 return start - loadSegment.Vaddr, true
200 }
201 if start%pageSize != 0 && stextOffset != nil && *stextOffset%pageSize == start%pageSize {
202
203
204
205
206
207 return start - *stextOffset, true
208 }
209 return 0, false
210 }
211
212
213
214
215
216 func GetBase(fh *elf.FileHeader, loadSegment *elf.ProgHeader, stextOffset *uint64, start, limit, offset uint64) (uint64, error) {
217
218 if start == 0 && offset == 0 && (limit == ^uint64(0) || limit == 0) {
219
220
221
222 return 0, nil
223 }
224
225 switch fh.Type {
226 case elf.ET_EXEC:
227 if loadSegment == nil {
228
229 return 0, nil
230 }
231 if stextOffset == nil && start > 0 && start < 0x8000000000000000 {
232
233
234
235
236
237
238
239
240 return start - offset + loadSegment.Off - loadSegment.Vaddr, nil
241 }
242
243 if base, match := kernelBase(loadSegment, stextOffset, start, limit, offset); match {
244 return base, nil
245 }
246
247
248
249 if start == 0 && limit != 0 && stextOffset == nil {
250 return start - loadSegment.Vaddr, nil
251 }
252
253 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)
254 case elf.ET_REL:
255 if offset != 0 {
256 return 0, fmt.Errorf("don't know how to handle mapping.Offset")
257 }
258 return start, nil
259 case elf.ET_DYN:
260
261
262
263
264 if loadSegment == nil {
265 return start - offset, nil
266 }
267
268
269 if base, match := kernelBase(loadSegment, stextOffset, start, limit, offset); match {
270 return base, nil
271 }
272
273
274
275
276
277
278
279
280 return start - offset + loadSegment.Off - loadSegment.Vaddr, nil
281 }
282 return 0, fmt.Errorf("don't know how to handle FileHeader.Type %v", fh.Type)
283 }
284
285
286
287 func FindTextProgHeader(f *elf.File) *elf.ProgHeader {
288 for _, s := range f.Sections {
289 if s.Name == ".text" {
290
291 for _, p := range f.Progs {
292 if p.Type == elf.PT_LOAD && p.Flags&elf.PF_X != 0 && s.Addr >= p.Vaddr && s.Addr < p.Vaddr+p.Memsz {
293 return &p.ProgHeader
294 }
295 }
296 }
297 }
298 return nil
299 }
300
301
302
303
304
305
306
307
308
309
310 func ProgramHeadersForMapping(phdrs []elf.ProgHeader, mapOff, mapSz uint64) []*elf.ProgHeader {
311 const (
312
313
314
315
316
317
318
319 pageSize = 4096
320 pageOffsetMask = pageSize - 1
321 )
322 mapLimit := mapOff + mapSz
323 var headers []*elf.ProgHeader
324 for i := range phdrs {
325 p := &phdrs[i]
326
327
328 if p.Filesz == 0 {
329 continue
330 }
331 segLimit := p.Off + p.Memsz
332
333 if p.Type == elf.PT_LOAD && mapOff < segLimit && p.Off < mapLimit {
334
335
336
337 alignedSegOffset := uint64(0)
338 if p.Off > (p.Vaddr & pageOffsetMask) {
339 alignedSegOffset = p.Off - (p.Vaddr & pageOffsetMask)
340 }
341 if mapOff < alignedSegOffset {
342 continue
343 }
344
345
346
347 if mapOff > p.Off && (segLimit < mapOff+pageSize) && (mapLimit >= segLimit+pageSize) {
348 continue
349 }
350 headers = append(headers, p)
351 }
352 }
353 return headers
354 }
355
356
357
358
359 func HeaderForFileOffset(headers []*elf.ProgHeader, fileOffset uint64) (*elf.ProgHeader, error) {
360 var ph *elf.ProgHeader
361 for _, h := range headers {
362 if fileOffset >= h.Off && fileOffset < h.Off+h.Memsz {
363 if ph != nil {
364
365
366
367
368
369 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)
370 }
371 ph = h
372 }
373 }
374 if ph == nil {
375 return nil, fmt.Errorf("no program header matches file offset %x", fileOffset)
376 }
377 return ph, nil
378 }
379
View as plain text