diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rwxr-xr-x | loader.c | 298 |
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; +} |