From: Tulio A M Mendes Date: Thu, 12 Feb 2026 02:19:39 +0000 (-0300) Subject: feat: dynamic linking infrastructure - PT_INTERP support, ET_DYN validation, elf32_lo... X-Git-Url: https://projects.tadryanom.me/docs/static/gitweb.css?a=commitdiff_plain;h=f88c0af5dd9cde9a40af5dd99372f619dfa93f65;p=AdrOS.git feat: dynamic linking infrastructure - PT_INTERP support, ET_DYN validation, elf32_load_interp, ELF dynamic section types, auxiliary vector definitions --- diff --git a/include/elf.h b/include/elf.h index 07f0b92..31b474a 100644 --- a/include/elf.h +++ b/include/elf.h @@ -45,12 +45,82 @@ typedef struct { #define ET_EXEC 2 #define EM_386 3 -#define PT_LOAD 1 +#define PT_NULL 0 +#define PT_LOAD 1 +#define PT_DYNAMIC 2 +#define PT_INTERP 3 +#define PT_NOTE 4 +#define PT_PHDR 6 + +#define ET_DYN 3 #define PF_X 0x1 #define PF_W 0x2 #define PF_R 0x4 +/* Dynamic section entry */ +typedef struct { + int32_t d_tag; + uint32_t d_val; +} elf32_dyn_t; + +#define DT_NULL 0 +#define DT_NEEDED 1 +#define DT_HASH 4 +#define DT_STRTAB 5 +#define DT_SYMTAB 6 +#define DT_RELA 7 +#define DT_RELASZ 8 +#define DT_RELAENT 9 +#define DT_STRSZ 10 +#define DT_SYMENT 11 +#define DT_REL 17 +#define DT_RELSZ 18 +#define DT_RELENT 19 +#define DT_JMPREL 23 +#define DT_PLTRELSZ 2 +#define DT_PLTREL 20 + +/* Relocation entry (without addend) */ +typedef struct { + uint32_t r_offset; + uint32_t r_info; +} elf32_rel_t; + +#define ELF32_R_SYM(i) ((i) >> 8) +#define ELF32_R_TYPE(i) ((unsigned char)(i)) + +#define R_386_NONE 0 +#define R_386_32 1 +#define R_386_PC32 2 +#define R_386_GLOB_DAT 6 +#define R_386_JMP_SLOT 7 +#define R_386_RELATIVE 8 + +/* Symbol table entry */ +typedef struct { + uint32_t st_name; + uint32_t st_value; + uint32_t st_size; + uint8_t st_info; + uint8_t st_other; + uint16_t st_shndx; +} elf32_sym_t; + +/* Auxiliary vector (passed on user stack after envp) */ +typedef struct { + uint32_t a_type; + uint32_t a_val; +} elf32_auxv_t; + +#define AT_NULL 0 +#define AT_PHDR 3 +#define AT_PHENT 4 +#define AT_PHNUM 5 +#define AT_PAGESZ 6 +#define AT_BASE 7 /* Interpreter base address */ +#define AT_ENTRY 9 /* Program entry point */ + 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); #endif diff --git a/src/arch/x86/elf.c b/src/arch/x86/elf.c index 14c6907..f7dc4b9 100644 --- a/src/arch/x86/elf.c +++ b/src/arch/x86/elf.c @@ -39,7 +39,7 @@ static int elf32_validate(const elf32_ehdr_t* eh, size_t file_len) { 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_type != ET_EXEC && eh->e_type != ET_DYN) return -EINVAL; if (eh->e_machine != EM_386) return -EINVAL; if (eh->e_phentsize != sizeof(elf32_phdr_t)) return -EINVAL; @@ -102,6 +102,92 @@ static int elf32_map_user_range(uintptr_t as, uintptr_t vaddr, size_t len, uint3 return 0; } +/* Load ELF segments at base_offset (0 for ET_EXEC, non-zero for interpreter). + * Operates in the target address space (caller must have activated it). + * Returns 0 on success, fills highest_end. */ +static int elf32_load_segments(const uint8_t* file, uint32_t file_len, + uintptr_t as, uintptr_t base_offset, + uintptr_t* highest_end) { + const elf32_ehdr_t* eh = (const elf32_ehdr_t*)file; + const elf32_phdr_t* ph = (const elf32_phdr_t*)(file + eh->e_phoff); + uintptr_t seg_max = 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; + + uintptr_t vaddr = (uintptr_t)ph[i].p_vaddr + base_offset; + if (vaddr == 0 || vaddr >= hal_mm_kernel_virt_base()) return -EINVAL; + + uint32_t seg_end = (uint32_t)vaddr + ph[i].p_memsz; + if (seg_end < vaddr || seg_end >= hal_mm_kernel_virt_base()) return -EINVAL; + + if ((uint64_t)ph[i].p_offset + (uint64_t)ph[i].p_filesz > (uint64_t)file_len) + return -EINVAL; + + int mrc = elf32_map_user_range(as, vaddr, (size_t)ph[i].p_memsz, VMM_FLAG_RW); + if (mrc < 0) return mrc; + + if (ph[i].p_filesz) + memcpy((void*)vaddr, file + ph[i].p_offset, ph[i].p_filesz); + if (ph[i].p_memsz > ph[i].p_filesz) + memset((void*)(vaddr + ph[i].p_filesz), 0, ph[i].p_memsz - ph[i].p_filesz); + + if (seg_end > seg_max) seg_max = seg_end; + } + + if (highest_end) *highest_end = seg_max; + return 0; +} + +/* Load an interpreter ELF (ld.so) at INTERP_BASE. + * Returns 0 on success, sets *interp_entry. */ +#define INTERP_BASE 0x40000000U + +static int elf32_load_interp(const char* interp_path, uintptr_t as, + uintptr_t* interp_entry, uintptr_t* interp_base_out) { + if (!interp_path || !interp_entry) return -EINVAL; + + fs_node_t* node = vfs_lookup(interp_path); + if (!node) { + uart_print("[ELF] interp not found: "); + uart_print(interp_path); + uart_print("\n"); + return -ENOENT; + } + + uint32_t flen = node->length; + if (flen < sizeof(elf32_ehdr_t)) return -EINVAL; + + uint8_t* fbuf = (uint8_t*)kmalloc(flen); + if (!fbuf) return -ENOMEM; + + if (vfs_read(node, 0, flen, fbuf) != flen) { + kfree(fbuf); + return -EIO; + } + + const elf32_ehdr_t* eh = (const elf32_ehdr_t*)fbuf; + int vrc = elf32_validate(eh, flen); + if (vrc < 0) { + kfree(fbuf); + return vrc; + } + + uintptr_t dummy = 0; + int rc = elf32_load_segments(fbuf, flen, as, INTERP_BASE, &dummy); + if (rc < 0) { + kfree(fbuf); + return rc; + } + + *interp_entry = (uintptr_t)eh->e_entry + INTERP_BASE; + if (interp_base_out) *interp_base_out = INTERP_BASE; + + kfree(fbuf); + 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; @@ -140,7 +226,6 @@ int elf32_load_user_from_initrd(const char* filename, uintptr_t* entry_out, uint 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; @@ -153,73 +238,44 @@ int elf32_load_user_from_initrd(const char* filename, uintptr_t* entry_out, uint return vrc; } - const elf32_phdr_t* ph = (const elf32_phdr_t*)(file + eh->e_phoff); uintptr_t highest_seg_end = 0; + int lrc = elf32_load_segments(file, file_len, new_as, 0, &highest_seg_end); + if (lrc < 0) { + uart_print("[ELF] segment load failed\n"); + kfree(file); + vmm_as_activate(old_as); + vmm_as_destroy(new_as); + return lrc; + } - 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); - } + /* Check for PT_INTERP — if present, load the dynamic linker */ + const elf32_phdr_t* ph = (const elf32_phdr_t*)(file + eh->e_phoff); + uintptr_t real_entry = (uintptr_t)eh->e_entry; + int has_interp = 0; - if (seg_end > highest_seg_end) { - highest_seg_end = seg_end; + for (uint16_t i = 0; i < eh->e_phnum; i++) { + if (ph[i].p_type == PT_INTERP) { + if (ph[i].p_filesz == 0 || ph[i].p_filesz > 256) break; + if (ph[i].p_offset + ph[i].p_filesz > file_len) break; + + char interp_path[256]; + memcpy(interp_path, file + ph[i].p_offset, ph[i].p_filesz); + interp_path[ph[i].p_filesz - 1] = '\0'; + + uintptr_t interp_entry = 0; + uintptr_t interp_base = 0; + int irc = elf32_load_interp(interp_path, new_as, &interp_entry, &interp_base); + if (irc == 0) { + real_entry = interp_entry; + has_interp = 1; + uart_print("[ELF] loaded interp: "); + uart_print(interp_path); + uart_print("\n"); + } + break; } } + (void)has_interp; const uintptr_t user_stack_base = 0x00800000U; const size_t user_stack_size = 0x1000; @@ -233,7 +289,7 @@ int elf32_load_user_from_initrd(const char* filename, uintptr_t* entry_out, uint return src2; } - *entry_out = (uintptr_t)eh->e_entry; + *entry_out = real_entry; *user_stack_top_out = user_stack_base + user_stack_size; *addr_space_out = new_as; if (heap_break_out) {