home *** CD-ROM | disk | FTP | other *** search
- /*
- (C) 1995-96 AROS - The Amiga Replacement OS
- $Id: loadseg_elf.c,v 1.17 1997/02/04 14:13:02 digulla Exp $
-
- Desc: Code to dynamically load ELF executables
- Lang: english
- */
- #include <exec/memory.h>
- #include <proto/exec.h>
- #include <dos/dosasl.h>
- #include <proto/dos.h>
- #include <proto/aros.h>
- #include "dos_intern.h"
- #include <aros/debug.h>
-
- extern struct DosLibrary * DOSBase;
-
- #define SHT_PROGBITS 1
- #define SHT_SYMTAB 2
- #define SHT_STRTAB 3
- #define SHT_RELA 4
- #define SHT_NOBITS 8
- #define SHT_REL 9
-
- #define ET_REL 1
-
- #define EM_386 3
- #define EM_68K 4
-
- #define R_386_32 1
- #define R_386_PC32 2
- #define R_68K_32 1
- #define R_68K_PC32 4
-
- #define STT_OBJECT 1
- #define STT_FUNC 2
-
- #define ELF32_ST_TYPE(i) ((i) & 0x0F)
-
- struct elfheader
- {
- UBYTE ident[16];
- UWORD type;
- UWORD machine;
- ULONG version;
- APTR entry;
- ULONG phoff;
- ULONG shoff;
- ULONG flags;
- UWORD ehsize;
- UWORD phentsize;
- UWORD phnum;
- UWORD shentsize;
- UWORD shnum;
- UWORD shstrndx;
- };
-
- struct sheader
- {
- ULONG name;
- ULONG type;
- ULONG flags;
- APTR addr;
- ULONG offset;
- ULONG size;
- ULONG link;
- ULONG info;
- ULONG addralign;
- ULONG entsize;
- };
-
- struct symbol
- {
- ULONG name; /* Offset of the name string in the string table */
- ULONG value; /* Varies; eg. the offset of the symbol in its hunk */
- ULONG size; /* How much memory does the symbol occupy */
- UBYTE info; /* What kind of symbol is this ? (global, variable, etc) */
- UBYTE other; /* undefined */
- WORD shindex; /* In which section is the symbol defined ? */
- };
-
- struct relo
- {
- ULONG addr; /* Address of the relocation (relative to the last loaded hunk) */
- ULONG info; /* Type of the relocation */
- #ifdef __mc68000__
- LONG addend; /* Constant addend used to compute value */
- #endif
- };
-
- struct hunk
- {
- ULONG size; /* Size of the hunk */
- UBYTE * memory; /* First byte */
- };
-
- int read_block (BPTR file, ULONG offset, APTR buffer, ULONG size)
- {
- LONG subsize;
- UBYTE * buf = (UBYTE *)buffer;
-
- if (Seek (file, offset, OFFSET_BEGINNING) < 0)
- return 1;
-
- while (size)
- {
- subsize = Read (file, buf, size);
-
- if (subsize == 0)
- {
- ((struct Process *)FindTask (NULL))->pr_Result2 = ERROR_BAD_HUNK;
- return 1;
- }
-
- if (subsize < 0)
- return 1;
-
- buf += subsize;
- size -= subsize;
- }
-
- return 0;
- } /* read_block */
-
- BPTR LoadSeg_ELF (BPTR file)
- {
- struct elfheader eh;
- UBYTE * shtab = NULL;
- UBYTE * shstrtab = NULL;
- UBYTE * strtab = NULL;
- struct symbol * symtab = NULL;
- struct hunk * hunks = NULL;
- struct relo * reltab = NULL;
- struct symbol * symbol;
- struct sheader * sh;
-
- ULONG numsym,
- numrel,
- i;
- WORD t,
- mint = 0,
- maxt = 0;
- UBYTE * loaded;
- BPTR last = 0;
- LONG * error = &((struct Process *)FindTask (NULL))->pr_Result2;
-
- #define ERROR(a) { *error = a; goto end; }
-
- /* Load the header */
- if (read_block (file, 0, &eh, sizeof (eh)))
- goto end;
-
- /* Check the header of the file */
- if
- (
- eh.ident[0] != 0x7f
- || eh.ident[1] != 'E'
- || eh.ident[2] != 'L'
- || eh.ident[3] != 'F'
- )
- ERROR (ERROR_NOT_EXECUTABLE);
-
- /* Check file type and the CPU the file is for */
- if (eh.type != ET_REL || (eh.machine != EM_386 && eh.machine != EM_68K))
- ERROR (ERROR_OBJECT_WRONG_TYPE);
-
- /* Get memory for section headers */
- shtab = AllocVec (eh.shentsize * eh.shnum, MEMF_ANY);
-
- if (shtab == NULL)
- ERROR (ERROR_NO_FREE_STORE);
-
- /* Read section table */
- if (read_block(file, eh.shoff, shtab, eh.shentsize * eh.shnum))
- goto end;
-
- /* Look up the symbol table */
- for (t=1; t<eh.shnum; t++)
- {
- sh = (struct sheader *)(shtab + t*eh.shentsize);
-
- if (sh->type == SHT_SYMTAB)
- break;
- }
-
- if (t == eh.shnum)
- ERROR (ERROR_OBJECT_WRONG_TYPE);
-
- /* Allocate memory for the symbol table */
- symtab = AllocVec (sh->size, MEMF_ANY);
-
- if (symtab == NULL)
- ERROR (ERROR_NO_FREE_STORE);
-
- /* Read the symbol table */
- if (read_block (file, sh->offset, symtab, sh->size))
- goto end;
-
- numsym = sh->size / sizeof (struct symbol);
-
- mint = maxt = symtab[0].shindex;
-
- /* kprintf ("Symbol %d: index=%d\n", 0, symtab[0].shindex); */
-
- /* Find the minimal number of hunks which are neccessary to satisfy
- all symbol references (ie. all hunks in which a symbol resides) */
- for (i=1; i<numsym; i++)
- {
- /* kprintf ("Symbol %d: index=%d\n", i, symtab[i].shindex); */
-
- if (symtab[i].shindex < mint)
- mint = symtab[i].shindex;
-
- if (symtab[i].shindex > maxt)
- maxt = symtab[i].shindex;
- }
-
- /* Allocate memory for information about every hunk */
- hunks = AllocVec (sizeof (struct hunk) * (maxt - mint + 1), MEMF_CLEAR);
-
- if (hunks == NULL)
- ERROR (ERROR_NO_FREE_STORE);
-
- /* Offset the base. Now I can simply access the first hunk as
- hunks[t] instead of hunks[t-mint] */
- hunks -= mint;
-
- /* Find the basic size for each hunk */
- for (t=1; t<eh.shnum; t++)
- {
- sh = (struct sheader *)(shtab + t*eh.shentsize);
-
- if (sh->type == SHT_PROGBITS || sh->type == SHT_NOBITS)
- hunks[t].size = sh->size;
- }
-
- /* Load names of sections */
- if (eh.shstrndx)
- {
- sh = (struct sheader *)(shtab + eh.shstrndx*eh.shentsize);
-
- shstrtab = AllocVec (sh->size, MEMF_ANY);
-
- if (shstrtab == NULL)
- ERROR (ERROR_NO_FREE_STORE);
-
- if (read_block (file, sh->offset, shstrtab, sh->size))
- goto end;
-
- /* {
- int n, t;
-
- for (n=t=0; t<sh->size; n++)
- {
- kprintf ("String %d@%d: \"%s\"\n", n, t, &shstrtab[t]);
- t += strlen (&shstrtab[t]) + 1;
- }
- } */
- }
-
- /* Look for names of symbols */
- for (t=eh.shnum; t>0; t--)
- {
- sh = (struct sheader *)(shtab + t*eh.shentsize);
-
- if (sh->type == SHT_STRTAB)
- break;
- }
-
- /* Found the section with the names ? Load the symbols' names */
- if (t)
- {
- strtab = AllocVec (sh->size, MEMF_ANY);
-
- if (strtab == NULL)
- ERROR (ERROR_NO_FREE_STORE);
-
- /* kprintf ("Reading StrTab at %d (offset=%ld, size=%ld)\n", eh.shstrndx, sh->offset, sh->size); */
-
- if (read_block (file, sh->offset, strtab, sh->size))
- goto end;
-
- /*{
- int n, t;
-
- for (n=t=0; t<sh->size; n++)
- {
- kprintf ("String %d@%d: \"%s\"\n", n, t, &strtab[t]);
- t += strlen (&strtab[t]) + 1;
- }
- } */
- }
-
- /* kprintf (" File has %d sections.\n", eh.shnum); */
-
- /* Reserve memory for each global symbol in its hunk */
- for (i=0; i<numsym; i++)
- {
- if (symtab[i].shindex < 0)
- {
- symtab[i].value = hunks[symtab[i].shindex].size;
-
- hunks[symtab[i].shindex].size += symtab[i].size;
- }
- }
-
- /* Allocate memory for each segment */
- for (t=mint; t<=maxt; t++)
- {
- /* Don't allocate memory for hunks which don't need any */
- if (hunks[t].size)
- {
- hunks[t].memory = AllocVec (hunks[t].size + sizeof (BPTR), MEMF_CLEAR);
-
- if (hunks[t].memory == NULL)
- ERROR (ERROR_NO_FREE_STORE);
-
- hunks[t].memory += sizeof(BPTR);
-
- D(bug(" Hunk %3d: 0x%p - 0x%p\n", t, hunks[t].memory, hunks[t].memory+hunks[t].size));
- }
- }
-
- /* Show the final addresses of global symbols */
- if (strtab)
- {
- for (i=0; i<numsym; i++)
- {
- /* Print the symbol if it has a name and if it's a variable
- or function */
- if (strtab[symtab[i].name]
- && (
- ELF32_ST_TYPE(symtab[i].info) == STT_OBJECT
- || ELF32_ST_TYPE(symtab[i].info) == STT_FUNC
- )
- )
- {
- kprintf (" Symbol at 0x%p: %s\n"
- , hunks[symtab[i].shindex].memory + symtab[i].value
- , &strtab[symtab[i].name]
- );
- }
- }
- }
-
- loaded = NULL;
-
- /* Now load the pieces into memory */
- for (t=1; t<eh.shnum; t++)
- {
- sh = (struct sheader *)(shtab + t*eh.shentsize);
-
- switch(sh->type)
- {
- case SHT_PROGBITS: /* Code */
- if (read_block (file, sh->offset, hunks[t].memory, sh->size))
- goto end;
-
- loaded = hunks[t].memory;
-
- if (strtab)
- {
- kprintf (" Section at 0x%p ... 0x%p: %s\n"
- , loaded
- , loaded + sh->size - 1
- , &shstrtab[sh->name]
- );
- }
-
- break;
-
- case SHT_RELA:
- case SHT_REL: /* Relocation table */
- if (loaded == NULL)
- ERROR (ERROR_OBJECT_WRONG_TYPE);
-
- /* Get memory for the relocation table */
- reltab = AllocVec (sh->size, MEMF_ANY);
-
- if (reltab == NULL)
- ERROR (ERROR_NO_FREE_STORE);
-
- /* Load it */
- if (read_block (file, sh->offset, reltab, sh->size))
- goto end;
-
- numrel = sh->size / sizeof (struct relo);
-
- /* For each relocation ... */
- for (i=0; i<numrel; i++)
- {
- symbol = &symtab[reltab[i].info >> 8];
-
- switch (reltab[i].info & 0xFF)
- {
- #ifdef __i386__
- case R_386_32: /* 32bit absolute */
- /* The address of a symbol is the base address of the
- hunk in which the symbol is plus the offset of the
- symbol to the beginning of this hunk. */
- *(ULONG *)&loaded[reltab[i].addr] +=
- (ULONG)hunks[symbol->shindex].memory + symbol->value;
- break;
-
- case R_386_PC32: /* 32bit PC relative */
- /* Similar to R_386_32 but relative to the address where
- the relocation is in memory. */
- *(ULONG *)&loaded[reltab[i].addr] +=
- (ULONG)hunks[symbol->shindex].memory +
- symbol->value - (ULONG)&loaded[reltab[i].addr];
- break;
- #endif
- #ifdef __mc68000__
- case R_68K_32:
- *(ULONG *)&loaded[reltab[i].addr] =
- (ULONG)hunks[symbol->shindex].memory + symbol->value +
- reltab[i].addend;
- break;
- case R_68K_PC32:
- *(ULONG *)&loaded[reltab[i].addr] =
- ((ULONG)hunks[symbol->shindex].memory+ symbol->value +
- reltab[i].addend - (ULONG)&loaded[reltab[i].addr]);
- break;
- #endif
-
- default:
- ERROR (ERROR_BAD_HUNK);
- } /* switch */
- } /* for */
-
- /* Release memory */
- FreeVec (reltab);
- reltab = NULL;
- loaded = NULL;
-
- break;
- } /* switch */
- }
-
- /* Link hunks (and flush caches) */
- for (t=mint; t<0; t++)
- {
- if (hunks[t].size)
- {
- CacheClearE(hunks[t].memory, hunks[t].size,
- CACRF_ClearI|CACRF_ClearD);
- ((BPTR *)hunks[t].memory)[-1] = last;
- last = MKBADDR((BPTR *)hunks[t].memory - 1);
- }
- }
-
- for (t=maxt; t>=0; t--)
- {
- if (hunks[t].size)
- {
- CacheClearE(hunks[t].memory, hunks[t].size,
- CACRF_ClearI|CACRF_ClearD);
- ((BPTR *)hunks[t].memory)[-1] = last;
- last = MKBADDR((BPTR *)hunks[t].memory-1);
- }
- }
-
- /* Free hunk information table */
- FreeVec (hunks+mint);
-
- hunks = NULL;
-
- end:
- FreeVec (reltab);
-
- /* Free all hunks, too ? */
- if (hunks != NULL)
- {
- for (t=mint; t<=maxt; t++)
- {
- if (hunks[t].memory != NULL)
- FreeVec (hunks[t].memory - sizeof(BPTR));
- }
-
- FreeVec (hunks + mint);
-
- /* Fail */
- last = NULL;
- }
-
- if (shstrtab)
- FreeVec (shstrtab);
-
- if (strtab)
- FreeVec (strtab);
-
- FreeVec (symtab);
- FreeVec (shtab);
-
- return last;
- } /* LoadSeg_ELF */
-