From 6e6cf5b2b079b72a3d46ab8545e0cc3a45ffde7c Mon Sep 17 00:00:00 2001 From: apio Date: Sat, 1 Oct 2022 12:15:56 +0200 Subject: [PATCH] Add an ELF Loader!! --- kernel/include/sys/elf/ELF.h | 48 ++++++++++++++++++ kernel/include/sys/elf/ELFLoader.h | 8 +++ kernel/src/sys/elf/ELFLoader.cpp | 79 ++++++++++++++++++++++++++++++ 3 files changed, 135 insertions(+) create mode 100644 kernel/include/sys/elf/ELF.h create mode 100644 kernel/include/sys/elf/ELFLoader.h create mode 100644 kernel/src/sys/elf/ELFLoader.cpp diff --git a/kernel/include/sys/elf/ELF.h b/kernel/include/sys/elf/ELF.h new file mode 100644 index 00000000..711bd3bd --- /dev/null +++ b/kernel/include/sys/elf/ELF.h @@ -0,0 +1,48 @@ +#pragma once + +#define ELFMAG "\177ELF" +#define SELFMAG 4 +#define EI_CLASS 4 /* File class byte index */ +#define ELFCLASS64 2 /* 64-bit objects */ +#define EI_DATA 5 /* Data encoding byte index */ +#define ELFDATA2LSB 1 /* 2's complement, little endian */ +#define ET_EXEC 2 /* Executable file */ +#define PT_LOAD 1 /* Loadable program segment */ +#ifdef __x86_64__ +#define EM_MACH 62 /* AMD x86-64 architecture */ +#endif +#ifdef __aarch64__ +#define EM_MACH 183 /* ARM aarch64 architecture */ +#endif + +#include + +typedef struct +{ + uint8_t e_ident[16]; /* Magic number and other info */ + uint16_t e_type; /* Object file type */ + uint16_t e_machine; /* Architecture */ + uint32_t e_version; /* Object file version */ + uint64_t e_entry; /* Entry point virtual address */ + uint64_t e_phoff; /* Program header table file offset */ + uint64_t e_shoff; /* Section header table file offset */ + uint32_t e_flags; /* Processor-specific flags */ + uint16_t e_ehsize; /* ELF header size in bytes */ + uint16_t e_phentsize; /* Program header table entry size */ + uint16_t e_phnum; /* Program header table entry count */ + uint16_t e_shentsize; /* Section header table entry size */ + uint16_t e_shnum; /* Section header table entry count */ + uint16_t e_shstrndx; /* Section header string table index */ +} Elf64_Ehdr; + +typedef struct +{ + uint32_t p_type; /* Segment type */ + uint32_t p_flags; /* Segment flags */ + uint64_t p_offset; /* Segment file offset */ + uint64_t p_vaddr; /* Segment virtual address */ + uint64_t p_paddr; /* Segment physical address */ + uint64_t p_filesz; /* Segment size in file */ + uint64_t p_memsz; /* Segment size in memory */ + uint64_t p_align; /* Segment alignment */ +} Elf64_Phdr; \ No newline at end of file diff --git a/kernel/include/sys/elf/ELFLoader.h b/kernel/include/sys/elf/ELFLoader.h new file mode 100644 index 00000000..2a0095d5 --- /dev/null +++ b/kernel/include/sys/elf/ELFLoader.h @@ -0,0 +1,8 @@ +#pragma once +#include + +namespace ELFLoader +{ + void* load_elf_from_address(uintptr_t addr); + void* load_elf_from_initrd(const char* filename); +} \ No newline at end of file diff --git a/kernel/src/sys/elf/ELFLoader.cpp b/kernel/src/sys/elf/ELFLoader.cpp new file mode 100644 index 00000000..bdb082f4 --- /dev/null +++ b/kernel/src/sys/elf/ELFLoader.cpp @@ -0,0 +1,79 @@ +#define MODULE "elf" + +#include "sys/elf/ELFLoader.h" +#include "init/InitRD.h" +#include "log/Log.h" +#include "memory/MemoryManager.h" +#include "misc/utils.h" +#include "std/string.h" +#include "sys/elf/ELF.h" + +void* ELFLoader::load_elf_from_initrd(const char* filename) +{ + InitRD::File elf_file = InitRD::open(filename); + if (!elf_file.addr) + { + kwarnln("failed to open file %s for loading", filename); + return 0; + } + + return load_elf_from_address((uintptr_t)elf_file.addr); +} + +void* ELFLoader::load_elf_from_address(uintptr_t addr) +{ + Elf64_Ehdr* elf_ehdr = (Elf64_Ehdr*)addr; + if (strncmp((const char*)elf_ehdr->e_ident, ELFMAG, SELFMAG) != 0) + { + kwarnln("ELF file has invalid magic, skipping"); + return 0; + } + if (elf_ehdr->e_ident[EI_CLASS] != ELFCLASS64) + { + kwarnln("ELF file is not ELF64, skipping"); + return 0; + } + if (elf_ehdr->e_ident[EI_DATA] != ELFDATA2LSB) + { + kwarnln("ELF file is not little-endian, skipping"); + return 0; + } + if (elf_ehdr->e_type != ET_EXEC) + { + kwarnln("not supported: ELF file is not an executable"); + return 0; + } + if (elf_ehdr->e_machine != EM_MACH) + { + kwarnln("unsupported target machine"); + return 0; + } + if (elf_ehdr->e_phnum == 0) + { + kwarnln("ELF file has no PHDRS"); + return 0; + } + int i; + Elf64_Phdr* phdr; + for (phdr = (Elf64_Phdr*)((uint64_t)addr + elf_ehdr->e_phoff), i = 0; i < elf_ehdr->e_phnum; + i++, phdr = (Elf64_Phdr*)((uint8_t*)phdr + elf_ehdr->e_phentsize)) + { + if (phdr->p_type == PT_LOAD) + { + kdbgln("loading loadable segment at vaddr %lx, filesz %ld, memsz %ld", phdr->p_vaddr, phdr->p_filesz, + phdr->p_memsz); + if (!phdr->p_vaddr) + { + kerrorln("vaddr is NULL, this is invalid :("); + return 0; + } + int pages = Utilities::get_blocks_from_size(0x1000, phdr->p_memsz); + void* buffer = MemoryManager::get_pages_at(phdr->p_vaddr, pages, + phdr->p_flags & 2 ? MAP_READ_WRITE | MAP_USER : MAP_USER); + memcpy(buffer, (void*)(addr + phdr->p_offset), phdr->p_filesz); + memset((void*)((uint64_t)buffer + phdr->p_filesz), 0, phdr->p_memsz - phdr->p_filesz); + } + else { kdbgln("skipping non-loadable segment"); } + } + return (void*)elf_ehdr->e_entry; +} \ No newline at end of file