]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
feat: shared library lazy binding — functional ld.so with auxv parsing, PLT/GOT eager...
authorTulio A M Mendes <[email protected]>
Sun, 15 Feb 2026 08:25:12 +0000 (05:25 -0300)
committerTulio A M Mendes <[email protected]>
Sun, 15 Feb 2026 08:25:12 +0000 (05:25 -0300)
src/arch/x86/elf.c
user/ldso.c

index 379a84121cec251dae551050bedb04fa007ef086..c1802099df2e68955a3eaba876eeb24c4c6ae66a 100644 (file)
@@ -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;
index 7a22637218c901ba5b21a170c2caa9ced799c504..46e1f2079c8c98bda269d3f76b283eeebf646f88 100644 (file)
@@ -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"
+    );
 }