summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--.gitignore2
-rwxr-xr-xloader.c298
2 files changed, 300 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..079dcb8
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+# testing samples
+Library*
diff --git a/loader.c b/loader.c
new file mode 100755
index 0000000..f2d54bf
--- /dev/null
+++ b/loader.c
@@ -0,0 +1,298 @@
+#!/usr/bin/env -S tcc -run
+
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+// ELF is defined assuming 8-bit bytes
+#include <stdint.h>
+
+int main64(int fd, char endianness);
+
+int main(void) {
+ int fd = open("./Library.so", O_RDONLY);
+
+ unsigned char e_ident[EI_NIDENT];
+ if (read(fd, e_ident, EI_NIDENT) != EI_NIDENT)
+ return 1;
+
+ if (e_ident[EI_MAG0] != ELFMAG0 || e_ident[EI_MAG1] != ELFMAG1 || e_ident[EI_MAG2] != ELFMAG2 || e_ident[EI_MAG3] != ELFMAG3) {
+ puts("This doesn't seem to be an ELF file.");
+ return 1;
+ }
+
+ char endianness;
+ switch (e_ident[EI_DATA]) {
+ case ELFDATA2LSB:
+ endianness = 0;
+ break;
+ case ELFDATA2MSB:
+ endianness = 1;
+ break;
+ default:
+ puts("Your ELF file seems invalid.");
+ return 1;
+ }
+
+ switch (e_ident[EI_CLASS]) {
+ case ELFCLASS32:
+ puts("32-bit ELF loading not yet implemented.");
+ return 1;
+ case ELFCLASS64:
+ return main64(fd, endianness);
+ default:
+ puts("Your ELF file seems invalid.");
+ return 1;
+ }
+
+ return 2;
+}
+
+void * real_addr64(Elf64_Phdr *program_headers, Elf64_Addr address) {
+ return 0;
+}
+
+int main64(int fd, char endianness) {
+ Elf64_Ehdr elf_header;
+ if (pread(fd, &elf_header, sizeof(elf_header), 0) != sizeof(elf_header))
+ return 1;
+
+ if (elf_header.e_type != ET_DYN) {
+ puts("This is not a shared library.");
+ return 1;
+ }
+
+ if (elf_header.e_version != EV_CURRENT) {
+ puts("Well... at the very least, the naming here is poor.");
+ return 1;
+ }
+
+
+
+ Elf64_Shdr *section_headers;
+ section_headers = calloc(sizeof(*section_headers), elf_header.e_shnum);
+ if (!section_headers && elf_header.e_shnum != 0) {
+ puts("OOM.");
+ return 1;
+ }
+
+ if (elf_header.e_shentsize != sizeof(*section_headers)) {
+ puts("Your ELF file seems wrong.");
+ return 1;
+ }
+
+ size_t shnum = elf_header.e_shnum;
+ for (size_t i = 0; i < shnum; i++) {
+ size_t remaining = elf_header.e_shentsize;
+ size_t got = 0;
+ ssize_t res;
+ do {
+ res = pread(fd, ((char*)&(section_headers[i]))+got, remaining, elf_header.e_shoff + (elf_header.e_shentsize * i) + got);
+ if (res < 0) {
+ puts("Failed to read ELF section headers");
+ return 1;
+ }
+ got += res;
+ remaining -= res;
+ } while (remaining != 0);
+ }
+
+
+ size_t shstrtab_index = elf_header.e_shstrndx;
+ if (shstrtab_index == 0xFFFF) {
+ if (shnum > 0)
+ shstrtab_index = section_headers[0].sh_link;
+ else
+ shstrtab_index = SHN_UNDEF;
+ }
+ if (shstrtab_index == SHN_UNDEF || shstrtab_index >= shnum) {
+ puts("Failed to find .shstrtab");
+ return 1;
+ }
+
+ char *shstrtab = malloc(section_headers[shstrtab_index].sh_size);
+ if (!shstrtab) {
+ puts("OOM.");
+ return 1;
+ }
+ {
+ size_t remaining = section_headers[shstrtab_index].sh_size;
+ size_t got = 0;
+ ssize_t res;
+ do {
+ res = pread(fd, shstrtab + got, remaining, section_headers[shstrtab_index].sh_offset + got);
+ if (res < 0) {
+ puts("Failed to real shstrtab");
+ return 1;
+ }
+ got += res;
+ remaining -= res;
+ } while (remaining != 0);
+ }
+ printf("shstrtab size: %d\n", section_headers[shstrtab_index].sh_size);
+
+
+
+ Elf64_Phdr *program_headers;
+ program_headers = calloc(sizeof(*program_headers), elf_header.e_phnum);
+ if (!program_headers && elf_header.e_phnum != 0) {
+ puts("OOM.");
+ return 1;
+ }
+
+ if (elf_header.e_phentsize != sizeof(*program_headers)) {
+ puts("Your ELF file seems wrong.");
+ return 1;
+ }
+
+ size_t phnum = elf_header.e_phnum;
+ if (phnum >= PN_XNUM) {
+ if (shnum == 0) {
+ puts("Your ELF file is broken.");
+ return 1;
+ }
+ }
+
+ for (size_t i = 0; i < phnum; i++) {
+ size_t remaining = elf_header.e_phentsize;
+ size_t got = 0;
+ ssize_t res;
+ do {
+ res = pread(fd, ((char*)&(program_headers[i]))+got, remaining, elf_header.e_phoff + (elf_header.e_phentsize * i) + got);
+ if (res < 0) {
+ puts("Failed to read ELF section headers");
+ return 1;
+ }
+ got += res;
+ remaining -= res;
+ } while (remaining != 0);
+
+ printf("loaded phent at %lu\n", elf_header.e_phoff + (elf_header.e_phentsize * i));
+ }
+
+
+
+ unsigned char __attribute__((__may_alias__)) **loaded_segments;
+ loaded_segments = malloc(phnum * sizeof(*loaded_segments));
+ if (!loaded_segments && phnum != 0) {
+ puts("OOM!");
+ return 1;
+ }
+
+ size_t dynamic_library_needed_entries = 0;
+
+ for (size_t i = 0; i < phnum; i++) {
+ switch (program_headers[i].p_type) {
+ case PT_DYNAMIC:
+ if (program_headers[i].p_memsz % sizeof(Elf64_Dyn) != 0) {
+ puts("Broken PT_DYNAMIC section");
+ return 1;
+ }
+ case PT_LOAD:
+ size_t;
+ size_t pre_padding;
+ if (program_headers[i].p_align == 0)
+ pre_padding = 0;
+ else
+ pre_padding = program_headers[i].p_offset % program_headers[i].p_align;
+
+ loaded_segments[i] = mmap(0, pre_padding + program_headers[i].p_memsz, PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ if (!loaded_segments[i]) {
+ puts("OOM!");
+ return 1;
+ }
+ printf("Mapped new segment of %lu length at %p\n", pre_padding + program_headers[i].p_memsz, loaded_segments[i]);
+ size_t offset = 0;
+ size_t remaining = program_headers[i].p_filesz;
+ do {
+ ssize_t res = pread(fd, &(loaded_segments[i][offset + pre_padding]), remaining, program_headers[i].p_offset + offset);
+ if (res < 0) {
+ puts("pread failed");
+ printf("errno: %d\n", errno);
+ return 1;
+ }
+ offset += res;
+ remaining -= res;
+ } while (remaining != 0);
+
+ if (program_headers[i].p_type == PT_DYNAMIC) {
+ Elf64_Dyn __attribute__((__may_alias__)) *dynamic = (Elf64_Dyn*)loaded_segments[i];
+ for (size_t x = 0; x < program_headers[i].p_memsz / sizeof(*dynamic); x++) {
+ if (dynamic[x].d_tag == DT_NEEDED) {
+ dynamic_library_needed_entries++;
+ }
+ }
+ }
+
+ break;
+ default:
+ break;
+ }
+ }
+
+ char *dynstr;
+ char dynstr_found = 0;
+
+ Elf64_Dyn *dynsym;
+ char dynsym_found = 0;
+
+ for (size_t i = 0; i < shnum; i++) {
+ switch (section_headers[i].sh_type) {
+ case SHT_STRTAB:
+ if (strcmp(&(shstrtab[section_headers[i].sh_name]), ".dynstr") == 0) {
+ if (dynstr_found) {
+ puts("Extra .dynstr found!");
+ return 1;
+ }
+ dynstr_found = 1;
+ // TODO: Translate offset to actual address
+ printf(".dynstr: %p\n", section_headers[i].sh_addr);
+ }
+ printf("STRTAB: %p\n", section_headers[i].sh_addr);
+ break;
+ case SHT_RELA:
+ printf("RELA: %p\n", section_headers[i].sh_addr);
+ break;
+ case SHT_DYNSYM:
+ if (dynsym_found) {
+ puts("Extra DYNSYM found!");
+ return 1;
+ }
+ dynsym_found = 1;
+ // TODO: Translate offset to actual address
+ printf("DYNSYM: %p\n", section_headers[i].sh_addr);
+ break;
+ default:
+ break;
+ }
+ }
+ if (!dynstr_found) {
+ puts("No .dynstr found!");
+ return 1;
+ }
+
+ printf("Got %lu dynamic libraries required\n", dynamic_library_needed_entries);
+
+ void ** dls;
+ dls = malloc(dynamic_library_needed_entries * sizeof(*dls));
+
+ {
+ size_t dls_done = 0;
+ for (size_t i = 0; i < phnum; i++) {
+ if (program_headers[i].p_type == PT_DYNAMIC) {
+ Elf64_Dyn __attribute__((__may_alias__)) *dynamic = (Elf64_Dyn*)loaded_segments[i];
+ for (size_t x = 0; x < program_headers[i].p_memsz / sizeof(*dynamic); x++) {
+ if (dynamic[x].d_tag == DT_NEEDED) {
+ }
+ }
+ }
+ }
+ }
+
+ return 0;
+}