1 /******************************************************************************
2 * Copyright (c) 2004, 2011 IBM Corporation
3 * All rights reserved.
4 * This program and the accompanying materials
5 * are made available under the terms of the BSD License
6 * which accompanies this distribution, and is available at
7 * http://www.opensource.org/licenses/bsd-license.php
8 *
9 * Contributors:
10 * IBM Corporation - initial implementation
11 *****************************************************************************/
12
13 /*
14 * 64-bit ELF loader for PowerPC.
15 * See the "64-bit PowerPC ELF Application Binary Interface Supplement" and
16 * the "ELF-64 Object File Format" documentation for details.
17 */
18
19 #include <string.h>
20 #include <stdio.h>
21 #include <libelf.h>
22 #include <byteorder.h>
23 #include <helpers.h>
24
25 struct ehdr64
26 {
27 uint32_t ei_ident;
28 uint8_t ei_class;
29 uint8_t ei_data;
30 uint8_t ei_version;
31 uint8_t ei_pad[9];
32 uint16_t e_type;
33 uint16_t e_machine;
34 uint32_t e_version;
35 uint64_t e_entry;
36 uint64_t e_phoff;
37 uint64_t e_shoff;
38 uint32_t e_flags;
39 uint16_t e_ehsize;
40 uint16_t e_phentsize;
41 uint16_t e_phnum;
42 uint16_t e_shentsize;
43 uint16_t e_shnum;
44 uint16_t e_shstrndx;
45 };
46
47 struct phdr64
48 {
49 uint32_t p_type;
50 uint32_t p_flags;
51 uint64_t p_offset;
52 uint64_t p_vaddr;
53 uint64_t p_paddr;
54 uint64_t p_filesz;
55 uint64_t p_memsz;
56 uint64_t p_align;
57 };
58
59 struct shdr64
60 {
61 uint32_t sh_name; /* Section name */
62 uint32_t sh_type; /* Section type */
63 uint64_t sh_flags; /* Section attributes */
64 uint64_t sh_addr; /* Virtual address in memory */
65 uint64_t sh_offset; /* Offset in file */
66 uint64_t sh_size; /* Size of section */
67 uint32_t sh_link; /* Link to other section */
68 uint32_t sh_info; /* Miscellaneous information */
69 uint64_t sh_addralign; /* Address alignment boundary */
70 uint64_t sh_entsize; /* Size of entries, if section has table */
71 };
72
73 struct rela /* RelA relocation table entry */
74 {
75 uint64_t r_offset; /* Address of reference */
76 uint64_t r_info; /* Symbol index and type of relocation */
77 int64_t r_addend; /* Constant part of expression */
78 };
79
80 struct sym64
81 {
82 uint32_t st_name; /* Symbol name */
83 uint8_t st_info; /* Type and Binding attributes */
84 uint8_t st_other; /* Reserved */
85 uint16_t st_shndx; /* Section table index */
86 uint64_t st_value; /* Symbol value */
87 uint64_t st_size; /* Size of object (e.g., common) */
88 };
89
90
91 /* For relocations */
92 #define ELF_R_SYM(i) ((i)>>32)
93 #define ELF_R_TYPE(i) ((uint32_t)(i) & 0xFFFFFFFF)
94 #define ELF_R_INFO(s,t) ((((uint64_t) (s)) << 32) + (t))
95
96 /*
97 * Relocation types for PowerPC64.
98 */
99 #define R_PPC64_NONE 0
100 #define R_PPC64_ADDR32 1
101 #define R_PPC64_ADDR24 2
102 #define R_PPC64_ADDR16 3
103 #define R_PPC64_ADDR16_LO 4
104 #define R_PPC64_ADDR16_HI 5
105 #define R_PPC64_ADDR16_HA 6
106 #define R_PPC64_ADDR14 7
107 #define R_PPC64_ADDR14_BRTAKEN 8
108 #define R_PPC64_ADDR14_BRNTAKEN 9
109 #define R_PPC64_REL24 10
110 #define R_PPC64_REL14 11
111 #define R_PPC64_REL14_BRTAKEN 12
112 #define R_PPC64_REL14_BRNTAKEN 13
113 #define R_PPC64_GOT16 14
114 #define R_PPC64_GOT16_LO 15
115 #define R_PPC64_GOT16_HI 16
116 #define R_PPC64_GOT16_HA 17
117 #define R_PPC64_COPY 19
118 #define R_PPC64_GLOB_DAT 20
119 #define R_PPC64_JMP_SLOT 21
120 #define R_PPC64_RELATIVE 22
121 #define R_PPC64_UADDR32 24
122 #define R_PPC64_UADDR16 25
123 #define R_PPC64_REL32 26
124 #define R_PPC64_PLT32 27
125 #define R_PPC64_PLTREL32 28
126 #define R_PPC64_PLT16_LO 29
127 #define R_PPC64_PLT16_HI 30
128 #define R_PPC64_PLT16_HA 31
129 #define R_PPC64_SECTOFF 33
130 #define R_PPC64_SECTOFF_LO 34
131 #define R_PPC64_SECTOFF_HI 35
132 #define R_PPC64_SECTOFF_HA 36
133 #define R_PPC64_ADDR30 37
134 #define R_PPC64_ADDR64 38
135 #define R_PPC64_ADDR16_HIGHER 39
136 #define R_PPC64_ADDR16_HIGHERA 40
137 #define R_PPC64_ADDR16_HIGHEST 41
138 #define R_PPC64_ADDR16_HIGHESTA 42
139 #define R_PPC64_UADDR64 43
140 #define R_PPC64_REL64 44
141 #define R_PPC64_PLT64 45
142 #define R_PPC64_PLTREL64 46
143 #define R_PPC64_TOC16 47
144 #define R_PPC64_TOC16_LO 48
145 #define R_PPC64_TOC16_HI 49
146 #define R_PPC64_TOC16_HA 50
147 #define R_PPC64_TOC 51
148 #define R_PPC64_PLTGOT16 52
149 #define R_PPC64_PLTGOT16_LO 53
150 #define R_PPC64_PLTGOT16_HI 54
151 #define R_PPC64_PLTGOT16_HA 55
152 #define R_PPC64_ADDR16_DS 56
153 #define R_PPC64_ADDR16_LO_DS 57
154 #define R_PPC64_GOT16_DS 58
155 #define R_PPC64_GOT16_LO_DS 59
156 #define R_PPC64_PLT16_LO_DS 60
157 #define R_PPC64_SECTOFF_DS 61
158 #define R_PPC64_SECTOFF_LO_DS 62
159 #define R_PPC64_TOC16_DS 63
160 #define R_PPC64_TOC16_LO_DS 64
161 #define R_PPC64_PLTGOT16_DS 65
162 #define R_PPC64_PLTGOT16_LO_DS 66
163 #define R_PPC64_TLS 67
164 #define R_PPC64_DTPMOD64 68
165 #define R_PPC64_TPREL16 69
166 #define R_PPC64_TPREL16_LO 60
167 #define R_PPC64_TPREL16_HI 71
168 #define R_PPC64_TPREL16_HA 72
169 #define R_PPC64_TPREL64 73
170 #define R_PPC64_DTPREL16 74
171 #define R_PPC64_DTPREL16_LO 75
172 #define R_PPC64_DTPREL16_HI 76
173 #define R_PPC64_DTPREL16_HA 77
174 #define R_PPC64_DTPREL64 78
175 #define R_PPC64_GOT_TLSGD16 79
176 #define R_PPC64_GOT_TLSGD16_LO 80
177 #define R_PPC64_GOT_TLSGD16_HI 81
178 #define R_PPC64_GOT_TLSGD16_HA 82
179 #define R_PPC64_GOT_TLSLD16 83
180 #define R_PPC64_GOT_TLSLD16_LO 84
181 #define R_PPC64_GOT_TLSLD16_HI 85
182 #define R_PPC64_GOT_TLSLD16_HA 86
183 #define R_PPC64_GOT_TPREL16_DS 87
184 #define R_PPC64_GOT_TPREL16_LO_ DS 88
185 #define R_PPC64_GOT_TPREL16_HI 89
186 #define R_PPC64_GOT_TPREL16_HA 90
187 #define R_PPC64_GOT_DTPREL16_DS 91
188 #define R_PPC64_GOT_DTPREL16_LO_DS 92
189 #define R_PPC64_GOT_DTPREL16_HI 93
190 #define R_PPC64_GOT_DTPREL16_HA 94
191 #define R_PPC64_TPREL16_DS 95
192 #define R_PPC64_TPREL16_LO_DS 96
193 #define R_PPC64_TPREL16_HIGHER 97
194 #define R_PPC64_TPREL16_HIGHERA 98
195 #define R_PPC64_TPREL16_HIGHEST 99
196 #define R_PPC64_TPREL16_HIGHESTA 100
197 #define R_PPC64_DTPREL16_DS 101
198 #define R_PPC64_DTPREL16_LO_DS 102
199 #define R_PPC64_DTPREL16_HIGHER 103
200 #define R_PPC64_DTPREL16_HIGHERA 104
201 #define R_PPC64_DTPREL16_HIGHEST 105
202 #define R_PPC64_DTPREL16_HIGHESTA 106
203
204
205 static struct phdr64*
get_phdr64(unsigned long * file_addr)206 get_phdr64(unsigned long *file_addr)
207 {
208 return (struct phdr64 *) (((unsigned char *) file_addr)
209 + ((struct ehdr64 *)file_addr)->e_phoff);
210 }
211
212 static void
load_segment64(unsigned long * file_addr,struct phdr64 * phdr,signed long offset,int (* pre_load)(void *,long),void (* post_load)(void *,long))213 load_segment64(unsigned long *file_addr, struct phdr64 *phdr, signed long offset,
214 int (*pre_load)(void*, long),
215 void (*post_load)(void*, long))
216 {
217 unsigned long src = phdr->p_offset + (unsigned long) file_addr;
218 unsigned long destaddr;
219
220 destaddr = phdr->p_paddr + offset;
221
222 /* check if we're allowed to copy */
223 if (pre_load != NULL) {
224 if (pre_load((void*)destaddr, phdr->p_memsz) != 0)
225 return;
226 }
227
228 /* copy into storage */
229 memmove((void*)destaddr, (void*)src, phdr->p_filesz);
230
231 /* clear bss */
232 memset((void*)(destaddr + phdr->p_filesz), 0,
233 phdr->p_memsz - phdr->p_filesz);
234
235 if (phdr->p_memsz && post_load != NULL) {
236 post_load((void*)destaddr, phdr->p_memsz);
237 }
238 }
239
240 unsigned long
elf_load_segments64(void * file_addr,signed long offset,int (* pre_load)(void *,long),void (* post_load)(void *,long))241 elf_load_segments64(void *file_addr, signed long offset,
242 int (*pre_load)(void*, long),
243 void (*post_load)(void*, long))
244 {
245 struct ehdr64 *ehdr = (struct ehdr64 *) file_addr;
246 /* Calculate program header address */
247 struct phdr64 *phdr = get_phdr64(file_addr);
248 int i;
249
250 /* loop e_phnum times */
251 for (i = 0; i <= ehdr->e_phnum; i++) {
252 /* PT_LOAD ? */
253 if (phdr->p_type == PT_LOAD) {
254 if (phdr->p_paddr != phdr->p_vaddr) {
255 printf("ELF64: VirtAddr(%lx) != PhysAddr(%lx) not supported, aborting\n",
256 (long)phdr->p_vaddr, (long)phdr->p_paddr);
257 return 0;
258 }
259
260 /* copy segment */
261 load_segment64(file_addr, phdr, offset, pre_load, post_load);
262 }
263 /* step to next header */
264 phdr = (struct phdr64 *)(((uint8_t *)phdr) + ehdr->e_phentsize);
265 }
266
267 /* Entry point is always a virtual address, so translate it
268 * to physical before returning it */
269 return ehdr->e_entry;
270 }
271
272 /**
273 * Return the base address for loading (i.e. the address of the first PT_LOAD
274 * segment)
275 * @param file_addr pointer to the ELF file in memory
276 * @return the base address
277 */
278 long
elf_get_base_addr64(void * file_addr)279 elf_get_base_addr64(void *file_addr)
280 {
281 struct ehdr64 *ehdr = (struct ehdr64 *) file_addr;
282 /* Calculate program header address */
283 struct phdr64 *phdr = get_phdr64(file_addr);
284 int i;
285
286 /* loop e_phnum times */
287 for (i = 0; i <= ehdr->e_phnum; i++) {
288 /* PT_LOAD ? */
289 if (phdr->p_type == PT_LOAD) {
290 /* Return base address */
291 return phdr->p_paddr;
292 }
293 /* step to next header */
294 phdr = (struct phdr64 *)(((uint8_t *)phdr) + ehdr->e_phentsize);
295 }
296
297 return 0;
298 }
299
300
301 /**
302 * Apply one relocation entry.
303 */
304 static void
elf_apply_rela64(void * file_addr,signed long offset,struct rela * relaentry,struct sym64 * symtabentry)305 elf_apply_rela64(void *file_addr, signed long offset, struct rela *relaentry,
306 struct sym64 *symtabentry)
307 {
308 void *addr;
309 unsigned long s_a;
310 unsigned long base_addr;
311
312 base_addr = elf_get_base_addr64(file_addr);
313
314 /* Sanity check */
315 if (relaentry->r_offset < base_addr) {
316 printf("\nELF relocation out of bounds!\n");
317 return;
318 }
319
320 base_addr += offset;
321
322 /* Actual address where the relocation will be applied at. */
323 addr = (void*)(relaentry->r_offset + offset);
324
325 /* Symbol value (S) + Addend (A) */
326 s_a = symtabentry->st_value + offset + relaentry->r_addend;
327
328 switch (ELF_R_TYPE(relaentry->r_info)) {
329 case R_PPC64_ADDR32: /* S + A */
330 *(uint32_t *)addr = (uint32_t) s_a;
331 break;
332 case R_PPC64_ADDR64: /* S + A */
333 *(uint64_t *)addr = (uint64_t) s_a;
334 break;
335 case R_PPC64_TOC: /* .TOC */
336 *(uint64_t *)addr += offset;
337 break;
338 case R_PPC64_ADDR16_HIGHEST: /* #highest(S + A) */
339 *(uint16_t *)addr = ((s_a >> 48) & 0xffff);
340 break;
341 case R_PPC64_ADDR16_HIGHER: /* #higher(S + A) */
342 *(uint16_t *)addr = ((s_a >> 32) & 0xffff);
343 break;
344 case R_PPC64_ADDR16_HI: /* #hi(S + A) */
345 *(uint16_t *)addr = ((s_a >> 16) & 0xffff);
346 break;
347 case R_PPC64_ADDR16_LO: /* #lo(S + A) */
348 *(uint16_t *)addr = s_a & 0xffff;
349 break;
350 case R_PPC64_ADDR16_LO_DS:
351 *(uint16_t *)addr = (s_a & 0xfffc);
352 break;
353 case R_PPC64_ADDR16_HA: /* #ha(S + A) */
354 *(uint16_t *)addr = (((s_a >> 16) + ((s_a & 0x8000) ? 1 : 0))
355 & 0xffff);
356 break;
357
358 case R_PPC64_TOC16: /* half16* S + A - .TOC. */
359 case R_PPC64_TOC16_LO_DS:
360 case R_PPC64_TOC16_LO: /* #lo(S + A - .TOC.) */
361 case R_PPC64_TOC16_HI: /* #hi(S + A - .TOC.) */
362 case R_PPC64_TOC16_HA:
363 case R_PPC64_TOC16_DS: /* (S + A - .TOC) >> 2 */
364 case R_PPC64_REL14:
365 case R_PPC64_REL24: /* (S + A - P) >> 2 */
366 case R_PPC64_REL32: /* S + A - P */
367 case R_PPC64_REL64: /* S + A - P */
368 case R_PPC64_GOT16_DS:
369 case R_PPC64_GOT16_LO_DS:
370 // printf("\t\tignoring relocation type %i\n",
371 // ELF_R_TYPE(relaentry->r_info));
372 break;
373 default:
374 printf("ERROR: Unhandled relocation (A) type %i\n",
375 ELF_R_TYPE(relaentry->r_info));
376 }
377 }
378
379
380 /**
381 * Step through all relocation entries and apply them one by one.
382 */
383 static void
elf_apply_all_rela64(void * file_addr,signed long offset,struct shdr64 * shdrs,int idx)384 elf_apply_all_rela64(void *file_addr, signed long offset, struct shdr64 *shdrs, int idx)
385 {
386 struct shdr64 *rela_shdr = &shdrs[idx];
387 struct shdr64 *dst_shdr = &shdrs[rela_shdr->sh_info];
388 struct shdr64 *sym_shdr = &shdrs[rela_shdr->sh_link];
389 struct rela *relaentry;
390 struct sym64 *symtabentry;
391 uint32_t symbolidx;
392 unsigned i;
393
394 /* If the referenced section has not been allocated, then it has
395 * not been loaded and thus does not need to be relocated. */
396 if ((dst_shdr->sh_flags & SHF_ALLOC) != SHF_ALLOC)
397 return;
398
399 for (i = 0; i < rela_shdr->sh_size; i += rela_shdr->sh_entsize) {
400 relaentry = (struct rela *)(file_addr + rela_shdr->sh_offset + i);
401
402 symbolidx = ELF_R_SYM(relaentry->r_info);
403 symtabentry = (struct sym64*)(file_addr + sym_shdr->sh_offset) + symbolidx;
404
405 elf_apply_rela64(file_addr, offset, relaentry, symtabentry);
406 }
407 }
408
409
410 /**
411 * Apply ELF relocations
412 */
413 void
elf_relocate64(void * file_addr,signed long offset)414 elf_relocate64(void *file_addr, signed long offset)
415 {
416 struct ehdr64 *ehdr = (struct ehdr64 *) file_addr;
417 /* Calculate section header address */
418 struct shdr64 *shdrs = (struct shdr64 *)
419 (((unsigned char *) file_addr) + ehdr->e_shoff);
420 int i;
421
422 /* loop over all segments */
423 for (i = 0; i <= ehdr->e_shnum; i++) {
424 /* Skip if it is not a relocation segment */
425 if (shdrs[i].sh_type == SHT_RELA) {
426 elf_apply_all_rela64(file_addr, offset, shdrs, i);
427 }
428 }
429 }
430
431 void
elf_byteswap_header64(void * file_addr)432 elf_byteswap_header64(void *file_addr)
433 {
434 struct ehdr64 *ehdr = (struct ehdr64 *) file_addr;
435 struct phdr64 *phdr;
436 int i;
437
438 bswap_16p(&ehdr->e_type);
439 bswap_16p(&ehdr->e_machine);
440 bswap_32p(&ehdr->e_version);
441 bswap_64p(&ehdr->e_entry);
442 bswap_64p(&ehdr->e_phoff);
443 bswap_64p(&ehdr->e_shoff);
444 bswap_32p(&ehdr->e_flags);
445 bswap_16p(&ehdr->e_ehsize);
446 bswap_16p(&ehdr->e_phentsize);
447 bswap_16p(&ehdr->e_phnum);
448 bswap_16p(&ehdr->e_shentsize);
449 bswap_16p(&ehdr->e_shnum);
450 bswap_16p(&ehdr->e_shstrndx);
451
452 phdr = get_phdr64(file_addr);
453
454 /* loop e_phnum times */
455 for (i = 0; i <= ehdr->e_phnum; i++) {
456 bswap_32p(&phdr->p_type);
457 bswap_32p(&phdr->p_flags);
458 bswap_64p(&phdr->p_offset);
459 bswap_64p(&phdr->p_vaddr);
460 bswap_64p(&phdr->p_paddr);
461 bswap_64p(&phdr->p_filesz);
462 bswap_64p(&phdr->p_memsz);
463 bswap_64p(&phdr->p_align);
464
465 /* step to next header */
466 phdr = (struct phdr64 *)(((uint8_t *)phdr) + ehdr->e_phentsize);
467 }
468 }
469
elf_get_eflags_64(void * file_addr)470 uint32_t elf_get_eflags_64(void *file_addr)
471 {
472 struct ehdr64 *ehdr = (struct ehdr64 *) file_addr;
473
474 return ehdr->e_flags;
475 }
476
477 /*
478 * Determine the size of an ELF image that has been loaded into
479 * a buffer larger than its size. We search all program headers
480 * and sections for the one that shows the farthest extent of the
481 * file.
482 * @return Return -1 on error, size of file otherwise.
483 */
elf_get_file_size64(const void * buffer,const unsigned long buffer_size)484 long elf_get_file_size64(const void *buffer, const unsigned long buffer_size)
485 {
486 const struct ehdr64 *ehdr = (const struct ehdr64 *) buffer;
487 const uint8_t *buffer_end = buffer + buffer_size;
488 const struct phdr64 *phdr;
489 const struct shdr64 *shdr;
490 unsigned long elf_size = 0;
491 uint16_t entsize;
492 unsigned i;
493
494 if (buffer_size < sizeof(struct ehdr) ||
495 ehdr->e_ehsize != sizeof(struct ehdr64))
496 return -1;
497
498 phdr = buffer + elf64_to_cpu(ehdr->e_phoff, ehdr);
499 entsize = elf16_to_cpu(ehdr->e_phentsize, ehdr);
500 for (i = 0; i < elf16_to_cpu(ehdr->e_phnum, ehdr); i++) {
501 if (((uint8_t *)phdr) + entsize > buffer_end)
502 return -1;
503
504 elf_size = MAX(elf64_to_cpu(phdr->p_offset, ehdr) +
505 elf64_to_cpu(phdr->p_filesz, ehdr),
506 elf_size);
507
508 /* step to next header */
509 phdr = (struct phdr64 *)(((uint8_t *)phdr) + entsize);
510 }
511
512 shdr = buffer + elf64_to_cpu(ehdr->e_shoff, ehdr);
513 entsize = elf16_to_cpu(ehdr->e_shentsize, ehdr);
514 for (i = 0; i < elf16_to_cpu(ehdr->e_shnum, ehdr); i++) {
515 if (((uint8_t *)shdr) + entsize > buffer_end)
516 return -1;
517
518 elf_size = MAX(elf64_to_cpu(shdr->sh_offset, ehdr) +
519 elf64_to_cpu(shdr->sh_size, ehdr),
520 elf_size);
521
522 /* step to next header */
523 shdr = (struct shdr64 *)(((uint8_t *)shdr) + entsize);
524 }
525
526 elf_size = ROUNDUP(elf_size, 4);
527 if (elf_size > buffer_size)
528 return -1;
529
530 return (long) elf_size;
531 }
532