Luna/kernel/src/sys/elf/ELFLoader.cpp
apio dc389da74e Implement an ELFImage struct
This struct allows us to keep track of what memory is used by the loaded executable. For some reason, freeing this memory when the task exits triggers a kernel page fault, so I'm not doing that right now.
2022-10-07 17:54:05 +02:00

104 lines
3.4 KiB
C++

#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/stdlib.h"
#include "std/string.h"
#include "sys/elf/ELF.h"
static const char* format_permissions(uint32_t flags)
{
static char perms[4];
perms[0] = (flags & 4) > 0 ? 'r' : '-';
perms[1] = (flags & 2) > 0 ? 'w' : '-';
perms[2] = (flags & 1) > 0 ? 'x' : '-';
perms[3] = 0;
return perms;
}
ELFImage* 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);
}
ELFImage* 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;
}
ELFImage* image = (ELFImage*)kmalloc(sizeof(ELFImage) - sizeof(ELFSection));
memset(image, 0, sizeof(ELFImage) - sizeof(ELFSection));
image->entry = elf_ehdr->e_entry;
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 address %lx, file size %ld, mem size %ld, permissions %s",
phdr->p_vaddr, phdr->p_filesz, phdr->p_memsz, format_permissions(phdr->p_flags));
if (!phdr->p_vaddr)
{
kerrorln("Address is NULL, this is invalid :(");
return 0;
}
uint64_t 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);
image = (ELFImage*)krealloc(image, (sizeof(ELFImage) - sizeof(ELFSection)) +
(image->section_count + 1) * sizeof(ELFSection));
ELFSection& section = image->sections[image->section_count];
section.base = (uintptr_t)buffer;
section.pages = pages;
image->section_count++;
}
else { kdbgln("skipping non-loadable segment"); }
}
if (!image->section_count)
{
kfree(image);
return 0;
}
return image;
}