From e25d91a1ba2906e30216135eea5584709bcde57b Mon Sep 17 00:00:00 2001 From: Tulio A M Mendes Date: Tue, 10 Feb 2026 02:32:41 -0300 Subject: [PATCH] refactor: move x86 ELF loader from src/kernel/elf.c to src/arch/x86/elf.c The ELF loader uses x86-specific page table manipulation (recursive mapping at 0xFFFFF000/0xFFC00000), EM_386 validation, and low-16MB allocation. It is 100% architecture-dependent and should not live in the generic kernel directory. - Move full implementation to src/arch/x86/elf.c (no #if guards) - Replace src/kernel/elf.c with a weak stub returning -1 - The linker picks the arch-specific version when available Passes: make, cppcheck, QEMU smoke test. --- src/arch/x86/elf.c | 245 ++++++++++++++++++++++++++++++++++++++++++++ src/kernel/elf.c | 247 +-------------------------------------------- 2 files changed, 246 insertions(+), 246 deletions(-) create mode 100644 src/arch/x86/elf.c diff --git a/src/arch/x86/elf.c b/src/arch/x86/elf.c new file mode 100644 index 0000000..8927517 --- /dev/null +++ b/src/arch/x86/elf.c @@ -0,0 +1,245 @@ +#include "elf.h" + +#include "fs.h" +#include "heap.h" +#include "pmm.h" +#include "uart_console.h" +#include "utils.h" +#include "vmm.h" + +#include "errno.h" + +#include "hal/cpu.h" +#include "hal/mm.h" + +#include + +static void* pmm_alloc_page_low_16mb(void) { + for (int tries = 0; tries < 4096; tries++) { + void* p = pmm_alloc_page(); + if (!p) return NULL; + if ((uintptr_t)p < 0x01000000U) { + return p; + } + pmm_free_page(p); + } + return NULL; +} + +static int elf32_validate(const elf32_ehdr_t* eh, size_t file_len) { + if (!eh) return -EFAULT; + if (file_len < sizeof(*eh)) return -EINVAL; + + if (eh->e_ident[0] != ELF_MAGIC0 || + eh->e_ident[1] != ELF_MAGIC1 || + eh->e_ident[2] != ELF_MAGIC2 || + eh->e_ident[3] != ELF_MAGIC3) { + return -EINVAL; + } + if (eh->e_ident[4] != ELFCLASS32) return -EINVAL; + if (eh->e_ident[5] != ELFDATA2LSB) return -EINVAL; + + if (eh->e_type != ET_EXEC) return -EINVAL; + if (eh->e_machine != EM_386) return -EINVAL; + + if (eh->e_phentsize != sizeof(elf32_phdr_t)) return -EINVAL; + if (eh->e_phnum == 0) return -EINVAL; + + uint32_t ph_end = eh->e_phoff + (uint32_t)eh->e_phnum * (uint32_t)sizeof(elf32_phdr_t); + if (ph_end < eh->e_phoff) return -EINVAL; + if (ph_end > file_len) return -EINVAL; + + if (eh->e_entry == 0) return -EINVAL; + if (eh->e_entry >= hal_mm_kernel_virt_base()) return -EINVAL; + + return 0; +} + +static int elf32_map_user_range(uintptr_t as, uintptr_t vaddr, size_t len, uint32_t flags) { + if (len == 0) return 0; + if (vaddr == 0) return -EINVAL; + if (vaddr >= hal_mm_kernel_virt_base()) return -EINVAL; + + uintptr_t end = vaddr + len - 1; + if (end < vaddr) return -EINVAL; + if (end >= hal_mm_kernel_virt_base()) return -EINVAL; + + uintptr_t start_page = vaddr & ~(uintptr_t)0xFFF; + uintptr_t end_page = end & ~(uintptr_t)0xFFF; + + uintptr_t old_as = hal_cpu_get_address_space(); + vmm_as_activate(as); + + for (uintptr_t va = start_page;; va += 0x1000) { + const uint32_t pdi = (uint32_t)(va >> 22); + const uint32_t pti = (uint32_t)((va >> 12) & 0x03FF); + + volatile uint32_t* pd = (volatile uint32_t*)0xFFFFF000U; + int already_mapped = 0; + if ((pd[pdi] & 1U) != 0U) { + volatile uint32_t* pt = (volatile uint32_t*)0xFFC00000U + ((uintptr_t)pdi << 10); + if ((pt[pti] & 1U) != 0U) { + already_mapped = 1; + } + } + + if (!already_mapped) { + void* phys = pmm_alloc_page_low_16mb(); + if (!phys) { + vmm_as_activate(old_as); + return -ENOMEM; + } + + vmm_map_page((uint64_t)(uintptr_t)phys, (uint64_t)va, flags | VMM_FLAG_PRESENT | VMM_FLAG_USER); + } + + if (va == end_page) break; + } + + vmm_as_activate(old_as); + + return 0; +} + +int elf32_load_user_from_initrd(const char* filename, uintptr_t* entry_out, uintptr_t* user_stack_top_out, uintptr_t* addr_space_out, uintptr_t* heap_break_out) { + if (!filename || !entry_out || !user_stack_top_out || !addr_space_out) return -EFAULT; + if (!fs_root) return -EINVAL; + + uintptr_t new_as = vmm_as_create_kernel_clone(); + if (!new_as) return -ENOMEM; + + uintptr_t old_as = hal_cpu_get_address_space(); + + fs_node_t* node = vfs_lookup(filename); + if (!node) { + uart_print("[ELF] file not found: "); + uart_print(filename); + uart_print("\n"); + vmm_as_destroy(new_as); + return -ENOENT; + } + + uint32_t file_len = node->length; + if (file_len < sizeof(elf32_ehdr_t)) { + vmm_as_activate(old_as); + vmm_as_destroy(new_as); + return -EINVAL; + } + + uint8_t* file = (uint8_t*)kmalloc(file_len); + if (!file) { + vmm_as_destroy(new_as); + return -ENOMEM; + } + + uint32_t rd = vfs_read(node, 0, file_len, file); + if (rd != file_len) { + kfree(file); + vmm_as_destroy(new_as); + return -EIO; + } + + // Switch into the new address space only after the ELF image is fully buffered. + vmm_as_activate(new_as); + + const elf32_ehdr_t* eh = (const elf32_ehdr_t*)file; + int vrc = elf32_validate(eh, file_len); + if (vrc < 0) { + uart_print("[ELF] invalid ELF header\n"); + kfree(file); + vmm_as_activate(old_as); + vmm_as_destroy(new_as); + return vrc; + } + + const elf32_phdr_t* ph = (const elf32_phdr_t*)(file + eh->e_phoff); + uintptr_t highest_seg_end = 0; + + for (uint16_t i = 0; i < eh->e_phnum; i++) { + if (ph[i].p_type != PT_LOAD) continue; + + if (ph[i].p_memsz == 0) continue; + if (ph[i].p_vaddr == 0) { + uart_print("[ELF] PT_LOAD with vaddr=0 rejected\n"); + kfree(file); + vmm_as_activate(old_as); + vmm_as_destroy(new_as); + return -EINVAL; + } + if (ph[i].p_vaddr >= hal_mm_kernel_virt_base()) { + uart_print("[ELF] PT_LOAD in kernel range rejected\n"); + kfree(file); + vmm_as_activate(old_as); + vmm_as_destroy(new_as); + return -EINVAL; + } + + uint32_t seg_end = ph[i].p_vaddr + ph[i].p_memsz; + if (seg_end < ph[i].p_vaddr) { + kfree(file); + vmm_as_activate(old_as); + vmm_as_destroy(new_as); + return -EINVAL; + } + if (seg_end >= hal_mm_kernel_virt_base()) { + kfree(file); + vmm_as_activate(old_as); + vmm_as_destroy(new_as); + return -EINVAL; + } + + if ((uint64_t)ph[i].p_offset + (uint64_t)ph[i].p_filesz > (uint64_t)file_len) { + uart_print("[ELF] segment outside file\n"); + kfree(file); + vmm_as_activate(old_as); + vmm_as_destroy(new_as); + return -EINVAL; + } + + const uint32_t map_flags = VMM_FLAG_RW; + + int mrc = elf32_map_user_range(new_as, (uintptr_t)ph[i].p_vaddr, (size_t)ph[i].p_memsz, map_flags); + if (mrc < 0) { + uart_print("[ELF] OOM mapping user segment\n"); + kfree(file); + vmm_as_activate(old_as); + vmm_as_destroy(new_as); + return mrc; + } + + if (ph[i].p_filesz) { + memcpy((void*)(uintptr_t)ph[i].p_vaddr, file + ph[i].p_offset, ph[i].p_filesz); + } + + if (ph[i].p_memsz > ph[i].p_filesz) { + memset((void*)(uintptr_t)(ph[i].p_vaddr + ph[i].p_filesz), 0, ph[i].p_memsz - ph[i].p_filesz); + } + + if (seg_end > highest_seg_end) { + highest_seg_end = seg_end; + } + } + + const uintptr_t user_stack_base = 0x00800000U; + const size_t user_stack_size = 0x1000; + + int src2 = elf32_map_user_range(new_as, user_stack_base, user_stack_size, VMM_FLAG_RW); + if (src2 < 0) { + uart_print("[ELF] OOM mapping user stack\n"); + kfree(file); + vmm_as_activate(old_as); + vmm_as_destroy(new_as); + return src2; + } + + *entry_out = (uintptr_t)eh->e_entry; + *user_stack_top_out = user_stack_base + user_stack_size; + *addr_space_out = new_as; + if (heap_break_out) { + *heap_break_out = (highest_seg_end + 0xFFFU) & ~(uintptr_t)0xFFFU; + } + + kfree(file); + vmm_as_activate(old_as); + return 0; +} diff --git a/src/kernel/elf.c b/src/kernel/elf.c index 379c38a..ff885f9 100644 --- a/src/kernel/elf.c +++ b/src/kernel/elf.c @@ -1,251 +1,7 @@ #include "elf.h" - -#include "fs.h" -#include "heap.h" -#include "pmm.h" -#include "uart_console.h" -#include "utils.h" -#include "vmm.h" - -#include "errno.h" - -#include "hal/cpu.h" -#include "hal/mm.h" - #include -#if defined(__i386__) - -static void* pmm_alloc_page_low_16mb(void) { - for (int tries = 0; tries < 4096; tries++) { - void* p = pmm_alloc_page(); - if (!p) return NULL; - if ((uintptr_t)p < 0x01000000U) { - return p; - } - pmm_free_page(p); - } - return NULL; -} - -static int elf32_validate(const elf32_ehdr_t* eh, size_t file_len) { - if (!eh) return -EFAULT; - if (file_len < sizeof(*eh)) return -EINVAL; - - if (eh->e_ident[0] != ELF_MAGIC0 || - eh->e_ident[1] != ELF_MAGIC1 || - eh->e_ident[2] != ELF_MAGIC2 || - eh->e_ident[3] != ELF_MAGIC3) { - return -EINVAL; - } - if (eh->e_ident[4] != ELFCLASS32) return -EINVAL; - if (eh->e_ident[5] != ELFDATA2LSB) return -EINVAL; - - if (eh->e_type != ET_EXEC) return -EINVAL; - if (eh->e_machine != EM_386) return -EINVAL; - - if (eh->e_phentsize != sizeof(elf32_phdr_t)) return -EINVAL; - if (eh->e_phnum == 0) return -EINVAL; - - uint32_t ph_end = eh->e_phoff + (uint32_t)eh->e_phnum * (uint32_t)sizeof(elf32_phdr_t); - if (ph_end < eh->e_phoff) return -EINVAL; - if (ph_end > file_len) return -EINVAL; - - if (eh->e_entry == 0) return -EINVAL; - if (eh->e_entry >= hal_mm_kernel_virt_base()) return -EINVAL; - - return 0; -} - -static int elf32_map_user_range(uintptr_t as, uintptr_t vaddr, size_t len, uint32_t flags) { - if (len == 0) return 0; - if (vaddr == 0) return -EINVAL; - if (vaddr >= hal_mm_kernel_virt_base()) return -EINVAL; - - uintptr_t end = vaddr + len - 1; - if (end < vaddr) return -EINVAL; - if (end >= hal_mm_kernel_virt_base()) return -EINVAL; - - uintptr_t start_page = vaddr & ~(uintptr_t)0xFFF; - uintptr_t end_page = end & ~(uintptr_t)0xFFF; - - uintptr_t old_as = hal_cpu_get_address_space(); - vmm_as_activate(as); - - for (uintptr_t va = start_page;; va += 0x1000) { - const uint32_t pdi = (uint32_t)(va >> 22); - const uint32_t pti = (uint32_t)((va >> 12) & 0x03FF); - - volatile uint32_t* pd = (volatile uint32_t*)0xFFFFF000U; - int already_mapped = 0; - if ((pd[pdi] & 1U) != 0U) { - volatile uint32_t* pt = (volatile uint32_t*)0xFFC00000U + ((uintptr_t)pdi << 10); - if ((pt[pti] & 1U) != 0U) { - already_mapped = 1; - } - } - - if (!already_mapped) { - void* phys = pmm_alloc_page_low_16mb(); - if (!phys) { - vmm_as_activate(old_as); - return -ENOMEM; - } - - vmm_map_page((uint64_t)(uintptr_t)phys, (uint64_t)va, flags | VMM_FLAG_PRESENT | VMM_FLAG_USER); - } - - if (va == end_page) break; - } - - vmm_as_activate(old_as); - - return 0; -} - -int elf32_load_user_from_initrd(const char* filename, uintptr_t* entry_out, uintptr_t* user_stack_top_out, uintptr_t* addr_space_out, uintptr_t* heap_break_out) { - if (!filename || !entry_out || !user_stack_top_out || !addr_space_out) return -EFAULT; - if (!fs_root) return -EINVAL; - - uintptr_t new_as = vmm_as_create_kernel_clone(); - if (!new_as) return -ENOMEM; - - uintptr_t old_as = hal_cpu_get_address_space(); - - fs_node_t* node = vfs_lookup(filename); - if (!node) { - uart_print("[ELF] file not found: "); - uart_print(filename); - uart_print("\n"); - vmm_as_destroy(new_as); - return -ENOENT; - } - - uint32_t file_len = node->length; - if (file_len < sizeof(elf32_ehdr_t)) { - vmm_as_activate(old_as); - vmm_as_destroy(new_as); - return -EINVAL; - } - - uint8_t* file = (uint8_t*)kmalloc(file_len); - if (!file) { - vmm_as_destroy(new_as); - return -ENOMEM; - } - - uint32_t rd = vfs_read(node, 0, file_len, file); - if (rd != file_len) { - kfree(file); - vmm_as_destroy(new_as); - return -EIO; - } - - // Switch into the new address space only after the ELF image is fully buffered. - vmm_as_activate(new_as); - - const elf32_ehdr_t* eh = (const elf32_ehdr_t*)file; - int vrc = elf32_validate(eh, file_len); - if (vrc < 0) { - uart_print("[ELF] invalid ELF header\n"); - kfree(file); - vmm_as_activate(old_as); - vmm_as_destroy(new_as); - return vrc; - } - - const elf32_phdr_t* ph = (const elf32_phdr_t*)(file + eh->e_phoff); - uintptr_t highest_seg_end = 0; - - for (uint16_t i = 0; i < eh->e_phnum; i++) { - if (ph[i].p_type != PT_LOAD) continue; - - if (ph[i].p_memsz == 0) continue; - if (ph[i].p_vaddr == 0) { - uart_print("[ELF] PT_LOAD with vaddr=0 rejected\n"); - kfree(file); - vmm_as_activate(old_as); - vmm_as_destroy(new_as); - return -EINVAL; - } - if (ph[i].p_vaddr >= hal_mm_kernel_virt_base()) { - uart_print("[ELF] PT_LOAD in kernel range rejected\n"); - kfree(file); - vmm_as_activate(old_as); - vmm_as_destroy(new_as); - return -EINVAL; - } - - uint32_t seg_end = ph[i].p_vaddr + ph[i].p_memsz; - if (seg_end < ph[i].p_vaddr) { - kfree(file); - vmm_as_activate(old_as); - vmm_as_destroy(new_as); - return -EINVAL; - } - if (seg_end >= hal_mm_kernel_virt_base()) { - kfree(file); - vmm_as_activate(old_as); - vmm_as_destroy(new_as); - return -EINVAL; - } - - if ((uint64_t)ph[i].p_offset + (uint64_t)ph[i].p_filesz > (uint64_t)file_len) { - uart_print("[ELF] segment outside file\n"); - kfree(file); - vmm_as_activate(old_as); - vmm_as_destroy(new_as); - return -EINVAL; - } - - const uint32_t map_flags = VMM_FLAG_RW; - - int mrc = elf32_map_user_range(new_as, (uintptr_t)ph[i].p_vaddr, (size_t)ph[i].p_memsz, map_flags); - if (mrc < 0) { - uart_print("[ELF] OOM mapping user segment\n"); - kfree(file); - vmm_as_activate(old_as); - vmm_as_destroy(new_as); - return mrc; - } - - if (ph[i].p_filesz) { - memcpy((void*)(uintptr_t)ph[i].p_vaddr, file + ph[i].p_offset, ph[i].p_filesz); - } - - if (ph[i].p_memsz > ph[i].p_filesz) { - memset((void*)(uintptr_t)(ph[i].p_vaddr + ph[i].p_filesz), 0, ph[i].p_memsz - ph[i].p_filesz); - } - - if (seg_end > highest_seg_end) { - highest_seg_end = seg_end; - } - } - - const uintptr_t user_stack_base = 0x00800000U; - const size_t user_stack_size = 0x1000; - - int src2 = elf32_map_user_range(new_as, user_stack_base, user_stack_size, VMM_FLAG_RW); - if (src2 < 0) { - uart_print("[ELF] OOM mapping user stack\n"); - kfree(file); - vmm_as_activate(old_as); - vmm_as_destroy(new_as); - return src2; - } - - *entry_out = (uintptr_t)eh->e_entry; - *user_stack_top_out = user_stack_base + user_stack_size; - *addr_space_out = new_as; - if (heap_break_out) { - *heap_break_out = (highest_seg_end + 0xFFFU) & ~(uintptr_t)0xFFFU; - } - - kfree(file); - vmm_as_activate(old_as); - return 0; -} -#else +__attribute__((weak)) int elf32_load_user_from_initrd(const char* filename, uintptr_t* entry_out, uintptr_t* user_stack_top_out, uintptr_t* addr_space_out, uintptr_t* heap_break_out) { (void)filename; (void)entry_out; @@ -254,4 +10,3 @@ int elf32_load_user_from_initrd(const char* filename, uintptr_t* entry_out, uint (void)heap_break_out; return -1; } -#endif -- 2.43.0