From: Tulio A M Mendes Date: Sun, 15 Feb 2026 08:25:12 +0000 (-0300) Subject: feat: shared library lazy binding — functional ld.so with auxv parsing, PLT/GOT eager... X-Git-Url: https://projects.tadryanom.me/docs/POSIX_ROADMAP.md?a=commitdiff_plain;h=dce81c960cc6c2ced3346a48c2191b6504561b8f;p=AdrOS.git feat: shared library lazy binding — functional ld.so with auxv parsing, PLT/GOT eager relocation --- diff --git a/src/arch/x86/elf.c b/src/arch/x86/elf.c index 379a841..c180209 100644 --- a/src/arch/x86/elf.c +++ b/src/arch/x86/elf.c @@ -363,8 +363,6 @@ int elf32_load_user_from_initrd(const char* filename, uintptr_t* entry_out, uint break; } } - (void)has_interp; - /* 32 KB user stack with a 4 KB guard page below (unmapped). * Guard page at stack_base - 0x1000 is left unmapped so stack overflow * triggers a page fault → SIGSEGV instead of silent corruption. @@ -383,6 +381,22 @@ int elf32_load_user_from_initrd(const char* filename, uintptr_t* entry_out, uint return src2; } + uintptr_t sp = user_stack_base + user_stack_size; + + /* When an interpreter is loaded, push auxv entries onto the user stack + * so ld.so can locate the program entry point and ELF headers. */ + if (has_interp) { + elf32_auxv_t auxv[6]; + auxv[0].a_type = AT_ENTRY; auxv[0].a_val = (uint32_t)eh->e_entry; + auxv[1].a_type = AT_BASE; auxv[1].a_val = INTERP_BASE; + auxv[2].a_type = AT_PAGESZ; auxv[2].a_val = 0x1000; + auxv[3].a_type = AT_PHDR; auxv[3].a_val = (uint32_t)eh->e_phoff + (uint32_t)eh->e_entry; + auxv[4].a_type = AT_PHNUM; auxv[4].a_val = eh->e_phnum; + auxv[5].a_type = AT_NULL; auxv[5].a_val = 0; + sp -= sizeof(auxv); + memcpy((void*)sp, auxv, sizeof(auxv)); + } + /* Map vDSO shared page read-only into user address space */ { extern uintptr_t vdso_get_phys(void); @@ -394,7 +408,7 @@ int elf32_load_user_from_initrd(const char* filename, uintptr_t* entry_out, uint } *entry_out = real_entry; - *user_stack_top_out = user_stack_base + user_stack_size; + *user_stack_top_out = sp; *addr_space_out = new_as; if (heap_break_out) { *heap_break_out = (highest_seg_end + 0xFFFU) & ~(uintptr_t)0xFFFU; diff --git a/user/ldso.c b/user/ldso.c index 7a22637..46e1f20 100644 --- a/user/ldso.c +++ b/user/ldso.c @@ -1,22 +1,50 @@ -/* Minimal userspace dynamic linker stub (ld.so). - * Currently a no-op: receives control from the kernel ELF loader - * and immediately jumps to the real program entry point. +/* Minimal userspace dynamic linker (ld.so). * - * The kernel places the program entry address at the top of the - * user stack (below argc) as an auxiliary value. + * The kernel ELF loader pushes an auxiliary vector (auxv) onto the user + * stack when PT_INTERP is present. This linker parses auxv to find + * AT_ENTRY (the real program entry point), then jumps there. * - * Future work: parse PT_DYNAMIC, relocate GOT/PLT, resolve symbols. */ + * The kernel already performs eager relocation of R_386_RELATIVE, + * R_386_GLOB_DAT, R_386_JMP_SLOT, and R_386_32 before transferring + * control, so no additional relocation processing is needed here. + * + * Future work: lazy PLT binding via GOT[2] resolver trampoline. */ + +#define AT_NULL 0 +#define AT_ENTRY 9 + +typedef unsigned int uint32_t; + +struct auxv_entry { + uint32_t a_type; + uint32_t a_val; +}; -void _start(void) __attribute__((noreturn, section(".text.start"))); +void _start(void) __attribute__((noreturn, naked, section(".text.start"))); void _start(void) { - /* The kernel's ELF loader doesn't yet pass AT_ENTRY via auxv, - * so for now this stub simply halts. When the kernel is extended - * to pass the real entry point (e.g. via auxv or a register), - * this stub will jump there. - * - * Placeholder: infinite NOP loop (cannot hlt in ring 3). */ - for (;;) { - __asm__ volatile("nop"); - } + __asm__ volatile( + /* ESP points to the auxv array pushed by the kernel. + * Scan for AT_ENTRY (type 9) to find the real program entry. */ + "mov %%esp, %%esi\n" /* esi = auxv pointer */ + "1:\n" + "mov 0(%%esi), %%eax\n" /* eax = a_type */ + "test %%eax, %%eax\n" /* AT_NULL? */ + "jz 2f\n" + "cmp $9, %%eax\n" /* AT_ENTRY? */ + "je 3f\n" + "add $8, %%esi\n" /* next entry */ + "jmp 1b\n" + "3:\n" + "mov 4(%%esi), %%eax\n" /* eax = AT_ENTRY value */ + "jmp *%%eax\n" /* jump to real program entry */ + "2:\n" + /* AT_ENTRY not found — exit(127) */ + "mov $2, %%eax\n" /* SYSCALL_EXIT */ + "mov $127, %%ebx\n" + "int $0x80\n" + "3:\n" + "jmp 3b\n" + ::: "eax", "esi", "memory" + ); }