major refactorings

Signed-off-by: kaguya3311 <kaguya3311@national.shitposting.agency>
This commit is contained in:
kaguya
2026-05-18 04:02:59 -04:00
parent f7aa6f913a
commit b28a6bcf29
211 changed files with 17699 additions and 8107 deletions
+442 -184
View File
@@ -1,231 +1,489 @@
// elf.c (now extracts AT_PHDR / AT_PHENT / AT_PHNUM + minor cleanups)
#include "elf.h"
#include "libk/stdio.h"
#include "libk/string.h"
#include "mm/pmm.h"
#include "mm/vmm.h"
#include "mm/memory.h"
#include "fs/ext2.h"
#include "libk/debug.h"
#include "libk/module.h"
#include "fs/vfs.h"
#include "mm/mmap.h"
#include "libk/resource.h"
#include "libk/misc.h"
extern uintptr_t g_hhdm_offset;
// static struct function_symbol *name_to_function = NULL;
static struct function_symbol *function_to_name = NULL;
static size_t function_table_size = 0;
bool symbol_table_initialised = false;
bool ELF_Read(const char* path,
void** entryPoint,
struct pagemap *target_pagemap,
uint64_t *out_tls_fs_base,
uint64_t *out_phdr_va,
uint16_t *out_phent,
uint16_t *out_phnum)
{
*out_tls_fs_base = 0;
*out_phdr_va = 0;
*out_phent = 0;
*out_phnum = 0;
module_list_t modules_list = {0};
/*
static void simple_append_name(const char *string, uint64_t address) {
size_t index = hash(string, strlen(string)) % function_table_size;
name_to_function[index].address = address;
name_to_function[index].name = string;
}
*/
const char *elf_get_name_from_function(uint64_t address) {
if (!symbol_table_initialised)
return "";
uint32_t inum = ext2_resolve_path(path);
if (!inum) {
printf("ELF: file not found: %s\n", path);
return false;
}
address -= KERNEL_BASE;
address += 0xffffffff80000000;
ext2_inode_t inode;
if (!ext2_read_inode(inum, &inode)) {
printf("ELF: failed to read inode\n");
return false;
}
// I HATE HASH COLLISIONS
// I HATE HASH COLLISIONS
for (size_t i = 0; i < function_table_size; i++) {
if (function_to_name[i].address == address)
return function_to_name[i].name;
}
uint64_t file_size = inode.i_size;
if (file_size < sizeof(ELFHeader)) {
printf("ELF: file too small\n");
return false;
}
return "UNKNOWN";
}
uint64_t buf_pages = ALIGN_UP(file_size, PAGE_SIZE) / PAGE_SIZE;
void* buffer_phys = pmm_allocz(buf_pages);
if (!buffer_phys) {
printf("ELF: failed to allocate %lu pages for file buffer\n", buf_pages);
return false;
}
uint64_t elf_get_function_from_name(const char *string) {
if (!symbol_table_initialised || !string)
return (uint64_t)NULL;
uint8_t* elf_buffer = (uint8_t*)((uintptr_t)buffer_phys + MEM_PHYS_OFFSET);
// size_t index = hash(string, strlen(string)) % function_table_size;
// uint64_t address = name_to_function[index].address;
if (!ext2_read_file(&inode, elf_buffer)) {
pmm_free(buffer_phys, buf_pages);
return false;
}
// I HATE HASH COLLISIONS
// I HATE HASH COLLISIONS
uint64_t address = 0;
for (size_t i = 0; i < function_table_size; i++) {
if (!strcmp(function_to_name[i].name, string)) {
address = function_to_name[i].address;
break;
}
}
ELFHeader* header = (ELFHeader*)elf_buffer;
if (address) {
address -= 0xffffffff80000000;
address += KERNEL_BASE;
}
printf("=== ELF DEBUG ===\n"
"Entry=0x%lx PHDR@0x%lx count=%u type=0x%x arch=0x%x\n"
"=== END ===\n",
header->ProgramEntryPosition,
header->ProgramHeaderTablePosition,
header->ProgramHeaderTableEntryCount,
header->Type,
header->InstructionSet);
return address;
}
if (memcmp(header->Magic, ELF_MAGIC, 4) != 0 ||
header->Bitness != ELF_BITNESS_64BIT ||
header->Endianness != ELF_ENDIANNESS_LITTLE ||
(header->Type != ELF_TYPE_EXECUTABLE && header->Type != ELF_TYPE_SHARED) ||
header->InstructionSet != ELF_INSTRUCTION_SET_X64) {
void elf_init_function_table(uint8_t *binary) {
vec_init(&modules_list);
printf("ELF: unsupported/invalid header\n");
goto cleanup;
}
Elf64_Ehdr *ehdr = (Elf64_Ehdr *)binary;
*entryPoint = (void*)header->ProgramEntryPosition;
Elf64_Shdr *elf_section_headers = (Elf64_Shdr *)(binary + ehdr->e_shoff);
// ------------------------------------------------------------------
// Parse program headers LOAD, TLS, and PHDR
// ------------------------------------------------------------------
uint64_t tls_vaddr = 0, tls_filesz = 0, tls_memsz = 0, tls_align = 8;
uint8_t* tls_src = NULL;
uint64_t phdr_vaddr = 0;
Elf64_Shdr *symtab = NULL;
char *strtab = NULL;
uint8_t* ph_table = elf_buffer + header->ProgramHeaderTablePosition;
uint64_t phdr_table_end = header->ProgramHeaderTablePosition +
(uint64_t)header->ProgramHeaderTableEntryCount *
header->ProgramHeaderTableEntrySize;
for (int i = 0; i < ehdr->e_shnum; i++) {
if (elf_section_headers[i].sh_type == SHT_SYMTAB) {
symtab = &elf_section_headers[i];
strtab = (char *)((uintptr_t)binary +
elf_section_headers[symtab->sh_link].sh_offset);
break;
}
}
if (phdr_table_end > file_size) {
printf("ELF: program header table extends beyond file\n");
goto cleanup;
}
if (symtab != NULL) {
Elf64_Sym *sym_entries = (Elf64_Sym *)(binary + symtab->sh_offset);
size_t num_symbols = symtab->sh_size / symtab->sh_entsize;
for (uint32_t i = 0; i < header->ProgramHeaderTableEntryCount; i++) {
ELFProgramHeader* ph = (ELFProgramHeader*)(ph_table + i * header->ProgramHeaderTableEntrySize);
function_table_size = 0;
// name_to_function =
// kmalloc(sizeof(struct function_symbol) * num_symbols);
function_to_name =
kmalloc(sizeof(struct function_symbol) * num_symbols);
// PT_PHDR
if (ph->Type == ELF_PROGRAM_TYPE_PHDR) {
phdr_vaddr = ph->VirtualAddress;
printf("ELF: Found PT_PHDR VA=0x%lx\n", phdr_vaddr);
// fall through
}
for (size_t i = 0; i < num_symbols; i++) {
if ((ELF64_ST_TYPE(sym_entries[i].st_info) == STT_OBJECT) &&
ELF64_ST_BIND(sym_entries[i].st_info) == STB_GLOBAL) {
if (((uint64_t)strtab + sym_entries[i].st_name)) {
char *dupped = strdup(
(char *)((uint64_t)strtab + sym_entries[i].st_name));
// PT_TLS
if (ph->Type == ELF_PROGRAM_TYPE_TLS) {
tls_vaddr = ph->VirtualAddress;
tls_filesz = ph->FileSize;
tls_memsz = ph->MemorySize;
tls_align = ph->Align ? ph->Align : 8;
tls_src = elf_buffer + ph->Offset;
function_to_name[function_table_size].address =
sym_entries[i].st_value;
function_to_name[function_table_size++].name = dupped;
}
}
if ((ELF64_ST_TYPE(sym_entries[i].st_info) == STT_FUNC) &&
ELF64_ST_BIND(sym_entries[i].st_info) == STB_GLOBAL) {
if (ph->Offset + ph->FileSize > file_size) {
printf("ELF: PT_TLS segment data out of file bounds\n");
goto cleanup;
}
// We need a better hash function for the addresses since there
// are hash collisions. For now we are using this slow way.
if (((uint64_t)strtab + sym_entries[i].st_name)) {
char *dupped = strdup(
(char *)((uint64_t)strtab + sym_entries[i].st_name));
printf("ELF: Found PT_TLS VA=0x%lx FileSz=0x%lx MemSz=0x%lx Align=0x%lx\n",
tls_vaddr, tls_filesz, tls_memsz, tls_align);
continue;
}
function_to_name[function_table_size].address =
sym_entries[i].st_value;
function_to_name[function_table_size++].name = dupped;
if (ph->Type != ELF_PROGRAM_TYPE_LOAD || ph->MemorySize == 0)
continue;
// simple_append_name(dupped, sym_entries[i].st_value);
}
}
}
symbol_table_initialised = true;
}
}
if (ph->Offset + ph->FileSize > file_size) {
printf("ELF: PT_LOAD segment data out of file bounds\n");
goto cleanup;
}
uint64_t module_load(const char *path) {
struct vfs_node *node = vfs_get_node(vfs_root, path, true);
if (!node) {
kprintf("Module not found\n");
return 1;
}
// Map with exact permissions
uint64_t map_flags = PAGE_USER;
if (ph->Flags & PF_R) map_flags |= PAGE_READ;
if (ph->Flags & PF_W) map_flags |= PAGE_WRITE;
if (!(ph->Flags & PF_X)) map_flags |= PAGE_NO_EXECUTE;
struct resource *res = node->resource;
uint64_t virt = ph->VirtualAddress;
uint64_t aligned_virt = ALIGN_DOWN(virt, PAGE_SIZE);
uint64_t page_offset = virt & (PAGE_SIZE - 1);
uint64_t aligned_memsz = ALIGN_UP(ph->MemorySize + page_offset, PAGE_SIZE);
uint64_t pages = aligned_memsz / PAGE_SIZE;
struct module *m = kmalloc(sizeof(struct module));
memzero(m, sizeof(struct module));
strncpy(m->name, path, sizeof(m->name));
void* seg_phys = pmm_allocz(pages);
if (!seg_phys) {
printf("ELF: failed to allocate physical pages for LOAD segment\n");
goto cleanup;
}
Elf64_Ehdr *ehdr = kmalloc(sizeof(Elf64_Ehdr));
for (uint64_t p = 0; p < pages; p++) {
if (!vmm_map_page(target_pagemap,
aligned_virt + p * PAGE_SIZE,
(uintptr_t)seg_phys + p * PAGE_SIZE,
map_flags,
Size4KiB)) {
pmm_free(seg_phys, pages);
goto cleanup;
}
}
res->read(res, NULL, ehdr, 0, sizeof(Elf64_Ehdr));
uint8_t* dst = (uint8_t*)((uintptr_t)seg_phys + MEM_PHYS_OFFSET);
memcpy(dst + page_offset, elf_buffer + ph->Offset, ph->FileSize);
if (ph->MemorySize > ph->FileSize) {
memset(dst + page_offset + ph->FileSize, 0,
ph->MemorySize - ph->FileSize);
}
}
if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
kprintf("ELF header check fail\n");
return 1;
}
if (ehdr->e_ident[EI_CLASS] != ELFCLASS64 ||
ehdr->e_ident[EI_DATA] != ELFDATA2LSB || ehdr->e_ident[EI_OSABI] != 0 ||
ehdr->e_machine != EM_X86_64) {
kprintf("This ELF isn't for us\n");
return 1;
}
uint64_t tls_size = tls_memsz ? ALIGN_UP(tls_memsz, tls_align) : 0ULL;
if (ehdr->e_type != 1) {
kprintf("This ELF isn't executable\n");
return 1;
}
uint64_t tcb_va = TLS_BASE_VA + PAGE_SIZE;
uint64_t tls_va = tcb_va - tls_size;
if (ehdr->e_shentsize != sizeof(Elf64_Shdr)) {
kprintf("Malformed ELF?\n");
return 1;
}
uint64_t page_va = ALIGN_DOWN(tls_va, PAGE_SIZE);
uint64_t tcb_size = sizeof(TCB);
uint64_t block_end_va = tcb_va + tcb_size;
uint64_t block_end_page = ALIGN_UP(block_end_va, PAGE_SIZE);
uint64_t map_pages = ((block_end_page - page_va) / PAGE_SIZE) + 8;
if (map_pages == 0) map_pages = 1;
// Setup variable for the section headers
size_t section_count = ehdr->e_shnum;
size_t section_header_size = sizeof(Elf64_Shdr);
Elf64_Shdr *elf_section_headers =
kmalloc(section_header_size * section_count);
res->read(res, NULL, elf_section_headers, ehdr->e_shoff,
section_header_size * section_count);
void* tls_phys = pmm_allocz(map_pages);
if (!tls_phys) {
printf("ELF: failed to allocate TLS/TCB pages\n");
goto cleanup;
}
for (size_t index = 0; index < section_count; index++) {
Elf64_Shdr *section = (elf_section_headers + index);
uint64_t tls_map_flags = PAGE_USER | PAGE_READ | PAGE_WRITE;
for (uint64_t p = 0; p < map_pages; p++) {
if (!vmm_map_page(target_pagemap,
page_va + p * PAGE_SIZE,
(uintptr_t)tls_phys + p * PAGE_SIZE,
tls_map_flags,
Size4KiB)) {
pmm_free(tls_phys, map_pages);
goto cleanup;
}
}
// If this section should allocate memory and is bigger than 0
if ((section->sh_flags & SHF_ALLOC) && section->sh_size > 0) {
// Allocate memory, currenty RW so we can write to it
char *mem = (char *)((uint64_t)pmm_allocz(
1 + (section->sh_size / PAGE_SIZE)) +
MEM_PHYS_OFFSET);
uint8_t* base_hhdm = (uint8_t*)((uintptr_t)tls_phys + MEM_PHYS_OFFSET);
m->mappings[m->mappings_count].addr = (uint64_t)(size_t)mem;
m->mappings[m->mappings_count++].size = section->sh_size;
if (tls_size > 0) {
uint8_t* tls_dst = base_hhdm + (tls_va - page_va);
if (tls_filesz) memcpy(tls_dst, tls_src, tls_filesz);
if (tls_memsz > tls_filesz)
memset(tls_dst + tls_filesz, 0, tls_memsz - tls_filesz);
}
if (section->sh_type == SHT_PROGBITS) {
// Read data from the file
res->read(res, NULL, mem, section->sh_offset, section->sh_size);
} else if (section->sh_type == SHT_NOBITS) {
// Section is empty, so fill with zeros
memzero(mem, section->sh_size);
}
section->sh_addr = (uintptr_t)mem;
}
TCB* tcb = (TCB*)(base_hhdm + (tcb_va - page_va));
memset(tcb, 0, sizeof(TCB));
tcb->self = (void*)tcb_va;
tcb->tid = 1;
// Load symbol and string tables from the file
if (section->sh_type == SHT_SYMTAB || section->sh_type == SHT_STRTAB) {
Elf64_Sym *table = kmalloc(section->sh_size);
*out_tls_fs_base = tcb_va;
res->read(res, NULL, table, section->sh_offset, section->sh_size);
*out_phdr_va = tls_vaddr ? tls_vaddr : phdr_vaddr;
*out_phent = header->ProgramHeaderTableEntrySize;
*out_phnum = header->ProgramHeaderTableEntryCount;
section->sh_addr = (uintptr_t)table;
}
}
printf("ELF: TLS/TCB setup complete TCB@0x%lx TLS@0x%lx FS=0x%lx\n"
" PHDR@0x%lx PHENT=0x%x PHNUM=%u\n",
tcb_va, tls_va, tcb_va, *out_phdr_va, *out_phent, *out_phnum);
for (size_t index = 0; index < section_count; index++) {
Elf64_Shdr *section = (elf_section_headers + index);
pmm_free(buffer_phys, buf_pages);
return true;
if (section->sh_type == SHT_RELA) {
size_t entry_count = section->sh_size / section->sh_entsize;
if (section->sh_entsize != sizeof(Elf64_Rela)) {
return 1;
}
cleanup:
pmm_free(buffer_phys, buf_pages);
return false;
}
// Load relocation entries from the file
Elf64_Rela *entries = kmalloc(section->sh_size);
res->read(res, NULL, entries, section->sh_offset, section->sh_size);
section->sh_addr = (uintptr_t)entries;
// Locate the section we are relocating
Elf64_Shdr *relocation_section =
(Elf64_Shdr *)(elf_section_headers + section->sh_info);
char *relocation_section_data = (char *)relocation_section->sh_addr;
// Locate the symbol table for this relocation table
Elf64_Shdr *section_symbol_table =
(elf_section_headers + section->sh_link);
Elf64_Sym *symbol_table =
(Elf64_Sym *)(section_symbol_table->sh_addr);
// Locate the string table for the symbol table
Elf64_Shdr *section_string_table =
(elf_section_headers + section_symbol_table->sh_link);
char *string_table = (char *)(section_string_table->sh_addr);
// Relocate all the entries
for (size_t entry_ind = 0; entry_ind < entry_count; entry_ind++) {
Elf64_Rela *entry = (entries + entry_ind);
// Find the symbol for this entry
Elf64_Sym *symbol = (symbol_table + ELF64_R_SYM(entry->r_info));
char *symbol_name = (string_table + symbol->st_name);
if (ELF64_R_TYPE(entry->r_info) == 1) {
// Determine the offset in the section
uint64_t *location =
(uint64_t *)((uint64_t)relocation_section_data +
entry->r_offset);
// Check that the symbol is defined in this file
if (symbol->st_shndx > 0) {
// Calculate the location of the symbol
Elf64_Shdr *symbol_section =
(elf_section_headers + symbol->st_shndx);
uint64_t symbol_value = symbol_section->sh_addr +
symbol->st_value +
entry->r_addend;
// Store the location
*location = symbol_value;
} else {
// The symbol is not defined inside the object file
// resolve using other rules
// kprintffos(0, "%s %p %s\n", symbol_name,
// elf_get_function_from_name(symbol_name),
// elf_get_name_from_function(elf_get_function_from_name(symbol_name)));
*location = elf_get_function_from_name(symbol_name);
if (*location == 0) {
// We need to add some exemptions
kprintf("Failed to find symbol %s\n", symbol_name);
return 1;
}
}
} else {
kprintf("What the fuck?\n");
return 1;
}
}
}
}
for (size_t index = 0; index < section_count; index++) {
Elf64_Shdr *section = (elf_section_headers + index);
if ((section->sh_flags & SHF_ALLOC) && section->sh_size > 0) {
// Calculate the correct permissions
int prot = PAGE_READ;
if (section->sh_flags & SHF_WRITE)
prot |= PAGE_WRITE;
m->mappings[index].prot = prot;
for (size_t i = 0; i < section->sh_size; i += PAGE_SIZE) {
vmm_map_page(kernel_pagemap, section->sh_addr,
section->sh_addr - MEM_PHYS_OFFSET, prot,
Size4KiB);
}
}
}
module_entry_t run_func = NULL;
for (size_t index = 0; index < section_count; index++) {
Elf64_Shdr *section = (elf_section_headers + index);
// Look in all symbol tables for the run function
if (section->sh_type == SHT_SYMTAB) {
Elf64_Sym *symbol_table = (Elf64_Sym *)section->sh_addr;
// Find the string table for this symbol table
Elf64_Shdr *section_string_table =
(elf_section_headers + section->sh_link);
char *string_table = (char *)(section_string_table->sh_addr);
size_t symbol_count = section->sh_size / section->sh_entsize;
for (size_t i = 0; i < symbol_count; i++) {
// Get the symbol from the table
Elf64_Sym *symbol = (symbol_table + i);
char *symbol_name = (string_table + symbol->st_name);
Elf64_Shdr *run_section =
(elf_section_headers + symbol->st_shndx);
// Check if it is the run symbol
if (strcmp("driver_entry", symbol_name) == 0 &&
((symbol->st_info >> 4) & 0xf) == 1 &&
(run_section->sh_flags & 4)) {
// Calculate the symbol location
run_func = (module_entry_t)(run_section->sh_addr +
symbol->st_value);
break;
}
}
}
if (run_func)
break;
}
kfree(ehdr);
kfree(elf_section_headers);
if (run_func != NULL) {
m->entry_point = run_func;
vec_push(&modules_list, m);
return run_func(m);
} else {
kprintf("Failed to find driver_entry\n");
return 1;
}
}
bool module_unload(const char *name) {
for (int i = 0; i < modules_list.length; i++) {
if (!strncmp(name, modules_list.data[i]->name, 128)) {
struct module *m = modules_list.data[i];
if (!m->exit)
return false;
m->exit();
for (size_t j = 0; j < m->mappings_count; j++) {
pmm_free((void *)(m->mappings[j].addr - MEM_PHYS_OFFSET),
(m->mappings[j].size / PAGE_SIZE) + 1);
for (size_t k = 0; k < m->mappings[j].size; k += PAGE_SIZE) {
vmm_unmap_page(kernel_pagemap, m->mappings[j].addr, false);
}
}
vec_remove(&modules_list, m);
kfree(m);
return true;
}
}
return false;
}
void module_dump(void) {
kprintf("Loaded drivers\n");
for (int i = 0; i < modules_list.length; i++) {
struct module *mod = modules_list.data[i];
kprintf("\t%s with entry point at %p\n", mod->name, mod->entry_point);
for (size_t j = 0; j < mod->mappings_count; j++) {
kprintf("\t\t%p - %p with protections 0b%b\n",
mod->mappings[j].addr,
mod->mappings[j].addr + mod->mappings[j].size,
mod->mappings[j].prot);
}
}
}
bool elf_load(struct pagemap *pagemap, struct resource *res, uint64_t load_base,
struct auxval *auxv, const char **ld_path) {
Elf64_Ehdr header;
if (res->read(res, NULL, &header, 0, sizeof(header)) < 0) {
return false;
}
if (memcmp(header.e_ident, ELFMAG, SELFMAG)) {
return false;
}
if (header.e_ident[EI_CLASS] != ELFCLASS64 ||
header.e_ident[EI_DATA] != ELFDATA2LSB ||
header.e_ident[EI_OSABI] != 0 || header.e_machine != EM_X86_64) {
return false;
}
for (size_t i = 0; i < header.e_phnum; i++) {
Elf64_Phdr phdr;
if (res->read(res, NULL, &phdr, header.e_phoff + i * header.e_phentsize,
sizeof(phdr)) < 0) {
goto fail;
}
switch (phdr.p_type) {
case PT_LOAD: {
int prot = PROT_READ;
if (phdr.p_flags & PF_W) {
prot |= PROT_WRITE;
}
if (phdr.p_flags & PF_X) {
prot |= PROT_EXEC;
}
size_t misalign = phdr.p_vaddr & (PAGE_SIZE - 1);
size_t page_count =
DIV_ROUNDUP(phdr.p_memsz + misalign, PAGE_SIZE);
void *phys = pmm_allocz(page_count);
if (phys == NULL) {
goto fail;
}
if (!mmap_range(pagemap, phdr.p_vaddr + load_base,
(uintptr_t)phys, page_count * PAGE_SIZE, prot,
MAP_ANONYMOUS)) {
pmm_free(phys, page_count);
goto fail;
}
if (res->read(
res, NULL,
(void *)((uintptr_t)phys + misalign + MEM_PHYS_OFFSET),
phdr.p_offset, phdr.p_filesz) < 0) {
goto fail;
}
break;
}
case PT_PHDR:
auxv->at_phdr = phdr.p_vaddr + load_base;
break;
case PT_INTERP: {
void *path = kmalloc(phdr.p_filesz + 1);
if (path == NULL) {
goto fail;
}
if (res->read(res, NULL, path, phdr.p_offset, phdr.p_filesz) <
0) {
kfree(path);
goto fail;
}
if (ld_path != NULL) {
*ld_path = path;
} else {
kfree(path);
path = NULL;
}
break;
}
}
}
auxv->at_entry = header.e_entry + load_base;
auxv->at_phent = header.e_phentsize;
auxv->at_phnum = header.e_phnum;
return true;
fail:
if (ld_path != NULL && *ld_path != NULL) {
kfree((void *)*ld_path);
}
return false;
}