This defines required information such as the type of architecture (Support checker) and where the process entry is for starting it. Also the offsets of the Program headers
This is very important as it describes the sections of the program that are needed to be loaded into memory (vaddr) and where about they are in the elf file (paddr) and the specific sizes.
The type is the important factor in understanding this. There are 3 main types for this.
#define PT_NULL 0 // Ignore
#define PT_LOAD 1 // Defines that it needs to be loaded into memory
// GNU
#define PT_GNU_STACK 0x6474E551 // Defines the requirement of no execute bit or not.
Loading
In order to load the ELF, I use two loops. One loop is in order to allocate all the memory using the Program Headers. (We do this as if we did it while we load some memory issues occur as some memory isn't allocated.)
{
for (uint16_t i = 0; i < elfHdr.phNum; i++) {
ELF64ProgramHeader elfPHdr = *((ELF64ProgramHeader*)(elf + elfHdr.phOff + i * elfHdr.phEntrySize));
if(elfPHdr.memSize == 0 || elfPHdr.type != PT_LOAD) continue;
else { // Load the data anyways
uint64_t size_alligned_down = elfPHdr.memSize + (elfPHdr.vaddr & PAGE_SIZE_4K);
// Calculate page count
uint64_t pages = (size_alligned_down + PAGE_SIZE_4K - 1) / PAGE_SIZE_4K;
// Calculate VAddr
uint64_t virtAddress = base + elfPHdr.vaddr - (elfPHdr.vaddr & 0xFFF); // Remove end bytes
size_t flags = 0;
flags |= (size_t)PAGE_PRESENT;
flags |= (size_t)PAGE_USER;
flags |= (size_t)PAGE_WRITABLE;
// Allocate the region
Drivers::Video::Print("ELF Allocator: Allocaing range of %x to %x\n",
virtAddress, virtAddress + pages * PAGE_SIZE_4K);
Paging::Region* r = proc->allocator->AllocateMemory(pages * PAGE_SIZE_4K, virtAddress, true, true, flags);
}
}
}
And the other stage is actually loading the data into memory, and to check if we need the No execute. We also use it to check if the process is static or dynamic and so if we need to load the linker as well for library loading.
{ // DATA Loader
for (uint16_t i = 0; i < elfHdr.phNum; i++) {
ELF64ProgramHeader elfPHdr = *((ELF64ProgramHeader*)(elf + elfHdr.phOff + i * elfHdr.phEntrySize));
Drivers::Video::Print("Program header: type=%x, offset=%x filesz=%x, memsz=%x\n",
elfPHdr.type, elfPHdr.offset, elfPHdr.fileSize, elfPHdr.memSize);
if(elfPHdr.memSize == 0) Drivers::Video::Print(" ! Empty PHdr\n");
else { // Load the data anyways
// Calculate size
uint64_t size_alligned_down = elfPHdr.memSize + (elfPHdr.vaddr & PAGE_SIZE_4K);
// Calculate page count
uint64_t pages = (size_alligned_down + PAGE_SIZE_4K - 1) / PAGE_SIZE_4K;
// Loading
uint64_t virtAddress = base + elfPHdr.vaddr - (elfPHdr.vaddr & 0xFFF); // Remove end bytes
uint64_t offset = elfPHdr.vaddr & 0xFFF; // Calculate its offset into the allocated memory
Drivers::Video::Print("Memcpy data from %x into %x of size %x\n",
elf + elfPHdr.offset, (uint8_t*)(virtAddress + offset), elfPHdr.fileSize);
asm volatile("cli; mov %%rax, %%cr3" ::"a"(proc->GetPageMap()->pml4Phys) : "memory");
memcpy((uint8_t*)(virtAddress + offset), elf + elfPHdr.offset, elfPHdr.fileSize);
asm volatile("mov %%rax, %%cr3; sti" ::"a"(pml4Phys) : "memory");
Drivers::Video::Print(" Loaded program header (%x Bytes) at offset %x into %x\n",
elfPHdr.fileSize, elfPHdr.offset, base + elfPHdr.vaddr);
}
if (elfPHdr.type == PT_PHDR) {
Drivers::Video::Print(" ! PHDR found, saving address: %x!\n", elfPHdr.vaddr);
elfInfo.pHdrSegment = base + elfPHdr.vaddr;
}
else if (elfPHdr.type == PT_GNU_STACK){
Drivers::Video::Print(" ! Stack permission set by GNU_STACK Program header!: %Y\n", !(elfPHdr.flags & PF_X));
if (!(elfPHdr.flags & PF_X))
stack_flags |= PAGE_NX;
} else if (elfPHdr.type == PT_INTERP) { // Linker location
linkPath = (char*)Memory::Heap::malloc(elfPHdr.fileSize + 1);
strncpy(linkPath, (char*)(elf + elfPHdr.offset), elfPHdr.fileSize);
linkPath[elfPHdr.fileSize] = 0; // Null terminate the path
elfInfo.linkerPath = linkPath;
Drivers::Video::Print(" ! Linker path found: %s!\n", linkPath);
}
}
}