1 /******************************************************************************
2 * Copyright (c) 2004, 2011 IBM Corporation
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
10 * IBM Corporation - initial implementation
11 *****************************************************************************/
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.
22 #include <byteorder.h>
60 uint32_t sh_name; /* Section name */
61 uint32_t sh_type; /* Section type */
62 uint64_t sh_flags; /* Section attributes */
63 uint64_t sh_addr; /* Virtual address in memory */
64 uint64_t sh_offset; /* Offset in file */
65 uint64_t sh_size; /* Size of section */
66 uint32_t sh_link; /* Link to other section */
67 uint32_t sh_info; /* Miscellaneous information */
68 uint64_t sh_addralign; /* Address alignment boundary */
69 uint64_t sh_entsize; /* Size of entries, if section has table */
72 struct rela /* RelA relocation table entry */
74 uint64_t r_offset; /* Address of reference */
75 uint64_t r_info; /* Symbol index and type of relocation */
76 int64_t r_addend; /* Constant part of expression */
81 uint32_t st_name; /* Symbol name */
82 uint8_t st_info; /* Type and Binding attributes */
83 uint8_t st_other; /* Reserved */
84 uint16_t st_shndx; /* Section table index */
85 uint64_t st_value; /* Symbol value */
86 uint64_t st_size; /* Size of object (e.g., common) */
91 #define ELF_R_SYM(i) ((i)>>32)
92 #define ELF_R_TYPE(i) ((uint32_t)(i) & 0xFFFFFFFF)
93 #define ELF_R_INFO(s,t) ((((uint64_t) (s)) << 32) + (t))
96 * Relocation types for PowerPC64.
98 #define R_PPC64_NONE 0
99 #define R_PPC64_ADDR32 1
100 #define R_PPC64_ADDR24 2
101 #define R_PPC64_ADDR16 3
102 #define R_PPC64_ADDR16_LO 4
103 #define R_PPC64_ADDR16_HI 5
104 #define R_PPC64_ADDR16_HA 6
105 #define R_PPC64_ADDR14 7
106 #define R_PPC64_ADDR14_BRTAKEN 8
107 #define R_PPC64_ADDR14_BRNTAKEN 9
108 #define R_PPC64_REL24 10
109 #define R_PPC64_REL14 11
110 #define R_PPC64_REL14_BRTAKEN 12
111 #define R_PPC64_REL14_BRNTAKEN 13
112 #define R_PPC64_GOT16 14
113 #define R_PPC64_GOT16_LO 15
114 #define R_PPC64_GOT16_HI 16
115 #define R_PPC64_GOT16_HA 17
116 #define R_PPC64_COPY 19
117 #define R_PPC64_GLOB_DAT 20
118 #define R_PPC64_JMP_SLOT 21
119 #define R_PPC64_RELATIVE 22
120 #define R_PPC64_UADDR32 24
121 #define R_PPC64_UADDR16 25
122 #define R_PPC64_REL32 26
123 #define R_PPC64_PLT32 27
124 #define R_PPC64_PLTREL32 28
125 #define R_PPC64_PLT16_LO 29
126 #define R_PPC64_PLT16_HI 30
127 #define R_PPC64_PLT16_HA 31
128 #define R_PPC64_SECTOFF 33
129 #define R_PPC64_SECTOFF_LO 34
130 #define R_PPC64_SECTOFF_HI 35
131 #define R_PPC64_SECTOFF_HA 36
132 #define R_PPC64_ADDR30 37
133 #define R_PPC64_ADDR64 38
134 #define R_PPC64_ADDR16_HIGHER 39
135 #define R_PPC64_ADDR16_HIGHERA 40
136 #define R_PPC64_ADDR16_HIGHEST 41
137 #define R_PPC64_ADDR16_HIGHESTA 42
138 #define R_PPC64_UADDR64 43
139 #define R_PPC64_REL64 44
140 #define R_PPC64_PLT64 45
141 #define R_PPC64_PLTREL64 46
142 #define R_PPC64_TOC16 47
143 #define R_PPC64_TOC16_LO 48
144 #define R_PPC64_TOC16_HI 49
145 #define R_PPC64_TOC16_HA 50
146 #define R_PPC64_TOC 51
147 #define R_PPC64_PLTGOT16 52
148 #define R_PPC64_PLTGOT16_LO 53
149 #define R_PPC64_PLTGOT16_HI 54
150 #define R_PPC64_PLTGOT16_HA 55
151 #define R_PPC64_ADDR16_DS 56
152 #define R_PPC64_ADDR16_LO_DS 57
153 #define R_PPC64_GOT16_DS 58
154 #define R_PPC64_GOT16_LO_DS 59
155 #define R_PPC64_PLT16_LO_DS 60
156 #define R_PPC64_SECTOFF_DS 61
157 #define R_PPC64_SECTOFF_LO_DS 62
158 #define R_PPC64_TOC16_DS 63
159 #define R_PPC64_TOC16_LO_DS 64
160 #define R_PPC64_PLTGOT16_DS 65
161 #define R_PPC64_PLTGOT16_LO_DS 66
162 #define R_PPC64_TLS 67
163 #define R_PPC64_DTPMOD64 68
164 #define R_PPC64_TPREL16 69
165 #define R_PPC64_TPREL16_LO 60
166 #define R_PPC64_TPREL16_HI 71
167 #define R_PPC64_TPREL16_HA 72
168 #define R_PPC64_TPREL64 73
169 #define R_PPC64_DTPREL16 74
170 #define R_PPC64_DTPREL16_LO 75
171 #define R_PPC64_DTPREL16_HI 76
172 #define R_PPC64_DTPREL16_HA 77
173 #define R_PPC64_DTPREL64 78
174 #define R_PPC64_GOT_TLSGD16 79
175 #define R_PPC64_GOT_TLSGD16_LO 80
176 #define R_PPC64_GOT_TLSGD16_HI 81
177 #define R_PPC64_GOT_TLSGD16_HA 82
178 #define R_PPC64_GOT_TLSLD16 83
179 #define R_PPC64_GOT_TLSLD16_LO 84
180 #define R_PPC64_GOT_TLSLD16_HI 85
181 #define R_PPC64_GOT_TLSLD16_HA 86
182 #define R_PPC64_GOT_TPREL16_DS 87
183 #define R_PPC64_GOT_TPREL16_LO_ DS 88
184 #define R_PPC64_GOT_TPREL16_HI 89
185 #define R_PPC64_GOT_TPREL16_HA 90
186 #define R_PPC64_GOT_DTPREL16_DS 91
187 #define R_PPC64_GOT_DTPREL16_LO_DS 92
188 #define R_PPC64_GOT_DTPREL16_HI 93
189 #define R_PPC64_GOT_DTPREL16_HA 94
190 #define R_PPC64_TPREL16_DS 95
191 #define R_PPC64_TPREL16_LO_DS 96
192 #define R_PPC64_TPREL16_HIGHER 97
193 #define R_PPC64_TPREL16_HIGHERA 98
194 #define R_PPC64_TPREL16_HIGHEST 99
195 #define R_PPC64_TPREL16_HIGHESTA 100
196 #define R_PPC64_DTPREL16_DS 101
197 #define R_PPC64_DTPREL16_LO_DS 102
198 #define R_PPC64_DTPREL16_HIGHER 103
199 #define R_PPC64_DTPREL16_HIGHERA 104
200 #define R_PPC64_DTPREL16_HIGHEST 105
201 #define R_PPC64_DTPREL16_HIGHESTA 106
204 static struct phdr64*
205 get_phdr64(unsigned long *file_addr)
207 return (struct phdr64 *) (((unsigned char *) file_addr)
208 + ((struct ehdr64 *)file_addr)->e_phoff);
212 load_segment64(unsigned long *file_addr, struct phdr64 *phdr, signed long offset,
213 int (*pre_load)(void*, long),
214 void (*post_load)(void*, long))
216 unsigned long src = phdr->p_offset + (unsigned long) file_addr;
217 unsigned long destaddr;
219 destaddr = phdr->p_paddr + offset;
221 /* check if we're allowed to copy */
222 if (pre_load != NULL) {
223 if (pre_load((void*)destaddr, phdr->p_memsz) != 0)
227 /* copy into storage */
228 memmove((void*)destaddr, (void*)src, phdr->p_filesz);
231 memset((void*)(destaddr + phdr->p_filesz), 0,
232 phdr->p_memsz - phdr->p_filesz);
234 if (phdr->p_memsz && post_load != NULL) {
235 post_load((void*)destaddr, phdr->p_memsz);
240 elf_load_segments64(void *file_addr, signed long offset,
241 int (*pre_load)(void*, long),
242 void (*post_load)(void*, long))
244 struct ehdr64 *ehdr = (struct ehdr64 *) file_addr;
245 /* Calculate program header address */
246 struct phdr64 *phdr = get_phdr64(file_addr);
249 /* loop e_phnum times */
250 for (i = 0; i <= ehdr->e_phnum; i++) {
252 if (phdr->p_type == PT_LOAD) {
253 if (phdr->p_paddr != phdr->p_vaddr) {
254 printf("ELF64: VirtAddr(%lx) != PhysAddr(%lx) not supported, aborting\n",
255 (long)phdr->p_vaddr, (long)phdr->p_paddr);
260 load_segment64(file_addr, phdr, offset, pre_load, post_load);
262 /* step to next header */
263 phdr = (struct phdr64 *)(((uint8_t *)phdr) + ehdr->e_phentsize);
266 /* Entry point is always a virtual address, so translate it
267 * to physical before returning it */
268 return ehdr->e_entry;
272 * Return the base address for loading (i.e. the address of the first PT_LOAD
274 * @param file_addr pointer to the ELF file in memory
275 * @return the base address
278 elf_get_base_addr64(void *file_addr)
280 struct ehdr64 *ehdr = (struct ehdr64 *) file_addr;
281 /* Calculate program header address */
282 struct phdr64 *phdr = get_phdr64(file_addr);
285 /* loop e_phnum times */
286 for (i = 0; i <= ehdr->e_phnum; i++) {
288 if (phdr->p_type == PT_LOAD) {
289 /* Return base address */
290 return phdr->p_paddr;
292 /* step to next header */
293 phdr = (struct phdr64 *)(((uint8_t *)phdr) + ehdr->e_phentsize);
301 * Apply one relocation entry.
304 elf_apply_rela64(void *file_addr, signed long offset, struct rela *relaentry,
305 struct sym64 *symtabentry)
309 unsigned long base_addr;
311 base_addr = elf_get_base_addr64(file_addr);
314 if (relaentry->r_offset < base_addr) {
315 printf("\nELF relocation out of bounds!\n");
321 /* Actual address where the relocation will be applied at. */
322 addr = (void*)(relaentry->r_offset + offset);
324 /* Symbol value (S) + Addend (A) */
325 s_a = symtabentry->st_value + offset + relaentry->r_addend;
327 switch (ELF_R_TYPE(relaentry->r_info)) {
328 case R_PPC64_ADDR32: /* S + A */
329 *(uint32_t *)addr = (uint32_t) s_a;
331 case R_PPC64_ADDR64: /* S + A */
332 *(uint64_t *)addr = (uint64_t) s_a;
334 case R_PPC64_TOC: /* .TOC */
335 *(uint64_t *)addr += offset;
337 case R_PPC64_ADDR16_HIGHEST: /* #highest(S + A) */
338 *(uint16_t *)addr = ((s_a >> 48) & 0xffff);
340 case R_PPC64_ADDR16_HIGHER: /* #higher(S + A) */
341 *(uint16_t *)addr = ((s_a >> 32) & 0xffff);
343 case R_PPC64_ADDR16_HI: /* #hi(S + A) */
344 *(uint16_t *)addr = ((s_a >> 16) & 0xffff);
346 case R_PPC64_ADDR16_LO: /* #lo(S + A) */
347 *(uint16_t *)addr = s_a & 0xffff;
349 case R_PPC64_ADDR16_LO_DS:
350 *(uint16_t *)addr = (s_a & 0xfffc);
352 case R_PPC64_ADDR16_HA: /* #ha(S + A) */
353 *(uint16_t *)addr = (((s_a >> 16) + ((s_a & 0x8000) ? 1 : 0))
357 case R_PPC64_TOC16: /* half16* S + A - .TOC. */
358 case R_PPC64_TOC16_LO_DS:
359 case R_PPC64_TOC16_LO: /* #lo(S + A - .TOC.) */
360 case R_PPC64_TOC16_HI: /* #hi(S + A - .TOC.) */
361 case R_PPC64_TOC16_HA:
362 case R_PPC64_TOC16_DS: /* (S + A - .TOC) >> 2 */
364 case R_PPC64_REL24: /* (S + A - P) >> 2 */
365 case R_PPC64_REL64: /* S + A - P */
366 case R_PPC64_GOT16_DS:
367 case R_PPC64_GOT16_LO_DS:
368 // printf("\t\tignoring relocation type %i\n",
369 // ELF_R_TYPE(relaentry->r_info));
372 printf("ERROR: Unhandled relocation (A) type %i\n",
373 ELF_R_TYPE(relaentry->r_info));
379 * Step through all relocation entries and apply them one by one.
382 elf_apply_all_rela64(void *file_addr, signed long offset, struct shdr64 *shdrs, int idx)
384 struct shdr64 *rela_shdr = &shdrs[idx];
385 struct shdr64 *dst_shdr = &shdrs[rela_shdr->sh_info];
386 struct shdr64 *sym_shdr = &shdrs[rela_shdr->sh_link];
387 struct rela *relaentry;
388 struct sym64 *symtabentry;
392 /* If the referenced section has not been allocated, then it has
393 * not been loaded and thus does not need to be relocated. */
394 if ((dst_shdr->sh_flags & SHF_ALLOC) != SHF_ALLOC)
397 for (i = 0; i < rela_shdr->sh_size; i += rela_shdr->sh_entsize) {
398 relaentry = (struct rela *)(file_addr + rela_shdr->sh_offset + i);
400 symbolidx = ELF_R_SYM(relaentry->r_info);
401 symtabentry = (struct sym64*)(file_addr + sym_shdr->sh_offset) + symbolidx;
403 elf_apply_rela64(file_addr, offset, relaentry, symtabentry);
409 * Apply ELF relocations
412 elf_relocate64(void *file_addr, signed long offset)
414 struct ehdr64 *ehdr = (struct ehdr64 *) file_addr;
415 /* Calculate section header address */
416 struct shdr64 *shdrs = (struct shdr64 *)
417 (((unsigned char *) file_addr) + ehdr->e_shoff);
420 /* loop over all segments */
421 for (i = 0; i <= ehdr->e_shnum; i++) {
422 /* Skip if it is not a relocation segment */
423 if (shdrs[i].sh_type == SHT_RELA) {
424 elf_apply_all_rela64(file_addr, offset, shdrs, i);
430 elf_byteswap_header64(void *file_addr)
432 struct ehdr64 *ehdr = (struct ehdr64 *) file_addr;
436 bswap_16p(&ehdr->e_type);
437 bswap_16p(&ehdr->e_machine);
438 bswap_32p(&ehdr->e_version);
439 bswap_64p(&ehdr->e_entry);
440 bswap_64p(&ehdr->e_phoff);
441 bswap_64p(&ehdr->e_shoff);
442 bswap_32p(&ehdr->e_flags);
443 bswap_16p(&ehdr->e_ehsize);
444 bswap_16p(&ehdr->e_phentsize);
445 bswap_16p(&ehdr->e_phnum);
446 bswap_16p(&ehdr->e_shentsize);
447 bswap_16p(&ehdr->e_shnum);
448 bswap_16p(&ehdr->e_shstrndx);
450 phdr = get_phdr64(file_addr);
452 /* loop e_phnum times */
453 for (i = 0; i <= ehdr->e_phnum; i++) {
454 bswap_32p(&phdr->p_type);
455 bswap_32p(&phdr->p_flags);
456 bswap_64p(&phdr->p_offset);
457 bswap_64p(&phdr->p_vaddr);
458 bswap_64p(&phdr->p_paddr);
459 bswap_64p(&phdr->p_filesz);
460 bswap_64p(&phdr->p_memsz);
461 bswap_64p(&phdr->p_align);
463 /* step to next header */
464 phdr = (struct phdr64 *)(((uint8_t *)phdr) + ehdr->e_phentsize);
468 uint32_t elf_get_eflags_64(void *file_addr)
470 struct ehdr64 *ehdr = (struct ehdr64 *) file_addr;
472 return ehdr->e_flags;