#include <assert.h>
-#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <libelfu/libelfu.h>
+static int cmpScnOffs(const void *ms1, const void *ms2)
+{
+ assert(ms1);
+ assert(ms2);
+
+ ElfuScn *s1 = *(ElfuScn**)ms1;
+ ElfuScn *s2 = *(ElfuScn**)ms2;
+
+ assert(s1);
+ assert(s2);
+
+
+ if (s1->shdr.sh_offset < s2->shdr.sh_offset) {
+ return -1;
+ } else if (s1->shdr.sh_offset == s2->shdr.sh_offset) {
+ return 0;
+ } else /* if (s1->shdr.sh_offset > s2->shdr.sh_offset) */ {
+ return 1;
+ }
+}
+
+
+
+static ElfuPhdr* parentPhdr(ElfuElf *me, ElfuScn *ms)
+{
+ ElfuPhdr *mp;
+
+ assert(me);
+ assert(ms);
+
+ CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
+ if (mp->phdr.p_type != PT_LOAD) {
+ continue;
+ }
+
+ if (ms->shdr.sh_addr >= mp->phdr.p_vaddr
+ && OFFS_END(ms->shdr.sh_addr, ms->shdr.sh_size) <= OFFS_END(mp->phdr.p_vaddr, mp->phdr.p_memsz)) {
+ return mp;
+ } else if (ms->shdr.sh_offset >= mp->phdr.p_offset
+ && OFFS_END(ms->shdr.sh_offset, SCNFILESIZE(&ms->shdr)) <= OFFS_END(mp->phdr.p_offset, mp->phdr.p_filesz)
+ && OFFS_END(ms->shdr.sh_offset, ms->shdr.sh_size) <= OFFS_END(mp->phdr.p_offset, mp->phdr.p_memsz)) {
+ return mp;
+ }
+ }
+
+ return NULL;
+}
+
+
static ElfuPhdr* modelFromPhdr(GElf_Phdr *phdr)
{
ElfuPhdr *mp;
+ assert(phdr);
+
mp = malloc(sizeof(ElfuPhdr));
if (!mp) {
+ ELFU_WARN("modelFromPhdr: malloc() failed for ElfuPhdr.\n");
return NULL;
}
mp->phdr = *phdr;
+ CIRCLEQ_INIT(&mp->childScnList);
+ CIRCLEQ_INIT(&mp->childPhdrList);
+
return mp;
}
{
ElfuScn *ms;
+ assert(scn);
ms = malloc(sizeof(ElfuScn));
if (!ms) {
- return NULL;
+ ELFU_WARN("modelFromSection: malloc() failed for ElfuScn.\n");
+ goto ERROR;
}
- if (gelf_getshdr(scn, &ms->shdr) != &ms->shdr) {
- fprintf(stderr, "gelf_getshdr() failed: %s\n", elf_errmsg(-1));
- goto out;
- }
+ assert(gelf_getshdr(scn, &ms->shdr) == &ms->shdr);
/* Copy each data part in source segment */
ms->data.d_size = ms->shdr.sh_size;
ms->data.d_version = elf_version(EV_NONE);
if (ms->shdr.sh_type != SHT_NOBITS
- && ms->shdr.sh_size > 1) {
+ && ms->shdr.sh_size > 0) {
Elf_Data *data;
ms->data.d_buf = malloc(ms->shdr.sh_size);
if (!ms->data.d_buf) {
- fprintf(stderr, "modelFromSection: Could not allocate data buffer (%jx bytes).\n", ms->shdr.sh_size);
- goto out;
+ ELFU_WARN("modelFromSection: malloc() failed for data buffer (%jx bytes).\n", ms->shdr.sh_size);
+ goto ERROR;
}
- /* A non-empty section should contain at least on data block. */
+ /* A non-empty section should contain at least one data block. */
data = elf_rawdata(scn, NULL);
-
assert(data);
ms->data.d_align = data->d_align;
while (data) {
if (data->d_off + data->d_size > ms->shdr.sh_size) {
- fprintf(stderr, "modelFromSection: libelf delivered a bogus data blob. Skipping\n");
+ ELFU_WARN("modelFromSection: libelf delivered a bogus data blob. Skipping\n");
} else {
memcpy(ms->data.d_buf + data->d_off, data->d_buf, data->d_size);
}
}
}
+ ms->linkptr = NULL;
+ ms->infoptr = NULL;
+
+ ms->oldptr = NULL;
+
return ms;
- out:
- // FIXME
+ ERROR:
+ if (ms) {
+ free(ms);
+ }
return NULL;
}
ElfuElf* elfu_mFromElf(Elf *e)
{
ElfuElf *me;
- Elf_Scn *scn;
size_t shstrndx;
- size_t i, n;
+ size_t i, numPhdr, numShdr;
+ ElfuScn **secArray = NULL;
- if (elf_getshdrstrndx(e, &shstrndx) != 0) {
- shstrndx = 0;
+ assert(e);
+ if (elfu_eCheck(e)) {
+ goto ERROR;
}
me = malloc(sizeof(ElfuElf));
if (!me) {
- return NULL;
+ ELFU_WARN("elfu_mFromElf: malloc() failed for ElfuElf.\n");
+ goto ERROR;
}
- CIRCLEQ_INIT(&me->scnList);
+
+ /* General stuff */
CIRCLEQ_INIT(&me->phdrList);
+ CIRCLEQ_INIT(&me->orphanScnList);
me->shstrtab = NULL;
- /*
- * General stuff
- */
me->elfclass = gelf_getclass(e);
- if (me->elfclass == ELFCLASSNONE) {
- fprintf(stderr, "getclass() failed: %s\n", elf_errmsg(-1));
+ assert(me->elfclass != ELFCLASSNONE);
+ assert(gelf_getehdr(e, &me->ehdr) == &me->ehdr);
+
+
+ /* Get the section string table index */
+ if (elf_getshdrstrndx(e, &shstrndx) != 0) {
+ shstrndx = 0;
}
- if (!gelf_getehdr(e, &me->ehdr)) {
- fprintf(stderr, "gelf_getehdr() failed: %s\n", elf_errmsg(-1));
- goto out;
+
+ /* Load segments */
+ assert(!elf_getphdrnum(e, &numPhdr));
+ for (i = 0; i < numPhdr; i++) {
+ GElf_Phdr phdr;
+ ElfuPhdr *mp;
+
+ assert(gelf_getphdr(e, i, &phdr) == &phdr);
+
+ mp = modelFromPhdr(&phdr);
+ if (!mp) {
+ goto ERROR;
+ }
+
+ CIRCLEQ_INSERT_TAIL(&me->phdrList, mp, elem);
+ }
+
+ if (numPhdr > 0) {
+ ElfuPhdr *mp;
+
+ /* Find PHDR -> PHDR dependencies (needs sorted sections) */
+ CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
+ ElfuPhdr *mp2;
+
+ if (mp->phdr.p_type != PT_LOAD) {
+ continue;
+ }
+
+ CIRCLEQ_FOREACH(mp2, &me->phdrList, elem) {
+ if (mp2 == mp) {
+ continue;
+ }
+
+ if (mp->phdr.p_vaddr <= mp2->phdr.p_vaddr
+ && OFFS_END(mp2->phdr.p_vaddr, mp2->phdr.p_memsz) <= OFFS_END(mp->phdr.p_vaddr, mp->phdr.p_memsz)) {
+ CIRCLEQ_INSERT_TAIL(&mp->childPhdrList, mp2, elemChildPhdr);
+ }
+ }
+ }
}
- /*
- * Sections
- */
- scn = elf_getscn(e, 1);
- i = 1;
- while (scn) {
- ElfuScn *ms = modelFromSection(scn);
+ /* Load sections */
+ assert(!elf_getshdrnum(e, &numShdr));
+ if (numShdr > 1) {
+ secArray = malloc((numShdr - 1) * sizeof(*secArray));
+ if (!secArray) {
+ ELFU_WARN("elfu_mFromElf: malloc() failed for secArray.\n");
+ goto ERROR;
+ }
+
+ for (i = 1; i < numShdr; i++) {
+ Elf_Scn *scn;
+ ElfuScn *ms;
+
+ scn = elf_getscn(e, i);
+ assert(scn);
+
+ ms = modelFromSection(scn);
+ if (!ms) {
+ goto ERROR;
+ }
+
+ secArray[i-1] = ms;
- if (ms) {
- CIRCLEQ_INSERT_TAIL(&me->scnList, ms, elem);
if (i == shstrndx) {
me->shstrtab = ms;
}
- } else {
- goto out;
}
- scn = elf_nextscn(e, scn);
- i++;
- }
+ /* Find sh_link dependencies */
+ for (i = 0; i < numShdr - 1; i++) {
+ ElfuScn *ms = secArray[i];
+
+ switch (ms->shdr.sh_type) {
+ case SHT_REL:
+ case SHT_RELA:
+ if (ms->shdr.sh_info > 0) {
+ ms->infoptr = secArray[ms->shdr.sh_info - 1];
+ }
+ case SHT_DYNAMIC:
+ case SHT_HASH:
+ case SHT_SYMTAB:
+ case SHT_DYNSYM:
+ case SHT_GNU_versym:
+ case SHT_GNU_verdef:
+ case SHT_GNU_verneed:
+ if (ms->shdr.sh_link > 0) {
+ ms->linkptr = secArray[ms->shdr.sh_link - 1];
+ }
+ }
+ }
- /*
- * Segments
- */
- if (elf_getphdrnum(e, &n)) {
- fprintf(stderr, "elf_getphdrnum() failed: %s\n", elf_errmsg(-1));
- }
+ /* Sort sections by file offset */
+ qsort(secArray, numShdr - 1, sizeof(*secArray), cmpScnOffs);
- for (i = 0; i < n; i++) {
- GElf_Phdr phdr;
- ElfuPhdr *mp;
- if (gelf_getphdr(e, i, &phdr) != &phdr) {
- fprintf(stderr, "gelf_getphdr() failed for #%d: %s\n", i, elf_errmsg(-1));
- break;
- }
+ /* Find PHDR -> Section dependencies (needs sorted sections) */
+ for (i = 0; i < numShdr - 1; i++) {
+ ElfuScn *ms = secArray[i];
- mp = modelFromPhdr(&phdr);
+ ElfuPhdr *parent = parentPhdr(me, ms);
- if (mp) {
- CIRCLEQ_INSERT_TAIL(&me->phdrList, mp, elem);
- } else {
- goto out;
+ if (parent) {
+ CIRCLEQ_INSERT_TAIL(&parent->childScnList, ms, elemChildScn);
+ } else {
+ CIRCLEQ_INSERT_TAIL(&me->orphanScnList, ms, elemChildScn);
+ }
}
}
-
return me;
- out:
- // FIXME
+
+ ERROR:
+ if (secArray) {
+ free(secArray);
+ }
+ if (me) {
+ // TODO: Free data structures
+ }
+
+ ELFU_WARN("elfu_mFromElf: Failed to load file.\n");
return NULL;
}