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:
@@ -12,4 +12,4 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define MONTAUK_BUILD_NUMBER 22
|
||||
#define MONTAUK_BUILD_NUMBER 23
|
||||
|
||||
@@ -433,33 +433,58 @@ namespace Sched {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Read entire file into a heap buffer
|
||||
uint8_t* fileData = (uint8_t*)Memory::g_heap->Request(fileSize);
|
||||
if (fileData == nullptr) {
|
||||
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Failed to allocate " << fileSize << " bytes for file";
|
||||
// Stream the load: headers first, then each segment page copied
|
||||
// straight from the file into freshly mapped process pages. The
|
||||
// previous whole-file heap buffer needed fileSize bytes of
|
||||
// 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);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Fs::Vfs::ReadBackendFile(file, fileData, 0, fileSize);
|
||||
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);
|
||||
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);
|
||||
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;
|
||||
uint64_t maxVaddrEnd = 0;
|
||||
for (uint16_t i = 0; i < hdr->e_phnum; i++) {
|
||||
Elf64ProgramHeader* phdr = (Elf64ProgramHeader*)(fileData + hdr->e_phoff + i * hdr->e_phentsize);
|
||||
for (uint16_t i = 0; i < hdr.e_phnum; i++) {
|
||||
Elf64ProgramHeader* phdr = &phdrs[i];
|
||||
|
||||
if (phdr->p_type == PT_TLS && phdr->p_memsz > 0) {
|
||||
tlsPhdr = phdr;
|
||||
@@ -487,7 +512,7 @@ namespace Sched {
|
||||
void* page = Memory::g_pfa->AllocateZeroed();
|
||||
if (page == nullptr) {
|
||||
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Out of physical pages";
|
||||
Memory::g_heap->Free(fileData);
|
||||
Fs::Vfs::CloseBackendFile(file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -497,28 +522,15 @@ namespace Sched {
|
||||
// Map into the process's PML4 with User bit set
|
||||
if (!Memory::VMM::Paging::MapUserIn(pml4Phys, physAddr, virtAddr)) {
|
||||
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Failed to map page";
|
||||
Memory::g_heap->Free(fileData);
|
||||
Fs::Vfs::CloseBackendFile(file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Copy file data that overlaps this page (via HHDM)
|
||||
uint64_t pageStart = virtAddr;
|
||||
uint64_t pageEnd = virtAddr + 0x1000;
|
||||
|
||||
uint64_t segFileStart = phdr->p_vaddr;
|
||||
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);
|
||||
if (!copyPageFromFile(physAddr, virtAddr, phdr->p_vaddr,
|
||||
phdr->p_offset, phdr->p_filesz)) {
|
||||
Kt::KernelLogStream(Kt::ERROR, "ELF") << "Segment read failed";
|
||||
Fs::Vfs::CloseBackendFile(file);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -550,21 +562,15 @@ namespace Sched {
|
||||
}
|
||||
|
||||
// Copy the .tdata template bytes that land in this page.
|
||||
uint64_t pageStart = virtAddr;
|
||||
uint64_t pageEnd = virtAddr + 0x1000;
|
||||
uint64_t dataStart = base;
|
||||
uint64_t dataEnd = base + tlsPhdr->p_filesz;
|
||||
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);
|
||||
if (!copyPageFromFile(physAddr, virtAddr, base,
|
||||
tlsPhdr->p_offset, tlsPhdr->p_filesz)) {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
|
||||
// Write the self-pointer if TP falls in this page.
|
||||
if (tp >= pageStart && tp + 8 <= pageEnd) {
|
||||
*(uint64_t*)((uint8_t*)Memory::HHDM(physAddr) + (tp - pageStart)) = tp;
|
||||
if (tp >= virtAddr && tp + 8 <= virtAddr + 0x1000) {
|
||||
*(uint64_t*)((uint8_t*)Memory::HHDM(physAddr) + (tp - virtAddr)) = tp;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -576,13 +582,13 @@ namespace Sched {
|
||||
outTls->align = align;
|
||||
} else {
|
||||
Kt::KernelLogStream(Kt::ERROR, "ELF")
|
||||
<< "PT_TLS setup failed (out of pages?), "
|
||||
<< "PT_TLS setup failed (out of pages?), "
|
||||
<< "process will run without TLS";
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t entryPoint = hdr->e_entry;
|
||||
Memory::g_heap->Free(fileData);
|
||||
uint64_t entryPoint = hdr.e_entry;
|
||||
Fs::Vfs::CloseBackendFile(file);
|
||||
|
||||
return entryPoint;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user