#include <string.h>
#include <elf.h>
#include <elfloader.h>
#include <stdio.h>
#include <vga_term.h>
// Checks whether the memory at the start of the pointer given
// is the ELF magic number.
int checkMagic(const void *e) {
const char *magic = ELFMAG;
int r = memcmp(e, (const void *) magic, 4);
//if(r == 0) putStr("r is true");
//else putStr("r is false");
return r;
}
/*
void loadSectionHeader(const Elf64_Shdr* s) {
putStr("Loading section header...");
}
*/
// Loads the program from a given ELF header structure.
// beginning = beginning of exe.
// All entries in the program table specifying where things
// are happen relative to that.
void loadProgramHeader(const Elf64_Phdr *p, const uint8_t *const beginning) {
switch(p->p_type) {
// Keep on trucking
case PT_LOAD:
//putStr("Actual load section");
break;
// Things we don't care about
case PT_NULL:
//putStr("Null section, skipping");
return;
case PT_NOTE:
//putStr("Note section, skipping");
return;
case PT_PHDR:
//putStr("Program header, skipping");
return;
// Cases not handled.
case PT_DYNAMIC:
//putStr("Dynamic section, skipping");
return;
case PT_INTERP:
//putStr("Interp section, skipping");
return;
case PT_SHLIB:
//putStr("Shlib section, skipping");
return;
default:
//putStr("Something else, skipping");
return;
}
// XXX: Alignment
const uint8_t *loadFrom = beginning + p->p_offset;
const uint8_t *loadTo = (uint8_t *) p->p_vaddr;
// p_align does not mean what you think it does.
//const char* loadTo = (char*)((uintptr_t)(p->p_vaddr) & ~(uintptr_t)(align-1)) ;
const size_t length = p->p_filesz;
const size_t memLength = p->p_memsz;
const uint8_t *bssStart = loadTo + length;
const size_t bssLength = memLength - length;
kprintf("Loading 0x%x bytes from 0x%x to 0x%x", length, loadFrom, loadTo);
// We don't need p_memsz right now because there's no paging;
// all we have to do is leave a gap, which will happen anyway
// if the ELF file is not bogus.
memmove((void *) loadTo, (void *) loadFrom, length);
memset((void *) bssStart, 0, bssLength);
}
// Loads an ELF64 file to the appropriate location it requests
// Returns 0 if the load was successful, nonzero otherwise.
// Takes a pointer to the very beginning of the ELF data.
// XXX: TODO: Make this return some reasonable error values.
// Not executable file, wrong machine type, malformed ELF file (such as
// exe file with no program header or entry point), wrong version,
int loadELF64(const void *from, kernel_entry_point_t *entry) {
if(checkMagic(from)) {
return -1;
}
// Lots of the things measure as byte offsets from the beginning
// of the file, so it's handy to have a reference to that.
const Elf64_Ehdr *header = (Elf64_Ehdr *) from;
const uint8_t *const headerAddress = (uint8_t *) from;
if(header->e_type != ET_EXEC) {
return -1;
}
if(header->e_machine != EM_X86_64) {
return -1;
}
//const void* const entry = (void*) header->e_entry;
Elf64_Phdr *pheader;
// e_phoff == 0 indicates no program header; ie, not an executable file.
if(header->e_phoff == 0) {
return -1;
}
pheader = (Elf64_Phdr *)(headerAddress + header->e_phoff);
// Sections are for linking only! I don't have to worry about them!
/*
const Elf64_Shdr* sheader;
// Again, 0 indicates no section headers.
if(header->e_shoff == 0) { return -1; }
sheader = (Elf64_Shdr*) (headerAddress + header->e_shoff);
*/
// I guess these aren't strictly necessary, but it makes it clearer
// to me.
uint16_t phEntrySize = header->e_phentsize;
uint16_t phEntryCount = header->e_phnum;
/*
int shEntrySize = header->e_shentsize;
int shEntryCount = header->e_shnum;
char display[80];
snprintf(display, 80, "Number of sections and program headers: %d, %d",
shEntryCount, phEntryCount);
putStr(display);
for(i = 0; i < shEntryCount; i++) {
const char* const current = ((char*) sheader) + (i*shEntrySize);
loadSectionHeader((Elf64_Shdr*) current);
}
*/
for(int i = 0; i < phEntryCount; i++) {
const char *const current = ((char *) pheader) + (i * phEntrySize);
loadProgramHeader((Elf64_Phdr *) current, headerAddress);
}
*entry = (kernel_entry_point_t) header->e_entry;
return 0;
}
int64_t elf64End(void *from) {
if(checkMagic(from)) {
return -1;
}
// Lots of the things measure as byte offsets from the beginning
// of the file, so it's handy to have a reference to that.
const Elf64_Ehdr *header = (Elf64_Ehdr *) from;
const uint8_t *const headerAddress = (uint8_t *) from;
if(header->e_type != ET_EXEC) {
return -1;
}
if(header->e_machine != EM_X86_64) {
return -1;
}
Elf64_Phdr *pheader;
// e_phoff == 0 indicates no program header; ie, not an executable file.
if(header->e_phoff == 0) {
return -1;
}
pheader = (Elf64_Phdr *)(headerAddress + header->e_phoff);
// I guess these aren't strictly necessary, but it makes it clearer
// to me.
uint32_t phEntrySize = header->e_phentsize;
uint32_t phEntryCount = header->e_phnum;
int64_t finalEndAddress = 0;
// char buf[80];
for(int i = 0; i < phEntryCount; i++) {
const char *const current = ((char *) pheader) + (i * phEntrySize);
Elf64_Phdr *currentHeader = (Elf64_Phdr *) current;
if(currentHeader->p_type != PT_LOAD) {
continue;
}
/*
snprintf(buf, 80, "Entry %d at 0x%X: finalEnd 0x%X, vaddr 0x%X, size 0x%X",
i, (int) currentHeader, (int) finalEndAddress, (int) currentHeader->p_vaddr,
(int) currentHeader->p_memsz);
putStr(buf);
*/
int64_t currentBase = currentHeader->p_vaddr;
int64_t currentEnd = currentBase + currentHeader->p_memsz;
if(finalEndAddress < currentEnd) {
finalEndAddress = currentEnd;
}
}
// snprintf(buf, 80, "Final end is now 0x%X", (int) finalEndAddress);
// putStr(buf);
return finalEndAddress;
}