fix: stream ELF program loading instead of buffering whole files

ElfLoad read the entire binary into one kernel heap allocation before
copying segments out. The heap grows through ReallocConsecutive, so a
40 MB cc1plus required 10k physically contiguous pages - effectively
impossible after boot with a 368 MB ramdisk module resident, and the
failed spawn surfaced as posix_spawnp ENOENT in the gcc driver.

The loader now reads the ELF header and program header table (bounded
at 64 entries), then copies each PT_LOAD page and the PT_TLS template
directly from the VFS into freshly mapped process pages. Peak kernel
memory per load drops from fileSize to one page regardless of binary
size. Boot-smoke verified: all userspace loads through this path.

Co-Authored-By: Claude Fable 5 <[email protected]>
This commit is contained in:
2026-07-17 10:25:30 +02:00
co-authored by Claude Fable 5
parent 78ce551688
commit 2f1010b02e
2 changed files with 60 additions and 54 deletions
+1 -1
View File
@@ -12,4 +12,4 @@
#pragma once #pragma once
#define MONTAUK_BUILD_NUMBER 22 #define MONTAUK_BUILD_NUMBER 23
+57 -51
View File
@@ -433,33 +433,58 @@ namespace Sched {
return 0; return 0;
} }
// Read entire file into a heap buffer // Stream the load: headers first, then each segment page copied
uint8_t* fileData = (uint8_t*)Memory::g_heap->Request(fileSize); // straight from the file into freshly mapped process pages. The
if (fileData == nullptr) { // previous whole-file heap buffer needed fileSize bytes of
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Failed to allocate " << fileSize << " bytes for file"; // physically contiguous kernel heap, which a 40 MB compiler
// binary cannot rely on after boot.
Elf64Header hdr{};
if (Fs::Vfs::ReadBackendFile(file, (uint8_t*)&hdr, 0, sizeof(hdr))
!= (int)sizeof(hdr) || !ValidateElfHeader(&hdr)) {
Fs::Vfs::CloseBackendFile(file); Fs::Vfs::CloseBackendFile(file);
return 0; return 0;
} }
Fs::Vfs::ReadBackendFile(file, fileData, 0, fileSize); if (hdr.e_phnum == 0 || hdr.e_phnum > 64 ||
hdr.e_phentsize != sizeof(Elf64ProgramHeader)) {
Kt::KernelLogStream(Kt::ERROR, "ELF")
<< "Unsupported program header table (" << (uint64_t)hdr.e_phnum
<< " entries of " << (uint64_t)hdr.e_phentsize << " bytes)";
Fs::Vfs::CloseBackendFile(file); Fs::Vfs::CloseBackendFile(file);
// Prevent the optimizer from reordering the VfsRead store past the
// header validation reads that follow.
asm volatile("" ::: "memory");
// Validate ELF header
Elf64Header* hdr = (Elf64Header*)fileData;
if (!ValidateElfHeader(hdr)) {
Memory::g_heap->Free(fileData);
return 0; return 0;
} }
// Process program headers Elf64ProgramHeader phdrs[64];
if (Fs::Vfs::ReadBackendFile(file, (uint8_t*)phdrs, hdr.e_phoff,
(uint64_t)hdr.e_phnum * sizeof(Elf64ProgramHeader))
!= (int)((uint64_t)hdr.e_phnum * sizeof(Elf64ProgramHeader))) {
Fs::Vfs::CloseBackendFile(file);
return 0;
}
// Copy the file bytes that overlap [pageStart, pageStart+0x1000)
// for a segment whose file image spans [vaddr, vaddr+filesz) at
// file offset fileOff.
auto copyPageFromFile = [&](uint64_t physAddr, uint64_t pageStart,
uint64_t vaddr, uint64_t fileOff,
uint64_t filesz) -> bool {
uint64_t pageEnd = pageStart + 0x1000;
uint64_t dataEnd = vaddr + filesz;
uint64_t copyStart = (pageStart > vaddr) ? pageStart : vaddr;
uint64_t copyEnd = (pageEnd < dataEnd) ? pageEnd : dataEnd;
if (copyStart >= copyEnd) {
return true;
}
uint8_t* dst = (uint8_t*)Memory::HHDM(physAddr) + (copyStart - pageStart);
uint64_t srcOff = fileOff + (copyStart - vaddr);
uint64_t len = copyEnd - copyStart;
return Fs::Vfs::ReadBackendFile(file, dst, srcOff, len) == (int)len;
};
Elf64ProgramHeader* tlsPhdr = nullptr; Elf64ProgramHeader* tlsPhdr = nullptr;
uint64_t maxVaddrEnd = 0; uint64_t maxVaddrEnd = 0;
for (uint16_t i = 0; i < hdr->e_phnum; i++) { for (uint16_t i = 0; i < hdr.e_phnum; i++) {
Elf64ProgramHeader* phdr = (Elf64ProgramHeader*)(fileData + hdr->e_phoff + i * hdr->e_phentsize); Elf64ProgramHeader* phdr = &phdrs[i];
if (phdr->p_type == PT_TLS && phdr->p_memsz > 0) { if (phdr->p_type == PT_TLS && phdr->p_memsz > 0) {
tlsPhdr = phdr; tlsPhdr = phdr;
@@ -487,7 +512,7 @@ namespace Sched {
void* page = Memory::g_pfa->AllocateZeroed(); void* page = Memory::g_pfa->AllocateZeroed();
if (page == nullptr) { if (page == nullptr) {
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Out of physical pages"; Kt::KernelLogStream(Kt::ERROR, "ELF") << "Out of physical pages";
Memory::g_heap->Free(fileData); Fs::Vfs::CloseBackendFile(file);
return 0; return 0;
} }
@@ -497,28 +522,15 @@ namespace Sched {
// Map into the process's PML4 with User bit set // Map into the process's PML4 with User bit set
if (!Memory::VMM::Paging::MapUserIn(pml4Phys, physAddr, virtAddr)) { if (!Memory::VMM::Paging::MapUserIn(pml4Phys, physAddr, virtAddr)) {
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Failed to map page"; Kt::KernelLogStream(Kt::ERROR, "ELF") << "Failed to map page";
Memory::g_heap->Free(fileData); Fs::Vfs::CloseBackendFile(file);
return 0; return 0;
} }
// Copy file data that overlaps this page (via HHDM) if (!copyPageFromFile(physAddr, virtAddr, phdr->p_vaddr,
uint64_t pageStart = virtAddr; phdr->p_offset, phdr->p_filesz)) {
uint64_t pageEnd = virtAddr + 0x1000; Kt::KernelLogStream(Kt::ERROR, "ELF") << "Segment read failed";
Fs::Vfs::CloseBackendFile(file);
uint64_t segFileStart = phdr->p_vaddr; return 0;
uint64_t segFileEnd = phdr->p_vaddr + phdr->p_filesz;
uint64_t copyStart = (pageStart > segFileStart) ? pageStart : segFileStart;
uint64_t copyEnd = (pageEnd < segFileEnd) ? pageEnd : segFileEnd;
if (copyStart < copyEnd) {
uint64_t dstOffset = copyStart - pageStart;
uint64_t srcOffset = copyStart - phdr->p_vaddr + phdr->p_offset;
uint64_t copySize = copyEnd - copyStart;
uint8_t* dst = (uint8_t*)Memory::HHDM(physAddr) + dstOffset;
uint8_t* src = fileData + srcOffset;
memcpy(dst, src, copySize);
} }
} }
} }
@@ -550,21 +562,15 @@ namespace Sched {
} }
// Copy the .tdata template bytes that land in this page. // Copy the .tdata template bytes that land in this page.
uint64_t pageStart = virtAddr; if (!copyPageFromFile(physAddr, virtAddr, base,
uint64_t pageEnd = virtAddr + 0x1000; tlsPhdr->p_offset, tlsPhdr->p_filesz)) {
uint64_t dataStart = base; ok = false;
uint64_t dataEnd = base + tlsPhdr->p_filesz; break;
uint64_t copyStart = (pageStart > dataStart) ? pageStart : dataStart;
uint64_t copyEnd = (pageEnd < dataEnd) ? pageEnd : dataEnd;
if (copyStart < copyEnd) {
memcpy((uint8_t*)Memory::HHDM(physAddr) + (copyStart - pageStart),
fileData + tlsPhdr->p_offset + (copyStart - dataStart),
copyEnd - copyStart);
} }
// Write the self-pointer if TP falls in this page. // Write the self-pointer if TP falls in this page.
if (tp >= pageStart && tp + 8 <= pageEnd) { if (tp >= virtAddr && tp + 8 <= virtAddr + 0x1000) {
*(uint64_t*)((uint8_t*)Memory::HHDM(physAddr) + (tp - pageStart)) = tp; *(uint64_t*)((uint8_t*)Memory::HHDM(physAddr) + (tp - virtAddr)) = tp;
} }
} }
@@ -581,8 +587,8 @@ namespace Sched {
} }
} }
uint64_t entryPoint = hdr->e_entry; uint64_t entryPoint = hdr.e_entry;
Memory::g_heap->Free(fileData); Fs::Vfs::CloseBackendFile(file);
return entryPoint; return entryPoint;
} }