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.
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);
}
*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;
-/* 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"
+ );
}