Delete platform directory from sun4i target
[AROS.git] / arch / arm-sun4i / boot / elf.c
blob9cdb8d7bb697e944ff17405e1cb7c8f1aa4f2f17
1 /*
2 Copyright © 2014, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc:
6 Lang: english
7 */
9 #include "elf.h"
10 #include "boot.h"
12 #include <dos/elf.h>
13 #include <stdlib.h>
14 #include <string.h>
16 uint32_t int_shnum;
17 uint32_t int_shstrndx;
19 int checkHeader(struct elfheader *eh)
21 if (eh->ident[0] != 0x7f || eh->ident[1] != 'E' ||
22 eh->ident[2] != 'L' || eh->ident[3] != 'F')
24 return 0;
27 int_shnum = eh->shnum;
28 int_shstrndx = eh->shstrndx;
30 /* the ELF header only uses 16 bits to store the count of section headers,
31 * so it can't handle more than 65535 headers. if the count is 0, and an
32 * offset is defined, then the real count can be found in the first
33 * section header (which always exists).
35 * similarly, if the string table index is SHN_XINDEX, then the actual
36 * index is found in the first section header also.
38 * see the System V ABI 2001-04-24 draft for more details.
40 if (int_shnum == 0 || int_shstrndx == SHN_XINDEX)
42 if (eh->shoff == 0)
44 return 0;
47 /* Get section header. I hope it's there, in memory already. */
48 struct sheader *sh = (struct sheader *)((intptr_t)eh + eh->shoff);
50 /* wider section header count is in the size field */
51 if (int_shnum == 0)
52 int_shnum = sh->size;
54 /* wider string table index is in the link field */
55 if (int_shstrndx == SHN_XINDEX)
56 int_shstrndx = sh->link;
58 /* sanity, if they're still invalid then this isn't elf */
59 if (int_shnum == 0 || int_shstrndx == SHN_XINDEX)
61 return 0;
67 eh->ident[EI_CLASS] != ELFCLASS32 ||
68 eh->ident[EI_VERSION] != EV_CURRENT ||
69 eh->type != ET_REL ||
70 eh->ident[EI_DATA] != ELFDATA2LSB ||
71 eh->machine != EM_ARM
74 return 0;
77 return 1;
80 int getElfSize(void *elf_file, uint32_t *size_rw, uint32_t *size_ro)
82 struct elfheader *eh = (struct elfheader *)elf_file;
83 uint32_t s_ro = 0;
84 uint32_t s_rw = 0;
86 if (checkHeader(eh))
88 struct sheader *sh = (struct sheader *)((intptr_t)elf_file + eh->shoff);
89 int i;
91 for (i = 0; i < int_shnum; i++)
93 /* Does the section require memoy allcation? */
94 if (sh[i].flags & SHF_ALLOC)
96 uint32_t size = (sh[i].size + sh[i].addralign - 1) & ~(sh[i].addralign - 1);
99 * I extend the section size according to the alignment requirement. However, also the already
100 * measured size has to be aligned poperly. It is so, because the loader has to align the load address later on.
102 if (sh[i].flags & SHF_WRITE)
104 s_rw = (s_rw + sh[i].addralign - 1) & ~(sh[i].addralign - 1);
105 s_rw += size;
107 else
109 s_ro = (s_ro + sh[i].addralign - 1) & ~(sh[i].addralign - 1);
110 s_ro += size;
116 if (size_ro)
117 *size_ro = s_ro;
118 if (size_rw)
119 *size_rw = s_rw;
121 return 1;
124 static uintptr_t ptr_ro;
125 static uintptr_t ptr_rw;
126 static uintptr_t virtoffset;
128 void initAllocator(uintptr_t addr_ro, uintptr_t addr_rw, uintptr_t virtoff)
130 ptr_ro = addr_ro;
131 ptr_rw = addr_rw;
132 virtoffset = virtoff;
135 struct bss_tracker tracker[MAX_BSS_SECTIONS];
136 static struct bss_tracker *bss_tracker = &tracker[0];
139 * read_block function copies the range of memory within ELF file to any specified location.
141 static inline void read_block(void *file, long offset, void *dest, long length)
143 memcpy(dest, (void *)((unsigned long)file + offset), length);
147 * Get the memory for chunk and load it
149 static int load_hunk(void *file, struct sheader *sh)
151 void *ptr = (void *)0;
153 /* empty chunk? Who cares :) */
154 if (!sh->size)
155 return 1;
157 /* Allocate a chunk with write access */
158 if (sh->flags & SHF_WRITE)
160 ptr_rw = (char *)(((unsigned long)ptr_rw
161 + (unsigned long)sh->addralign - 1)
162 & ~((unsigned long)sh->addralign - 1));
163 ptr = ptr_rw;
164 ptr_rw = ptr_rw + sh->size;
166 else
168 /* Read-Only mode? Get the memory from the kernel space, align it accorting to the demand */
169 ptr_ro = (char *)(((unsigned long)ptr_ro
170 + (unsigned long)sh->addralign - 1)
171 & ~((unsigned long)sh->addralign - 1));
172 ptr = ptr_ro;
173 ptr_ro = ptr_ro + sh->size;
176 sh->addr = ptr;
178 /* copy block of memory from ELF file if it exists */
179 if (sh->type != SHT_NOBITS) {
180 read_block(file, sh->offset, (void *)((unsigned long)sh->addr), sh->size);
181 } else {
182 bzero(ptr, sh->size);
183 bss_tracker->addr = (void *)((unsigned long)ptr + virtoffset);
184 bss_tracker->length = sh->size;
185 bss_tracker++;
187 * empty the subsequent tracker in case it's the last one. We did that in case a buggy firmare
188 * forgot to clear our .bss section
190 bss_tracker->addr = (void*)0;
191 bss_tracker->length = 0;
194 return 1;
197 /* Perform relocations of given section */
198 static int relocate(struct elfheader *eh, struct sheader *sh, long shrel_idx,
199 uint32_t virt)
201 struct sheader *shrel = &sh[shrel_idx];
202 struct sheader *shsymtab = &sh[shrel->link];
203 struct sheader *toreloc = &sh[shrel->info];
205 struct symbol *symtab =
206 (struct symbol *)((unsigned long)shsymtab->addr);
207 struct relo *rel = (struct relo *)((unsigned long)shrel->addr);
208 char *section = (char *)((unsigned long)toreloc->addr);
210 unsigned int numrel = (unsigned long)shrel->size
211 / (unsigned long)shrel->entsize;
212 unsigned int i;
214 uint32_t virtoffset;
216 struct symbol *SysBase_sym = (void *)0;
218 for (i = 0; i < numrel; i++, rel++)
220 struct symbol *sym = &symtab[ELF32_R_SYM(rel->info)];
221 uint32_t *p = (uint32_t *) & section[rel->offset];
222 uint32_t s;
223 virtoffset = virt;
226 * R_ARM_V4BX are actually special marks for the linker.
227 * They even never have a target (shindex == SHN_UNDEF),
228 * so we simply ignore them before doing any checks.
230 if (ELF_R_TYPE(rel->info) == R_ARM_V4BX)
231 continue;
233 switch (sym->shindex)
235 case SHN_UNDEF:
236 kprintf
237 ("[ELF Loader] Undefined symbol '%s' in section '%s'\n",
238 (char *)((uint32_t) sh[shsymtab->link].addr) +
239 sym->name,
240 (char *)((uint32_t) sh[eh->shstrndx].addr) +
241 toreloc->name);
242 return 0;
244 case SHN_COMMON:
245 kprintf
246 ("[ELF Loader] COMMON symbol '%s' in section '%s'\n",
247 (char *)((uint32_t) sh[shsymtab->link].addr) +
248 sym->name,
249 (char *)((uint32_t) sh[eh->shstrndx].addr) +
250 toreloc->name);
252 return 0;
254 case SHN_ABS:
255 if (SysBase_sym == (void *)0) {
256 if (strncmp
257 ((char *)((uint32_t) sh[shsymtab->link].
258 addr) + sym->name, "SysBase",
259 8) == 0) {
260 SysBase_sym = sym;
261 goto SysBase_yes;
262 } else
263 goto SysBase_no;
264 } else if (SysBase_sym == sym) {
265 SysBase_yes: s = (uint32_t) 4UL;
266 virtoffset = 0;
267 } else
268 SysBase_no: s = sym->value;
269 break;
270 default:
271 s = (uint32_t) sh[sym->shindex].addr + sym->value;
273 switch (ELF32_R_TYPE(rel->info)) {
275 // case R_386_32: /* 32bit direct/absolute */
276 // *p += s + virtoffset;
277 // break;
279 case R_ARM_CALL:
280 case R_ARM_JUMP24:
281 case R_ARM_PC24:
283 /* On ARM the 24 bit offset is shifted by 2 to the right */
284 signed long offset = (*p & 0x00ffffff) << 2;
285 /* If highest bit set, make offset negative */
286 if (offset & 0x02000000)
287 offset -= 0x04000000;
289 offset += s - (ULONG)p;
291 offset >>= 2;
292 *p &= 0xff000000;
293 *p |= offset & 0x00ffffff;
295 break;
298 case R_ARM_MOVW_ABS_NC:
299 case R_ARM_MOVT_ABS:
301 signed long offset = *p;
302 offset = ((offset & 0xf0000) >> 4) | (offset & 0xfff);
303 offset = (offset ^ 0x8000) - 0x8000;
305 offset += s + virtoffset;
307 if (ELF_R_TYPE(rel->info) == R_ARM_MOVT_ABS)
308 offset >>= 16;
310 *p &= 0xfff0f000;
311 *p |= ((offset & 0xf000) << 4) | (offset & 0x0fff);
313 break;
315 case R_ARM_ABS32: /* PC relative 32 bit signed */
316 *p += s + virtoffset;
317 break;
319 case R_ARM_NONE: /* No reloc */
320 break;
322 default:
323 kprintf("[BOOT] Unknown relocation %d in ELF file\n",
324 ELF32_R_TYPE(rel->info));
325 return 0;
328 return 1;
332 int loadElf(void *elf_file)
334 struct elfheader *eh = (struct elfheader *)elf_file;
335 uint32_t s_ro = 0;
336 uint32_t s_rw = 0;
338 kprintf("[BOOT] loadElf(%p)\n", eh);
340 if (checkHeader(eh))
342 struct sheader *sh = (struct sheader *)((intptr_t)elf_file + eh->shoff);
343 int i;
345 for (i = 0; i < int_shnum; i++)
347 /* Load the symbol and string tables */
348 if (sh[i].type == SHT_SYMTAB || sh[i].type == SHT_STRTAB)
350 sh[i].addr = (unsigned long)elf_file + sh[i].offset;
352 /* Does the section require memoy allcation? */
353 else if (sh[i].flags & SHF_ALLOC)
355 /* Yup, it does. Load the hunk */
356 if (!load_hunk(elf_file, &sh[i])) {
357 return 0;
358 } else {
359 if (sh[i].size) {
360 kprintf("[BOOT] ELF: %s section loaded at %p (Virtual addr: %p-%p)\n",
361 sh[i].flags & SHF_WRITE ? "RW":"RO",
362 sh[i].addr,
363 sh[i].addr + virtoffset,
364 sh[i].addr + sh[i].size + virtoffset);
370 /* For every loaded section perform the relocations */
371 for (i = 0; i < int_shnum; i++)
373 if (sh[i].type == SHT_REL && sh[sh[i].info].addr)
375 sh[i].addr = (uint32_t) elf_file + sh[i].offset;
376 if (!sh[i].addr || !relocate(eh, sh, i, virtoffset))
378 return 0;