2022-10-01 12:15:56 +02:00
|
|
|
#define MODULE "elf"
|
|
|
|
|
|
|
|
#include "sys/elf/ELFLoader.h"
|
2022-10-11 19:33:48 +02:00
|
|
|
#include "fs/VFS.h"
|
2022-10-01 12:15:56 +02:00
|
|
|
#include "init/InitRD.h"
|
|
|
|
#include "log/Log.h"
|
|
|
|
#include "memory/MemoryManager.h"
|
|
|
|
#include "misc/utils.h"
|
2022-10-07 17:54:05 +02:00
|
|
|
#include "std/stdlib.h"
|
2022-10-01 12:15:56 +02:00
|
|
|
#include "std/string.h"
|
|
|
|
#include "sys/elf/ELF.h"
|
|
|
|
|
2022-10-02 17:10:24 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2022-10-11 19:33:48 +02:00
|
|
|
ELFImage* ELFLoader::load_elf_from_filesystem(const char* filename)
|
2022-10-01 12:15:56 +02:00
|
|
|
{
|
2022-10-11 19:33:48 +02:00
|
|
|
VFS::Node* node = VFS::resolve_path(filename);
|
|
|
|
|
|
|
|
if (!node)
|
2022-10-01 12:15:56 +02:00
|
|
|
{
|
2022-10-02 17:10:24 +02:00
|
|
|
kwarnln("Failed to open file %s for loading", filename);
|
2022-10-01 12:15:56 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-10-11 19:33:48 +02:00
|
|
|
if (node->type == VFS_DIRECTORY)
|
|
|
|
{
|
|
|
|
kwarnln("Failed to load %s: is a directory", filename);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void* file = kmalloc(node->length);
|
|
|
|
if (VFS::read(node, 0, node->length, (char*)file) < 0)
|
|
|
|
{
|
|
|
|
kwarnln("Failed to read ELF image from file");
|
|
|
|
kfree(file);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ELFImage* result =
|
|
|
|
load_elf_from_address((uintptr_t)file); // FIXME: Read headers and sections as we go along the file, to avoid
|
|
|
|
// loading the entire file at once into memory.
|
|
|
|
|
|
|
|
kfree(file);
|
|
|
|
|
|
|
|
return result;
|
2022-10-01 12:15:56 +02:00
|
|
|
}
|
|
|
|
|
2022-10-07 17:54:05 +02:00
|
|
|
ELFImage* ELFLoader::load_elf_from_address(uintptr_t addr)
|
2022-10-01 12:15:56 +02:00
|
|
|
{
|
|
|
|
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)
|
|
|
|
{
|
2022-10-02 17:10:24 +02:00
|
|
|
kwarnln("Unsupported target machine");
|
2022-10-01 12:15:56 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (elf_ehdr->e_phnum == 0)
|
|
|
|
{
|
|
|
|
kwarnln("ELF file has no PHDRS");
|
|
|
|
return 0;
|
|
|
|
}
|
2022-10-07 17:54:05 +02:00
|
|
|
ELFImage* image = (ELFImage*)kmalloc(sizeof(ELFImage) - sizeof(ELFSection));
|
|
|
|
memset(image, 0, sizeof(ELFImage) - sizeof(ELFSection));
|
|
|
|
image->entry = elf_ehdr->e_entry;
|
2022-10-01 12:15:56 +02:00
|
|
|
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)
|
|
|
|
{
|
2022-10-02 17:10:24 +02:00
|
|
|
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));
|
2022-10-01 12:15:56 +02:00
|
|
|
if (!phdr->p_vaddr)
|
|
|
|
{
|
2022-10-02 17:10:24 +02:00
|
|
|
kerrorln("Address is NULL, this is invalid :(");
|
2022-10-01 12:15:56 +02:00
|
|
|
return 0;
|
|
|
|
}
|
2022-10-12 14:40:06 +02:00
|
|
|
uint64_t pages = Utilities::get_blocks_from_size(PAGE_SIZE, (phdr->p_vaddr % PAGE_SIZE) + phdr->p_memsz);
|
2022-10-12 17:08:17 +02:00
|
|
|
void* buffer = (void*)((uint64_t)MemoryManager::get_pages_at(
|
|
|
|
Utilities::round_down_to_nearest_page(phdr->p_vaddr), pages,
|
|
|
|
phdr->p_flags & 2 ? MAP_READ_WRITE | MAP_USER : MAP_USER) +
|
|
|
|
(phdr->p_vaddr % PAGE_SIZE));
|
2022-10-01 12:15:56 +02:00
|
|
|
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);
|
2022-10-07 17:54:05 +02:00
|
|
|
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++;
|
2022-10-01 12:15:56 +02:00
|
|
|
}
|
|
|
|
else { kdbgln("skipping non-loadable segment"); }
|
|
|
|
}
|
2022-10-07 17:54:05 +02:00
|
|
|
if (!image->section_count)
|
|
|
|
{
|
|
|
|
kfree(image);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return image;
|
2022-10-12 17:08:17 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void ELFLoader::release_elf_image(ELFImage* image)
|
|
|
|
{
|
|
|
|
for (uint64_t i = 0; i < image->section_count; i++)
|
|
|
|
{
|
|
|
|
ELFSection& section = image->sections[i];
|
|
|
|
kdbgln("Freeing up section %lx, was using %ld pages", section.base, section.pages);
|
|
|
|
MemoryManager::release_pages((void*)Utilities::round_down_to_nearest_page(section.base), section.pages);
|
|
|
|
}
|
|
|
|
kfree(image);
|
2022-10-01 12:15:56 +02:00
|
|
|
}
|