temporary changed in ELF loader for ARM AROS. More changes follow shortly (change...
[AROS.git] / arch / arm-raspi / boot / elf.c
blobc1270f407052d9eede7e534a6efc734814b990f6
1 /*
2 Copyright © 2013-2019, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: elf.c
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 #define DELF(x) /* x */
18 uint32_t int_shnum;
19 uint32_t int_shstrndx;
21 int checkHeader(struct elfheader *eh)
23 if (eh->ident[0] != 0x7f || eh->ident[1] != 'E' ||
24 eh->ident[2] != 'L' || eh->ident[3] != 'F')
26 return 0;
29 int_shnum = eh->shnum;
30 int_shstrndx = eh->shstrndx;
32 /* the ELF header only uses 16 bits to store the count of section headers,
33 * so it can't handle more than 65535 headers. if the count is 0, and an
34 * offset is defined, then the real count can be found in the first
35 * section header (which always exists).
37 * similarly, if the string table index is SHN_XINDEX, then the actual
38 * index is found in the first section header also.
40 * see the System V ABI 2001-04-24 draft for more details.
42 if (int_shnum == 0 || int_shstrndx == SHN_XINDEX)
44 if (eh->shoff == 0)
46 return 0;
49 /* Get section header. I hope it's there, in memory already. */
50 struct sheader *sh = (struct sheader *)((intptr_t)eh + eh->shoff);
52 /* wider section header count is in the size field */
53 if (int_shnum == 0)
54 int_shnum = sh->size;
56 /* wider string table index is in the link field */
57 if (int_shstrndx == SHN_XINDEX)
58 int_shstrndx = sh->link;
60 /* sanity, if they're still invalid then this isn't elf */
61 if (int_shnum == 0 || int_shstrndx == SHN_XINDEX)
63 return 0;
69 eh->ident[EI_CLASS] != ELFCLASS32 ||
70 eh->ident[EI_VERSION] != EV_CURRENT ||
71 !(eh->type == ET_REL || eh->type == ET_EXEC) ||
72 #if AROS_BIG_ENDIAN
73 eh->ident[EI_DATA] != ELFDATA2MSB ||
74 #else
75 eh->ident[EI_DATA] != ELFDATA2LSB ||
76 #endif
77 eh->machine != EM_ARM
80 return 0;
83 return 1;
86 int getElfSize(void *elf_file, uint32_t *size_rw, uint32_t *size_ro)
88 struct elfheader *eh = (struct elfheader *)elf_file;
89 uint32_t s_ro = 0;
90 uint32_t s_rw = 0;
92 DELF(kprintf("[BOOT:ELF] getElfSize(%p)", eh));
94 if (checkHeader(eh))
96 struct sheader *sh = (struct sheader *)((intptr_t)elf_file + eh->shoff);
97 int i;
99 for (i = 0; i < int_shnum; i++)
101 /* Does the section require memoy allcation? */
102 if (sh[i].flags & SHF_ALLOC)
104 uint32_t size = (sh[i].size + sh[i].addralign - 1) & ~(sh[i].addralign - 1);
107 * I extend the section size according to the alignment requirement. However, also the already
108 * measured size has to be aligned poperly. It is so, because the loader has to align the load address later on.
110 if (sh[i].flags & SHF_WRITE)
112 s_rw = (s_rw + sh[i].addralign - 1) & ~(sh[i].addralign - 1);
113 s_rw += size;
115 else
117 s_ro = (s_ro + sh[i].addralign - 1) & ~(sh[i].addralign - 1);
118 s_ro += size;
123 DELF(kprintf(": ro=%p, rw=%p\n", s_ro, s_rw));
125 if (size_ro)
126 *size_ro = s_ro;
127 if (size_rw)
128 *size_rw = s_rw;
130 return 1;
133 static uintptr_t ptr_ro;
134 static uintptr_t ptr_rw;
135 static uintptr_t virtoffset;
137 void initAllocator(uintptr_t addr_ro, uintptr_t addr_rw, uintptr_t virtoff)
139 ptr_ro = addr_ro;
140 ptr_rw = addr_rw;
141 virtoffset = virtoff;
144 struct bss_tracker tracker[MAX_BSS_SECTIONS];
145 static struct bss_tracker *bss_tracker = &tracker[0];
148 * read_block function copies the range of memory within ELF file to any specified location.
150 static inline void read_block(void *file, long offset, void *dest, long length)
152 memcpy(dest, (void *)((unsigned long)file + offset), length);
156 * Get the memory for chunk and load it
158 static int load_hunk(void *file, struct sheader *sh)
160 void *ptr = (void *)0;
162 /* empty chunk? Who cares :) */
163 if (!sh->size)
164 return 1;
166 /* Allocate a chunk with write access */
167 if (sh->flags & SHF_WRITE)
169 ptr_rw = (((unsigned long)ptr_rw
170 + (unsigned long)sh->addralign - 1)
171 & ~((unsigned long)sh->addralign - 1));
172 ptr = (APTR)ptr_rw;
173 ptr_rw = ptr_rw + sh->size;
175 else
177 /* Read-Only mode? Get the memory from the kernel space, align it accorting to the demand */
178 ptr_ro = (((unsigned long)ptr_ro
179 + (unsigned long)sh->addralign - 1)
180 & ~((unsigned long)sh->addralign - 1));
181 ptr = (APTR)ptr_ro;
182 ptr_ro = ptr_ro + sh->size;
185 sh->addr = ptr;
187 /* copy block of memory from ELF file if it exists */
188 if (sh->type != SHT_NOBITS)
190 read_block(file, sh->offset, (void *)((unsigned long)sh->addr),
191 sh->size);
193 else
195 bzero(ptr, sh->size);
196 bss_tracker->addr =
197 (void *)((unsigned long)ptr + virtoffset);
198 bss_tracker->length = sh->size;
199 bss_tracker++;
201 * empty the subsequent tracker in case it's the last one. We did that in case a buggy firmare
202 * forgot to clear our .bss section
204 bss_tracker->addr = (void*)0;
205 bss_tracker->length = 0;
208 return 1;
211 /* Perform relocations of given section */
212 static int relocate(struct elfheader *eh, struct sheader *sh, long shrel_idx,
213 uint32_t virt, uintptr_t *deltas)
215 struct sheader *shrel = &sh[shrel_idx];
216 struct sheader *shsymtab = &sh[shrel->link];
217 struct sheader *toreloc = &sh[shrel->info];
218 uintptr_t orig_addr = deltas[shrel->info];
219 int is_exec = (eh->type == ET_EXEC);
221 struct symbol *symtab =
222 (struct symbol *)((unsigned long)shsymtab->addr);
223 struct relo *rel = (struct relo *)((unsigned long)shrel->addr);
224 char *section = (char *)((unsigned long)toreloc->addr);
226 unsigned int numrel = (unsigned long)shrel->size
227 / (unsigned long)shrel->entsize;
228 unsigned int i;
230 uint32_t virtoffset;
232 struct symbol *SysBase_sym = (void *)0;
234 for (i = 0; i < numrel; i++, rel++)
236 struct symbol *sym = &symtab[ELF32_R_SYM(rel->info)];
237 uint32_t *p = (uint32_t *) & section[rel->offset - orig_addr];
238 uint32_t s;
239 virtoffset = virt;
242 * R_ARM_V4BX are actually special marks for the linker.
243 * They even never have a target (shindex == SHN_UNDEF),
244 * so we simply ignore them before doing any checks.
246 if (ELF_R_TYPE(rel->info) == R_ARM_V4BX)
247 continue;
249 switch (sym->shindex)
251 case SHN_UNDEF:
252 kprintf
253 ("[BOOT:ELF] Undefined symbol '%s' in section '%s'\n",
254 (char *)((uint32_t) sh[shsymtab->link].addr) +
255 sym->name,
256 (char *)((uint32_t) sh[eh->shstrndx].addr) +
257 toreloc->name);
258 return 0;
260 case SHN_COMMON:
261 kprintf
262 ("[BOOT:ELF] COMMON symbol '%s' in section '%s'\n",
263 (char *)((uint32_t) sh[shsymtab->link].addr) +
264 sym->name,
265 (char *)((uint32_t) sh[eh->shstrndx].addr) +
266 toreloc->name);
268 return 0;
270 case SHN_ABS:
271 if (SysBase_sym == (void *)0) {
272 if (strncmp
273 ((char *)((uint32_t) sh[shsymtab->link].
274 addr) + sym->name, "SysBase",
275 8) == 0) {
276 SysBase_sym = sym;
277 goto SysBase_yes;
278 } else
279 goto SysBase_no;
280 } else if (SysBase_sym == sym) {
281 SysBase_yes: s = (uint32_t) 4UL;
282 virtoffset = 0;
283 } else
284 SysBase_no: s = sym->value;
285 break;
286 default:
287 s = (uint32_t) sh[sym->shindex].addr + sym->value - deltas[sym->shindex];
289 switch (ELF32_R_TYPE(rel->info)) {
290 case R_ARM_CALL:
291 case R_ARM_JUMP24:
292 case R_ARM_PC24:
294 if (is_exec)
297 Real executables are already properly relocated. Here, adjustment is
298 necessary *only* if the jump is across sections and *only* if offset
299 between both sections differs from the one in executable.
301 if shrel->info == sym->shindex don't do anything!
303 if (shrel->info != sym->shindex)
305 intptr_t expected_delta = deltas[sym->shindex] - deltas[shrel->info];
306 intptr_t actual_delta = sh[sym->shindex].addr - sh[shrel->info].addr;
308 /* On ARM the 24 bit offset is shifted by 2 to the right */
309 intptr_t offset = (AROS_LE2LONG(*p) & 0x00ffffff) << 2;
310 /* If highest bit set, make offset negative */
311 if (offset & 0x02000000)
312 offset -= 0x04000000;
314 /* Adjust offset */
315 offset += actual_delta - expected_delta;
317 /* Store back */
318 offset >>= 2;
319 *p &= AROS_LONG2LE(0xff000000);
320 *p |= AROS_LONG2LE(offset & 0x00ffffff);
323 else
325 /* On ARM the 24 bit offset is shifted by 2 to the right */
326 signed long offset = (AROS_LE2LONG(*p) & 0x00ffffff) << 2;
327 /* If highest bit set, make offset negative */
328 if (offset & 0x02000000)
329 offset -= 0x04000000;
331 offset += s - (ULONG)p;
333 offset >>= 2;
334 *p &= AROS_LONG2LE(0xff000000);
335 *p |= AROS_LONG2LE(offset & 0x00ffffff);
338 break;
341 case R_ARM_MOVW_ABS_NC:
342 case R_ARM_MOVT_ABS:
344 intptr_t offset = AROS_LE2LONG(*p);
345 offset = ((offset & 0xf0000) >> 4) | (offset & 0xfff);
346 //offset = (offset ^ 0x8000) - 0x8000;
348 /* If MOVT relocation shift the offset 16 bits left */
349 if (ELF_R_TYPE(rel->info) == R_ARM_MOVT_ABS)
350 offset <<= 16;
352 kprintf("movw/movt: offset=%08x, s=%08x, sym->value=%08x, section_orig_addr=%08x, section_addr=%08x\n", offset, s, sym->value,
353 deltas[sym->shindex], sh[sym->shindex].addr + virtoffset);
355 if (is_exec)
357 /* Fix address by difference between actual address and expected address */
358 offset += (intptr_t)sh[sym->shindex].addr - (intptr_t)deltas[sym->shindex] + (intptr_t)virtoffset;
360 else
362 offset += (intptr_t)(s + virtoffset);
365 /* If MOVT shift the offset back */
366 if (ELF_R_TYPE(rel->info) == R_ARM_MOVT_ABS)
367 offset >>= 16;
369 *p &= AROS_LONG2LE(0xfff0f000);
370 *p |= AROS_LONG2LE(((offset & 0xf000) << 4) | (offset & 0x0fff));
372 break;
374 case R_ARM_ABS32: /* PC relative 32 bit signed */
375 if (is_exec)
377 *p += (uintptr_t)sh[sym->shindex].addr - (uintptr_t)deltas[sym->shindex] + (uintptr_t)virtoffset;
379 else
381 *p += s + virtoffset;
383 break;
385 case R_ARM_NONE: /* No reloc */
386 break;
388 default:
389 kprintf("[BOOT:ELF] Unknown relocation %d in ELF file\n",
390 ELF32_R_TYPE(rel->info));
391 return 0;
394 return 1;
398 int loadElf(void *elf_file)
400 struct elfheader *eh = (struct elfheader *)elf_file;
401 uintptr_t deltas[int_shnum];
402 //uint32_t s_ro = 0;
403 //uint32_t s_rw = 0;
405 DELF(kprintf("[BOOT] loadElf(%p)\n", eh));
407 if (checkHeader(eh))
409 struct sheader *sh = (struct sheader *)((intptr_t)elf_file + eh->shoff);
410 int i;
412 for (i = 0; i < int_shnum; i++)
414 /* Load the symbol and string tables */
415 if (sh[i].type == SHT_SYMTAB || sh[i].type == SHT_STRTAB)
417 sh[i].addr = (APTR)((unsigned long)elf_file + sh[i].offset);
419 /* Does the section require memoy allcation? */
420 else if (sh[i].flags & SHF_ALLOC)
422 deltas[i] = (uintptr_t)sh[i].addr;
423 /* Yup, it does. Load the hunk */
424 if (!load_hunk(elf_file, &sh[i]))
426 return 0;
428 else
430 if (sh[i].size)
432 DELF(kprintf("[BOOT:ELF] %s section loaded at %p (Virtual addr: %p, requestet addr: %p)\n",
433 sh[i].flags & SHF_WRITE ? "RW":"RO",
434 sh[i].addr,
435 sh[i].addr + virtoffset,
436 deltas[i]));
442 /* For every loaded section perform the relocations */
443 for (i = 0; i < int_shnum; i++)
445 if (sh[i].type == SHT_REL && sh[sh[i].info].addr)
447 sh[i].addr = (APTR)((uint32_t) elf_file + sh[i].offset);
448 if (!sh[i].addr || !relocate(eh, sh, i, virtoffset, deltas))
450 return 0;
455 return 1;