Build fix, still using old KrnRegisterInterface.
[AROS.git] / arch / ppc-sam440 / dos / internalloadseg_elf.c
bloba218023d2ccf5f2d1cfbabae2bdb66a57f9b1e7e
1 /*
2 Copyright © 1995-2011, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc: Code to dynamically load ELF executables
6 Lang: english
8 1997/12/13: Changed filename to internalloadseg_elf.c
9 Original file was created by digulla.
12 #define DEBUG 0
14 #include <aros/config.h>
15 #include <aros/kernel.h>
16 #include <exec/memory.h>
17 #include <proto/exec.h>
18 #include <dos/elf.h>
19 #include <dos/dosasl.h>
20 #include <proto/dos.h>
21 #include <proto/arossupport.h>
22 #include <proto/kernel.h>
23 #include <aros/asmcall.h>
24 #include "internalloadseg.h"
25 #include "dos_intern.h"
27 #include <aros/debug.h>
28 #include <string.h>
29 #include <stddef.h>
31 #include <aros/macros.h>
33 struct hunk
35 ULONG size;
36 BPTR next;
37 char data[0];
38 } __attribute__((packed));
40 #define BPTR2HUNK(bptr) ((struct hunk *)((void *)bptr - offsetof(struct hunk, next)))
41 #define HUNK2BPTR(hunk) MKBADDR(&hunk->next)
43 #undef MyRead
44 #undef MyAlloc
45 #undef MyFree
47 #define MyRead(file, buf, size) \
48 AROS_UFC4 \
49 ( \
50 LONG, funcarray[0], \
51 AROS_UFCA(BPTR, file, D1), \
52 AROS_UFCA(void *, buf, D2), \
53 AROS_UFCA(LONG, size, D3), \
54 AROS_UFCA(struct DosLibrary *, DOSBase, A6) \
58 #define MyAlloc(size, flags) \
59 AROS_UFC3 \
60 ( \
61 void *, funcarray[1], \
62 AROS_UFCA(ULONG, size, D0), \
63 AROS_UFCA(ULONG, flags, D1), \
64 AROS_UFCA(struct ExecBase *, SysBase, A6) \
68 #define MyFree(addr, size) \
69 AROS_UFC3 \
70 ( \
71 void, funcarray[2], \
72 AROS_UFCA(void *, addr, A1), \
73 AROS_UFCA(ULONG, size, D0), \
74 AROS_UFCA(struct ExecBase *, SysBase, A6) \
77 static int read_block
79 BPTR file,
80 ULONG offset,
81 APTR buffer,
82 ULONG size,
83 SIPTR *funcarray,
84 struct DosLibrary *DOSBase
87 UBYTE *buf = (UBYTE *)buffer;
88 LONG subsize;
90 if (Seek(file, offset, OFFSET_BEGINNING) < 0)
91 return 0;
93 while (size)
95 subsize = MyRead(file, buf, size);
97 if (subsize <= 0)
99 if (subsize == 0)
100 SetIoErr(ERROR_BAD_HUNK);
102 return 0;
105 buf += subsize;
106 size -= subsize;
109 return 1;
112 static void *load_block
114 BPTR file,
115 ULONG offset,
116 ULONG size,
117 SIPTR *funcarray,
118 struct DosLibrary *DOSBase
121 D(bug("[ELF Loader] Load Block\n"));
122 D(bug("[ELF Loader] (size=%d)\n",size));
123 D(bug("[ELF Loader] (funcarray=0x%x)\n",funcarray));
124 D(bug("[ELF Loader] (funcarray[1]=0x%x)\n",funcarray[1]));
125 void *block = MyAlloc(size, MEMF_ANY);
126 if (block)
128 if (read_block(file, offset, block, size, funcarray, DOSBase))
129 return block;
131 MyFree(block, size);
133 else
134 SetIoErr(ERROR_NO_FREE_STORE);
136 return NULL;
139 static ULONG read_shnum(BPTR file, struct elfheader *eh, SIPTR *funcarray, struct DosLibrary *DOSBase)
141 ULONG shnum = eh->shnum;
143 /* the ELF header only uses 16 bits to store the count of section headers,
144 * so it can't handle more than 65535 headers. if the count is 0, and an
145 * offset is defined, then the real count can be found in the first
146 * section header (which always exists).
148 * similarly, if the string table index is SHN_XINDEX, then the actual
149 * index is found in the first section header also.
151 * see the System V ABI 2001-04-24 draft for more details.
153 if (eh->shnum == 0)
155 struct sheader sh;
157 if (eh->shoff == 0) {
158 SetIoErr(ERROR_NOT_EXECUTABLE);
159 return 0;
162 if (!read_block(file, eh->shoff, &sh, sizeof(sh), funcarray, DOSBase))
163 return 0;
165 /* wider section header count is in the size field */
166 shnum = sh.size;
168 /* sanity, if they're still invalid then this isn't elf */
169 if (shnum == 0)
170 SetIoErr(ERROR_NOT_EXECUTABLE);
173 return shnum;
176 static void register_elf(BPTR file, BPTR hunks, struct elfheader *eh, struct sheader *sh, struct DosLibrary *DOSBase)
178 #ifdef KrnRegisterModule
179 if (KernelBase)
181 char buffer[512];
183 if (NameFromFH(file, buffer, sizeof(buffer))) {
184 char *nameptr = buffer;
185 struct ELF_DebugInfo dbg = {eh, sh};
187 /* gdb support needs full paths */
188 #if !AROS_MODULES_DEBUG
189 /* First, go through the name, till end of the string */
190 while(*nameptr++);
191 /* Now, go back until either ":" or "/" is found */
192 while(nameptr > buffer && nameptr[-1] != ':' && nameptr[-1] != '/')
193 nameptr--;
194 #endif
195 KrnRegisterModule(nameptr, sh, &eh);
196 //KrnRegisterModule(nameptr, hunks, DEBUG_ELF, &dbg);
199 #endif
202 static int load_header(BPTR file, struct elfheader *eh, SIPTR *funcarray, struct DosLibrary *DOSBase) {
203 Seek(file, OFFSET_BEGINNING, 0);
204 if (!read_block(file, 0, eh, sizeof(struct elfheader), funcarray, DOSBase))
205 return 0;
207 if (eh->ident[0] != 0x7f || eh->ident[1] != 'E' ||
208 eh->ident[2] != 'L' || eh->ident[3] != 'F') {
209 D(bug("[ELF Loader] Not an ELF object\n"));
210 SetIoErr(ERROR_NOT_EXECUTABLE);
211 return 0;
213 D(bug("[ELF Loader] ELF object\n"));
215 /* WANT_CLASS should be defined for your target */
216 if (eh->ident[EI_CLASS] != AROS_ELF_CLASS ||
217 eh->ident[EI_VERSION] != EV_CURRENT ||
218 eh->type != ET_REL ||
219 eh->ident[EI_DATA] != AROS_ELF_DATA ||
220 eh->machine != AROS_ELF_MACHINE)
222 D(bug("[ELF Loader] Object is of wrong type\n"));
223 D(bug("[ELF Loader] EI_CLASS is %d - should be %d\n", eh->ident[EI_CLASS] , AROS_ELF_CLASS ));
224 D(bug("[ELF Loader] EI_VERSION is %d - should be %d\n", eh->ident[EI_VERSION], EV_CURRENT ));
225 D(bug("[ELF Loader] type is %d - should be %d\n", eh->type , ET_REL ));
226 D(bug("[ELF Loader] EI_DATA is %d - should be %d\n", eh->ident[EI_DATA] , AROS_ELF_DATA ));
227 D(bug("[ELF Loader] machine is %d - should be %d\n", eh->machine , AROS_ELF_MACHINE));
229 SetIoErr(ERROR_NOT_EXECUTABLE);
230 return 0;
233 return 1;
236 static int load_hunk
238 BPTR file,
239 BPTR **next_hunk_ptr,
240 struct sheader *sh,
241 SIPTR *funcarray,
242 BOOL do_align,
243 struct DosLibrary *DOSBase
246 struct hunk *hunk;
247 ULONG hunk_size;
249 if (!sh->size)
250 return 1;
252 /* The size of the hunk is the size of the section, plus
253 the size of the hunk structure, plus the size of the alignment (if necessary)*/
254 hunk_size = sh->size + sizeof(struct hunk);
256 if (do_align)
258 hunk_size += sh->addralign;
260 /* Also create space for a trampoline, if necessary */
261 if (sh->flags & SHF_EXECINSTR)
262 hunk_size += sizeof(struct FullJumpVec);
265 hunk = MyAlloc(hunk_size, MEMF_ANY | (sh->type == SHT_NOBITS) ? MEMF_CLEAR : 0);
266 if (hunk)
268 hunk->next = 0;
269 hunk->size = hunk_size;
271 /* In case we are required to honour alignment, and If this section contains
272 executable code, create a trampoline to its beginning, so that even if the
273 alignment requirements make the actual code go much after the end of the
274 hunk structure, the code can still be reached in the usual way. */
275 if (do_align)
277 if (sh->flags & SHF_EXECINSTR)
279 sh->addr = (char *)AROS_ROUNDUP2
281 (IPTR)hunk->data + sizeof(struct FullJumpVec), sh->addralign
283 __AROS_SET_FULLJMP((struct FullJumpVec *)hunk->data, sh->addr);
285 else
286 sh->addr = (char *)AROS_ROUNDUP2((IPTR)hunk->data, sh->addralign);
288 else
289 sh->addr = hunk->data;
291 /* Link the previous one with the new one */
292 BPTR2HUNK(*next_hunk_ptr)->next = HUNK2BPTR(hunk);
294 D(bug("[dos] hunk @ %p, size=%08x, addr @ %p\n", hunk, hunk->size, sh->addr));
296 /* Update the pointer to the previous one, which is now the current one */
297 *next_hunk_ptr = &hunk->next;
299 if (sh->type != SHT_NOBITS)
300 return read_block(file, sh->offset, sh->addr, sh->size, funcarray, DOSBase);
302 return 1;
306 SetIoErr(ERROR_NO_FREE_STORE);
308 return 0;
311 static int relocate
313 struct elfheader *eh,
314 struct sheader *sh,
315 ULONG shrel_idx,
316 struct sheader *symtab_shndx,
317 struct DosLibrary *DOSBase
320 struct sheader *shrel = &sh[shrel_idx];
321 struct sheader *shsymtab = &sh[SHINDEX(shrel->link)];
322 struct sheader *toreloc = &sh[SHINDEX(shrel->info)];
324 struct symbol *symtab = (struct symbol *)shsymtab->addr;
325 struct relo *rel = (struct relo *)shrel->addr;
328 * Ignore relocs if the target section has no allocation. that can happen
329 * eg. with a .debug PROGBITS and a .rel.debug section
331 if (!(toreloc->flags & SHF_ALLOC))
332 return 1;
334 ULONG numrel = shrel->size / shrel->entsize;
335 ULONG i;
337 struct symbol *SysBase_sym = NULL;
339 for (i=0; i<numrel; i++, rel++)
341 struct symbol *sym;
342 ULONG *p;
343 IPTR s;
344 ULONG shindex;
346 #ifdef __arm__
348 * R_ARM_V4BX are actually special marks for the linker.
349 * They even never have a target (shindex == SHN_UNDEF),
350 * so we simply ignore them before doing any checks.
352 if (ELF_R_TYPE(rel->info) == R_ARM_V4BX)
353 continue;
354 #endif
356 sym = &symtab[ELF_R_SYM(rel->info)];
357 p = toreloc->addr + rel->offset;
359 if (sym->shindex != SHN_XINDEX)
360 shindex = sym->shindex;
362 else {
363 if (symtab_shndx == NULL) {
364 D(bug("[ELF Loader] got symbol with shndx 0xfff, but there's no symtab shndx table\n"));
365 SetIoErr(ERROR_BAD_HUNK);
366 return 0;
368 shindex = ((ULONG *)symtab_shndx->addr)[ELF_R_SYM(rel->info)];
371 DB2(bug("[ELF Loader] Processing symbol %s\n", sh[SHINDEX(shsymtab->link)].addr + sym->name));
373 switch (shindex)
376 case SHN_UNDEF:
377 D(bug("[ELF Loader] Undefined symbol '%s'\n",
378 (STRPTR)sh[SHINDEX(shsymtab->link)].addr + sym->name));
379 SetIoErr(ERROR_BAD_HUNK);
380 return 0;
382 case SHN_COMMON:
383 D(bug("[ELF Loader] COMMON symbol '%s'\n",
384 (STRPTR)sh[SHINDEX(shsymtab->link)].addr + sym->name));
385 SetIoErr(ERROR_BAD_HUNK);
387 return 0;
389 case SHN_ABS:
390 if (SysBase_sym == NULL)
392 if (strncmp((STRPTR)sh[SHINDEX(shsymtab->link)].addr + sym->name, "SysBase", 8) == 0)
394 SysBase_sym = sym;
395 goto SysBase_yes;
397 else
398 goto SysBase_no;
400 else
401 if (SysBase_sym == sym)
403 SysBase_yes: s = (IPTR)&SysBase;
405 else
406 SysBase_no: s = sym->value;
407 break;
409 default:
410 s = (IPTR)sh[SHINDEX(shindex)].addr + sym->value;
413 switch (ELF_R_TYPE(rel->info))
415 #if defined(__i386__)
417 case R_386_32: /* 32bit absolute */
418 *p += s;
419 break;
421 case R_386_PC32: /* 32bit PC relative */
422 *p += s - (ULONG)p;
423 break;
425 case R_386_NONE:
426 break;
428 #elif defined(__x86_64__)
429 case R_X86_64_64: /* 64bit direct/absolute */
430 *(UQUAD *)p = s + rel->addend;
431 break;
433 case R_X86_64_PC32: /* PC relative 32 bit signed */
434 *(ULONG *)p = s + rel->addend - (IPTR) p;
435 break;
437 case R_X86_64_32:
438 *(ULONG *)p = (UQUAD)s + (UQUAD)rel->addend;
439 break;
441 case R_X86_64_32S:
442 *(LONG *)p = (QUAD)s + (QUAD)rel->addend;
443 break;
445 case R_X86_64_NONE: /* No reloc */
446 break;
448 #elif defined(__mc68000__)
450 case R_68K_32:
451 *p = s + rel->addend;
452 break;
454 case R_68K_PC32:
455 *p = s + rel->addend - (ULONG)p;
456 break;
458 case R_68k_NONE:
459 break;
461 #elif defined(__ppc__) || defined(__powerpc__)
463 case R_PPC_ADDR32:
464 *p = s + rel->addend;
465 break;
467 case R_PPC_ADDR16_LO:
469 unsigned short *c = (unsigned short *) p;
470 *c = (s + rel->addend) & 0xffff;
472 break;
474 case R_PPC_ADDR16_HA:
476 unsigned short *c = (unsigned short *) p;
477 ULONG temp = s + rel->addend;
478 *c = temp >> 16;
479 if ((temp & 0x8000) != 0)
480 (*c)++;
482 break;
484 case R_PPC_REL16_LO:
486 unsigned short *c = (unsigned short *) p;
487 *c = (s + rel->addend - (ULONG) p) & 0xffff;
489 break;
491 case R_PPC_REL16_HA:
493 unsigned short *c = (unsigned short *) p;
494 ULONG temp = s + rel->addend - (ULONG) p;
495 *c = temp >> 16;
496 if ((temp & 0x8000) != 0)
497 (*c)++;
499 break;
501 case R_PPC_REL24:
502 *p &= ~0x3fffffc;
503 *p |= (s + rel->addend - (ULONG) p) & 0x3fffffc;
504 break;
506 case R_PPC_REL32:
507 *p = s + rel->addend - (ULONG) p;
508 break;
510 case R_PPC_NONE:
511 break;
513 #elif defined(__arm__)
514 case R_ARM_CALL:
515 case R_ARM_JUMP24:
516 case R_ARM_PC24:
518 /* On ARM the 24 bit offset is shifted by 2 to the right */
519 signed long offset = (*p & 0x00ffffff) << 2;
520 /* If highest bit set, make offset negative */
521 if (offset & 0x02000000)
522 offset -= 0x04000000;
524 offset += s - (uint32_t)p;
526 offset >>= 2;
527 *p &= 0xff000000;
528 *p |= offset & 0x00ffffff;
530 break;
532 case R_ARM_MOVW_ABS_NC:
533 case R_ARM_MOVT_ABS:
535 signed long offset = *p;
536 offset = ((offset & 0xf0000) >> 4) | (offset & 0xfff);
537 offset = (offset ^ 0x8000) - 0x8000;
539 offset += s;
541 if (ELF_R_TYPE(rel->info) == R_ARM_MOVT_ABS)
542 offset >>= 16;
544 *p &= 0xfff0f000;
545 *p |= ((offset & 0xf000) << 4) | (offset & 0x0fff);
547 break;
549 case R_ARM_ABS32:
550 *p += s;
551 break;
553 case R_ARM_NONE:
554 break;
556 #else
557 # error Your architecture is not supported
558 #endif
560 default:
561 D(bug("[ELF Loader] Unrecognized relocation type %d %d\n", i, ELF_R_TYPE(rel->info)));
562 SetIoErr(ERROR_BAD_HUNK);
563 return 0;
567 return 1;
570 BPTR InternalLoadSeg_ELF
572 BPTR file,
573 BPTR table __unused,
574 SIPTR *funcarray,
575 SIPTR *stack __unused,
576 struct DosLibrary *DOSBase
579 struct elfheader eh;
580 struct sheader *sh;
581 struct sheader *symtab_shndx = NULL;
582 BPTR hunks = 0;
583 BPTR *next_hunk_ptr = &hunks;
584 ULONG i;
585 BOOL exec_hunk_seen = FALSE;
586 ULONG int_shnum;
588 /* load and validate ELF header */
589 if (!load_header(file, &eh, funcarray, DOSBase))
590 return 0;
592 int_shnum = read_shnum(file, &eh, funcarray, DOSBase);
593 if (!int_shnum)
594 return 0;
596 /* load section headers */
597 if (!(sh = load_block(file, eh.shoff, int_shnum * eh.shentsize, funcarray, DOSBase)))
598 return 0;
600 /* Iterate over the section headers in order to do some stuff... */
601 for (i = 0; i < int_shnum; i++)
604 Load the symbol and string table(s).
606 NOTICE: the ELF standard, at the moment (Nov 2002) explicitely states
607 that only one symbol table per file is allowed. However, it
608 also states that this may change in future... we already handle it.
610 if (sh[i].type == SHT_SYMTAB || sh[i].type == SHT_STRTAB || sh[i].type == SHT_SYMTAB_SHNDX)
612 sh[i].addr = load_block(file, sh[i].offset, sh[i].size, funcarray, DOSBase);
613 if (!sh[i].addr)
614 goto error;
616 if (sh[i].type == SHT_SYMTAB_SHNDX) {
617 if (symtab_shndx == NULL)
618 symtab_shndx = &sh[i];
619 else
620 D(bug("[ELF Loader] file contains multiple symtab shndx tables. only using the first one\n"));
623 else
624 /* Load the section in memory if needed, and make an hunk out of it */
625 if (sh[i].flags & SHF_ALLOC)
627 if (sh[i].size)
629 /* Only allow alignment if this is an executable hunk
630 or if an executable hunk has been loaded already,
631 so to avoid the situation in which a data hunk has its
632 content displaced from the hunk's header in case it's the
633 first hunk (this happens with Keymaps, for instance). */
634 if (sh[i].flags & SHF_EXECINSTR)
635 exec_hunk_seen = TRUE;
637 if (!load_hunk(file, &next_hunk_ptr, &sh[i], funcarray, exec_hunk_seen, DOSBase))
638 goto error;
644 /* Relocate the sections */
645 for (i = 0; i < int_shnum; i++)
647 /* Does this relocation section refer to a hunk? If so, addr must be != 0 */
648 if ((sh[i].type == AROS_ELF_REL) && sh[SHINDEX(sh[i].info)].addr)
650 sh[i].addr = load_block(file, sh[i].offset, sh[i].size, funcarray, DOSBase);
651 if (!sh[i].addr || !relocate(&eh, sh, i, symtab_shndx, DOSBase))
652 goto error;
654 MyFree(sh[i].addr, sh[i].size);
655 sh[i].addr = NULL;
659 /* Everything is loaded now. Register the module at kernel.resource */
660 register_elf(file, hunks, &eh, sh, DOSBase);
661 goto end;
663 error:
665 /* There were some errors, deallocate The hunks */
667 InternalUnLoadSeg(hunks, (VOID_FUNC)funcarray[2]);
668 hunks = 0;
670 end:
672 /* Clear the caches to let the CPU see the new data and instructions */
674 BPTR curr = hunks;
675 while (curr)
677 struct hunk *hunk = BPTR2HUNK(BADDR(curr));
679 CacheClearE(hunk->data, hunk->size, CACRF_ClearD | CACRF_ClearI);
681 curr = hunk->next;
685 /* deallocate the symbol tables */
686 for (i = 0; i < int_shnum; i++)
688 if (((sh[i].type == SHT_SYMTAB) || (sh[i].type == SHT_STRTAB)) && (sh[i].addr != NULL))
689 MyFree(sh[i].addr, sh[i].size);
692 /* Free the section headers */
693 MyFree(sh, int_shnum * eh.shentsize);
695 return hunks;
698 #undef MyRead1
699 #undef MyAlloc
700 #undef MyFree