]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
refactor: move x86 ELF loader from src/kernel/elf.c to src/arch/x86/elf.c
authorTulio A M Mendes <[email protected]>
Tue, 10 Feb 2026 05:32:41 +0000 (02:32 -0300)
committerTulio A M Mendes <[email protected]>
Tue, 10 Feb 2026 05:32:41 +0000 (02:32 -0300)
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 [new file with mode: 0644]
src/kernel/elf.c

diff --git a/src/arch/x86/elf.c b/src/arch/x86/elf.c
new file mode 100644 (file)
index 0000000..8927517
--- /dev/null
@@ -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 <stdint.h>
+
+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;
+}
index 379c38adc981272304b6a67c29ba52c2ab28b357..ff885f9c4f8f2d744f31c52f838dbc87d3e9dee4 100644 (file)
@@ -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 <stdint.h>
 
-#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