summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/elf.c106
-rw-r--r--lib/elf.h17
2 files changed, 123 insertions, 0 deletions
diff --git a/lib/elf.c b/lib/elf.c
new file mode 100644
index 0000000..551a5a5
--- /dev/null
+++ b/lib/elf.c
@@ -0,0 +1,106 @@
+#include "elf.h"
+
+bool elf64_validate(Elf64_Ehdr *ehdr, unsigned type, unsigned machine)
+{
+ if (ehdr->e_ident[EI_MAG0] != ELFMAG0 ||
+ ehdr->e_ident[EI_MAG1] != ELFMAG1 ||
+ ehdr->e_ident[EI_MAG2] != ELFMAG2 ||
+ ehdr->e_ident[EI_MAG3] != ELFMAG3 ||
+ ehdr->e_ident[EI_CLASS] != ELFCLASS64 ||
+ ehdr->e_ident[EI_DATA] != ELFDATA2LSB ||
+ ehdr->e_ident[EI_VERSION] != EV_CURRENT ||
+ ehdr->e_machine != machine ||
+ ehdr->e_type != type)
+ {
+ return false;
+ }
+
+ return true;
+}
+
+size_t elf64_size(Elf64_Ehdr *ehdr, Elf64_Phdr *phdrs)
+{
+ size_t size = 0;
+
+ for (int i = 0; i < ehdr->e_phnum; i++)
+ {
+ if (phdrs[i].p_type != PT_LOAD)
+ {
+ continue;
+ }
+
+ if (phdrs[i].p_paddr + phdrs[i].p_memsz > size)
+ {
+ size = phdrs[i].p_paddr + phdrs[i].p_memsz;
+ if (phdrs[i].p_align > 1)
+ {
+ size = (size + phdrs[i].p_align - 1) &
+ ~(phdrs[i].p_align - 1);
+ }
+ }
+ }
+
+ return size;
+}
+
+void elf64_reloc(Elf64_Ehdr *ehdr)
+{
+ // locate the dynamic segment
+ Elf64_Phdr *phdrs = (Elf64_Phdr *)(ehdr->e_phoff + (uintptr_t)ehdr);
+ Elf64_Dyn *dyn = NULL;
+ Elf64_Rela *rela = NULL;
+ size_t rela_size;
+ int rela_count;
+
+ for (int i = 0; i < ehdr->e_phnum; i++)
+ {
+ if (phdrs[i].p_type != PT_DYNAMIC) continue;
+
+ dyn = (Elf64_Dyn *)(phdrs[i].p_offset + (uintptr_t)ehdr);
+ }
+
+ // parse the dynamic segment
+ while (dyn->d_tag != DT_NULL)
+ {
+ switch (dyn->d_tag)
+ {
+ case DT_RELA:
+ rela = (Elf64_Rela *)(dyn->d_ptr + (uintptr_t)ehdr);
+ break;
+ case DT_RELASZ:
+ rela_size = dyn->d_val;
+ break;
+ case DT_RELAENT:
+ rela_count = rela_size / dyn->d_val;
+ break;
+ default:
+ break;
+ }
+ dyn++;
+ }
+
+ // perform the actual relocations
+
+ for (int i; i < rela_count; i++)
+ {
+ union
+ {
+ Elf_Byte word8;
+ Elf64_Half word16;
+ Elf64_Word word32;
+ Elf64_Xword word64;
+ }
+ *reloc_ptr = NULL;
+
+ switch (ELF64_R_TYPE(rela[i].r_info))
+ {
+ case R_AMD64_RELATIVE:
+ reloc_ptr = (void *)(rela[i].r_offset + (uintptr_t)ehdr);
+ reloc_ptr->word64 = (rela[i].r_addend + (uintptr_t)ehdr);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
diff --git a/lib/elf.h b/lib/elf.h
new file mode 100644
index 0000000..3edb0a1
--- /dev/null
+++ b/lib/elf.h
@@ -0,0 +1,17 @@
+#pragma once
+
+#include <elf/elf64.h>
+
+#include <stdbool.h>
+#include <stddef.h>
+
+#ifdef __x86_64__
+# define elf_validate elf64_validate
+# define elf_size elf64_size
+# define elf_reloc elf64_reloc
+#endif
+
+bool elf64_validate(Elf64_Ehdr *ehdr, unsigned type, unsigned machine);
+size_t elf64_size(Elf64_Ehdr *ehdr, Elf64_Phdr *phdr);
+void elf64_reloc(Elf64_Ehdr *ehdr);
+