user: implement mlibc as the libc, finally.

It's finally done..

Signed-off-by: kaguya <vpshinomiya@protonmail.com>
This commit is contained in:
kaguya
2026-05-02 03:31:49 -04:00
parent 2fa39ad85a
commit 9a9b91c940
2387 changed files with 152741 additions and 315 deletions
+182 -106
View File
@@ -1,3 +1,4 @@
// elf.c (now extracts AT_PHDR / AT_PHENT / AT_PHNUM + minor cleanups)
#include "elf.h"
#include "libk/stdio.h"
#include "libk/string.h"
@@ -8,148 +9,223 @@
extern uintptr_t g_hhdm_offset;
#define ELF_BUFFER_SIZE (1024 * 1024)
bool ELF_Read(const char* path, void** entryPoint, struct pagemap *target_pagemap)
bool ELF_Read(const char* path,
void** entryPoint,
struct pagemap *target_pagemap,
uint64_t *out_tls_fs_base,
uint64_t *out_phdr_va,
uint16_t *out_phent,
uint16_t *out_phnum)
{
uint32_t size;
*out_tls_fs_base = 0;
*out_phdr_va = 0;
*out_phent = 0;
*out_phnum = 0;
uint8_t* elf_buffer = kmalloc(ELF_BUFFER_SIZE);
if (!elf_buffer) {
printf("ELF: kmalloc failed\n");
uint32_t inum = ext2_resolve_path(path);
if (!inum) {
printf("ELF: file not found: %s\n", path);
return false;
}
// ── load file ─────────────────────────────────────
if (!ext2_read_file_from_root(path, elf_buffer, &size)) {
printf("ELF: failed to read file\n");
kfree(elf_buffer);
ext2_inode_t inode;
if (!ext2_read_inode(inum, &inode)) {
printf("ELF: failed to read inode\n");
return false;
}
if (size < sizeof(ELFHeader)) {
uint64_t file_size = inode.i_size;
if (file_size < sizeof(ELFHeader)) {
printf("ELF: file too small\n");
kfree(elf_buffer);
return false;
}
uint64_t buf_pages = ALIGN_UP(file_size, PAGE_SIZE) / PAGE_SIZE;
void* buffer_phys = pmm_allocz(buf_pages);
if (!buffer_phys) {
printf("ELF: failed to allocate %lu pages for file buffer\n", buf_pages);
return false;
}
uint8_t* elf_buffer = (uint8_t*)((uintptr_t)buffer_phys + MEM_PHYS_OFFSET);
if (!ext2_read_file(&inode, elf_buffer)) {
pmm_free(buffer_phys, buf_pages);
return false;
}
ELFHeader* header = (ELFHeader*)elf_buffer;
printf("=== ELF DEBUG ===\n");
printf("Entry point VA = 0x%lx\n", header->ProgramEntryPosition);
printf("PHDR offset = 0x%lx\n", header->ProgramHeaderTablePosition);
printf("PHDR count = %u\n", header->ProgramHeaderTableEntryCount);
printf("=== ELF DEBUG ===\n"
"Entry=0x%lx PHDR@0x%lx count=%u type=0x%x arch=0x%x\n"
"=== END ===\n",
header->ProgramEntryPosition,
header->ProgramHeaderTablePosition,
header->ProgramHeaderTableEntryCount,
header->Type,
header->InstructionSet);
if (memcmp(header->Magic, ELF_MAGIC, 4) != 0 ||
header->Bitness != ELF_BITNESS_64BIT ||
header->Endianness != ELF_ENDIANNESS_LITTLE ||
(header->Type != ELF_TYPE_EXECUTABLE && header->Type != ELF_TYPE_SHARED) ||
header->InstructionSet != ELF_INSTRUCTION_SET_X64) {
printf("=== END ELF DEBUG ===\n");
// ── validate ELF ──────────────────────────────────
if (memcmp(header->Magic, ELF_MAGIC, 4) != 0) {
printf("ELF: bad magic\n");
kfree(elf_buffer);
return false;
}
if (header->Bitness != ELF_BITNESS_64BIT) {
printf("ELF: not 64-bit\n");
kfree(elf_buffer);
return false;
}
if (header->Endianness != ELF_ENDIANNESS_LITTLE) {
printf("ELF: wrong endianness\n");
kfree(elf_buffer);
return false;
}
if (header->Type != ELF_TYPE_EXECUTABLE) {
printf("ELF: not executable\n");
kfree(elf_buffer);
return false;
}
if (header->InstructionSet != ELF_INSTRUCTION_SET_X64) {
printf("ELF: wrong arch\n");
kfree(elf_buffer);
return false;
printf("ELF: unsupported/invalid header\n");
goto cleanup;
}
*entryPoint = (void*)header->ProgramEntryPosition;
// ── program headers ───────────────────────────────
// ------------------------------------------------------------------
// Parse program headers LOAD, TLS, and PHDR
// ------------------------------------------------------------------
uint64_t tls_vaddr = 0, tls_filesz = 0, tls_memsz = 0, tls_align = 8;
uint8_t* tls_src = NULL;
uint64_t phdr_vaddr = 0;
uint8_t* ph_table = elf_buffer + header->ProgramHeaderTablePosition;
uint64_t phdr_table_end = header->ProgramHeaderTablePosition +
(uint64_t)header->ProgramHeaderTableEntryCount *
header->ProgramHeaderTableEntrySize;
for (uint32_t i = 0; i < header->ProgramHeaderTableEntryCount; i++)
{
ELFProgramHeader* ph = (ELFProgramHeader*)(ph_table +
i * header->ProgramHeaderTableEntrySize);
if (phdr_table_end > file_size) {
printf("ELF: program header table extends beyond file\n");
goto cleanup;
}
if (ph->Type != ELF_PROGRAM_TYPE_LOAD) {
printf("LOAD segment: VA=0x%lx FileSz=0x%lx MemSz=0x%lx\n",
ph->VirtualAddress, ph->FileSize, ph->MemorySize);
for (uint32_t i = 0; i < header->ProgramHeaderTableEntryCount; i++) {
ELFProgramHeader* ph = (ELFProgramHeader*)(ph_table + i * header->ProgramHeaderTableEntrySize);
// PT_PHDR
if (ph->Type == ELF_PROGRAM_TYPE_PHDR) {
phdr_vaddr = ph->VirtualAddress;
printf("ELF: Found PT_PHDR VA=0x%lx\n", phdr_vaddr);
// fall through
}
// PT_TLS
if (ph->Type == ELF_PROGRAM_TYPE_TLS) {
tls_vaddr = ph->VirtualAddress;
tls_filesz = ph->FileSize;
tls_memsz = ph->MemorySize;
tls_align = ph->Align ? ph->Align : 8;
tls_src = elf_buffer + ph->Offset;
if (ph->Offset + ph->FileSize > file_size) {
printf("ELF: PT_TLS segment data out of file bounds\n");
goto cleanup;
}
printf("ELF: Found PT_TLS VA=0x%lx FileSz=0x%lx MemSz=0x%lx Align=0x%lx\n",
tls_vaddr, tls_filesz, tls_memsz, tls_align);
continue;
}
uint64_t virt = ph->VirtualAddress;
uint64_t offset = ph->Offset;
uint64_t memsz = ph->MemorySize;
uint64_t filesz = ph->FileSize;
if (memsz == 0)
if (ph->Type != ELF_PROGRAM_TYPE_LOAD || ph->MemorySize == 0)
continue;
// ── align to page boundary ─────────────────────
uint64_t aligned_virt = ALIGN_DOWN(virt, PAGE_SIZE);
uint64_t page_offset = virt & 0xFFF;
uint64_t aligned_memsz = ALIGN_UP(memsz + page_offset, PAGE_SIZE);
uint64_t pages = aligned_memsz / PAGE_SIZE;
// Allocate physical pages
uint64_t phys_base = (uint64_t)pmm_alloc(pages);
if (!phys_base) {
printf("ELF: pmm_alloc failed for %lu pages\n", pages);
kfree(elf_buffer);
return false;
if (ph->Offset + ph->FileSize > file_size) {
printf("ELF: PT_LOAD segment data out of file bounds\n");
goto cleanup;
}
// Map with exact permissions
uint64_t map_flags = PAGE_USER;
if (ph->Flags & PF_R) map_flags |= PAGE_READ;
if (ph->Flags & PF_W) map_flags |= PAGE_WRITE;
if (!(ph->Flags & PF_X)) map_flags |= PAGE_NO_EXECUTE;
uint64_t virt = ph->VirtualAddress;
uint64_t aligned_virt = ALIGN_DOWN(virt, PAGE_SIZE);
uint64_t page_offset = virt & (PAGE_SIZE - 1);
uint64_t aligned_memsz = ALIGN_UP(ph->MemorySize + page_offset, PAGE_SIZE);
uint64_t pages = aligned_memsz / PAGE_SIZE;
void* seg_phys = pmm_allocz(pages);
if (!seg_phys) {
printf("ELF: failed to allocate physical pages for LOAD segment\n");
goto cleanup;
}
// ── map each page individually using new vmm_map_page ─────
for (uint64_t p = 0; p < pages; p++) {
uint64_t virt_addr = aligned_virt + p * PAGE_SIZE;
uint64_t phys_addr = phys_base + p * PAGE_SIZE;
bool success = vmm_map_page(
target_pagemap,
virt_addr,
phys_addr,
PAGE_READ | PAGE_WRITE | PAGE_USER, // RW + User mode
Size4KiB
);
if (!success) {
printf("ELF: failed to map page at 0x%lx\n", virt_addr);
// TODO: cleanup previously mapped pages + free phys
kfree(elf_buffer);
return false;
if (!vmm_map_page(target_pagemap,
aligned_virt + p * PAGE_SIZE,
(uintptr_t)seg_phys + p * PAGE_SIZE,
map_flags,
Size4KiB)) {
pmm_free(seg_phys, pages);
goto cleanup;
}
}
// ── copy segment data ───────────────────────────────
uint8_t* dst = (uint8_t*)(phys_base + MEM_PHYS_OFFSET); // via HHDM
uint8_t* src = elf_buffer + offset;
memcpy(dst + page_offset, src, filesz);
// ── zero BSS section ────────────────────────────────
if (memsz > filesz) {
memset(dst + page_offset + filesz, 0, memsz - filesz);
uint8_t* dst = (uint8_t*)((uintptr_t)seg_phys + MEM_PHYS_OFFSET);
memcpy(dst + page_offset, elf_buffer + ph->Offset, ph->FileSize);
if (ph->MemorySize > ph->FileSize) {
memset(dst + page_offset + ph->FileSize, 0,
ph->MemorySize - ph->FileSize);
}
}
kfree(elf_buffer);
uint64_t tls_size = tls_memsz ? ALIGN_UP(tls_memsz, tls_align) : 0ULL;
uint64_t tcb_va = TLS_BASE_VA + PAGE_SIZE;
uint64_t tls_va = tcb_va - tls_size;
uint64_t page_va = ALIGN_DOWN(tls_va, PAGE_SIZE);
uint64_t tcb_size = sizeof(TCB);
uint64_t block_end_va = tcb_va + tcb_size;
uint64_t block_end_page = ALIGN_UP(block_end_va, PAGE_SIZE);
uint64_t map_pages = ((block_end_page - page_va) / PAGE_SIZE) + 8;
if (map_pages == 0) map_pages = 1;
void* tls_phys = pmm_allocz(map_pages);
if (!tls_phys) {
printf("ELF: failed to allocate TLS/TCB pages\n");
goto cleanup;
}
uint64_t tls_map_flags = PAGE_USER | PAGE_READ | PAGE_WRITE;
for (uint64_t p = 0; p < map_pages; p++) {
if (!vmm_map_page(target_pagemap,
page_va + p * PAGE_SIZE,
(uintptr_t)tls_phys + p * PAGE_SIZE,
tls_map_flags,
Size4KiB)) {
pmm_free(tls_phys, map_pages);
goto cleanup;
}
}
uint8_t* base_hhdm = (uint8_t*)((uintptr_t)tls_phys + MEM_PHYS_OFFSET);
if (tls_size > 0) {
uint8_t* tls_dst = base_hhdm + (tls_va - page_va);
if (tls_filesz) memcpy(tls_dst, tls_src, tls_filesz);
if (tls_memsz > tls_filesz)
memset(tls_dst + tls_filesz, 0, tls_memsz - tls_filesz);
}
TCB* tcb = (TCB*)(base_hhdm + (tcb_va - page_va));
memset(tcb, 0, sizeof(TCB));
tcb->self = (void*)tcb_va;
tcb->tid = 1;
*out_tls_fs_base = tcb_va;
*out_phdr_va = tls_vaddr ? tls_vaddr : phdr_vaddr;
*out_phent = header->ProgramHeaderTableEntrySize;
*out_phnum = header->ProgramHeaderTableEntryCount;
printf("ELF: TLS/TCB setup complete TCB@0x%lx TLS@0x%lx FS=0x%lx\n"
" PHDR@0x%lx PHENT=0x%x PHNUM=%u\n",
tcb_va, tls_va, tcb_va, *out_phdr_va, *out_phent, *out_phnum);
pmm_free(buffer_phys, buf_pages);
return true;
cleanup:
pmm_free(buffer_phys, buf_pages);
return false;
}