major refactorings
Signed-off-by: kaguya3311 <kaguya3311@national.shitposting.agency>
This commit is contained in:
+442
-184
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user