234 lines
6.9 KiB
C
234 lines
6.9 KiB
C
/**
|
|
* @file kernel/misc/elf64.c
|
|
* @brief Elf64 parsing tools for modules and static userspace binaries.
|
|
*
|
|
* Provides exec() for Elf64 binaries. Note that the loader only directly
|
|
* loads static binaries; for dynamic binaries, the requested interpreter
|
|
* is loaded, which should generally be /lib/ld.so, which should itself
|
|
* be a static binary. This loader is platform-generic.
|
|
*/
|
|
#include <errno.h>
|
|
#include <kernel/types.h>
|
|
#include <kernel/symboltable.h>
|
|
#include <kernel/printf.h>
|
|
#include <kernel/string.h>
|
|
#include <kernel/elf.h>
|
|
#include <kernel/vfs.h>
|
|
#include <kernel/process.h>
|
|
#include <kernel/mmu.h>
|
|
#include <kernel/misc.h>
|
|
|
|
static Elf64_Shdr * elf_getSection(Elf64_Header * this, Elf64_Word index) {
|
|
return (Elf64_Shdr*)((uintptr_t)this + this->e_shoff + index * this->e_shentsize);
|
|
}
|
|
|
|
void elf_parseModuleFromMemory(void * atAddress) {
|
|
struct Elf64_Header * elfHeader = atAddress;
|
|
|
|
if (elfHeader->e_ident[0] != ELFMAG0 ||
|
|
elfHeader->e_ident[1] != ELFMAG1 ||
|
|
elfHeader->e_ident[2] != ELFMAG2 ||
|
|
elfHeader->e_ident[3] != ELFMAG3) {
|
|
printf("(Not an elf)\n");
|
|
return;
|
|
}
|
|
if (elfHeader->e_ident[EI_CLASS] != ELFCLASS64) {
|
|
printf("(Wrong Elf class)\n");
|
|
return;
|
|
}
|
|
if (elfHeader->e_type != ET_REL) {
|
|
printf("(Not a relocatable object)\n");
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* In order to load a module, we need to link it as an object
|
|
* into the running kernel using the symbol table we integrated
|
|
* into our binary.
|
|
*/
|
|
//char * shrstrtab = (char*)elfHeader + elf_getSection(elfHeader, elfHeader->e_shstrndx)->sh_offset;
|
|
|
|
/**
|
|
* First, we're going to check sections and update their addresses.
|
|
*/
|
|
for (unsigned int i = 0; i < elfHeader->e_shnum; ++i) {
|
|
Elf64_Shdr * shdr = elf_getSection(elfHeader, i);
|
|
if (shdr->sh_type == SHT_NOBITS) {
|
|
if (shdr->sh_size) {
|
|
printf("Warning: Module needs %lu bytes for BSS, we don't have an allocator.\n",
|
|
shdr->sh_size);
|
|
}
|
|
/* otherwise, skip bss */
|
|
} else {
|
|
shdr->sh_addr = (uintptr_t)atAddress + shdr->sh_offset;
|
|
}
|
|
}
|
|
}
|
|
|
|
int elf_exec(const char * path, fs_node_t * file, int argc, const char *const argv[], const char *const env[], int interp) {
|
|
Elf64_Header header;
|
|
|
|
read_fs(file, 0, sizeof(Elf64_Header), (uint8_t*)&header);
|
|
|
|
if (header.e_ident[0] != ELFMAG0 ||
|
|
header.e_ident[1] != ELFMAG1 ||
|
|
header.e_ident[2] != ELFMAG2 ||
|
|
header.e_ident[3] != ELFMAG3) {
|
|
printf("Invalid file: Bad header.\n");
|
|
close_fs(file);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (header.e_ident[EI_CLASS] != ELFCLASS64) {
|
|
printf("(Wrong Elf class)\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* This loader can only handle basic executables. */
|
|
if (header.e_type != ET_EXEC) {
|
|
printf("(Not an executable)\n");
|
|
/* TODO: what about DYN? */
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (file->mask & 0x800) {
|
|
/* setuid */
|
|
this_core->current_process->user = file->uid;
|
|
}
|
|
|
|
/* First check if it is dynamic and needs an interpreter */
|
|
for (int i = 0; i < header.e_phnum; ++i) {
|
|
Elf64_Phdr phdr;
|
|
read_fs(file, header.e_phoff + header.e_phentsize * i, sizeof(Elf64_Phdr), (uint8_t*)&phdr);
|
|
if (phdr.p_type == PT_DYNAMIC) {
|
|
close_fs(file);
|
|
unsigned int nargc = argc + 3;
|
|
const char * args[nargc+1]; /* oh yeah, great, a stack-allocated dynamic array... wonderful... */
|
|
args[0] = "ld.so";
|
|
args[1] = "-e";
|
|
args[2] = strdup(this_core->current_process->name);
|
|
int j = 3;
|
|
for (int i = 0; i < argc; ++i, ++j) {
|
|
args[j] = argv[i];
|
|
}
|
|
args[j] = NULL;
|
|
fs_node_t * file = kopen("/lib/ld.so",0); /* FIXME PT_INTERP value */
|
|
if (!file) return -EINVAL;
|
|
return elf_exec(NULL, file, nargc, args, env, 1);
|
|
}
|
|
}
|
|
|
|
uintptr_t execBase = -1;
|
|
uintptr_t heapBase = 0;
|
|
|
|
mmu_set_directory(NULL);
|
|
process_release_directory(this_core->current_process->thread.page_directory);
|
|
this_core->current_process->thread.page_directory = malloc(sizeof(page_directory_t));
|
|
this_core->current_process->thread.page_directory->refcount = 1;
|
|
spin_init(this_core->current_process->thread.page_directory->lock);
|
|
this_core->current_process->thread.page_directory->directory = mmu_clone(NULL);
|
|
mmu_set_directory(this_core->current_process->thread.page_directory->directory);
|
|
|
|
for (int i = 0; i < header.e_phnum; ++i) {
|
|
Elf64_Phdr phdr;
|
|
read_fs(file, header.e_phoff + header.e_phentsize * i, sizeof(Elf64_Phdr), (uint8_t*)&phdr);
|
|
if (phdr.p_type == PT_LOAD) {
|
|
for (uintptr_t i = phdr.p_vaddr; i < phdr.p_vaddr + phdr.p_memsz; i += 0x1000) {
|
|
union PML * page = mmu_get_page(i, MMU_GET_MAKE);
|
|
mmu_frame_allocate(page, MMU_FLAG_WRITABLE);
|
|
mmu_invalidate(i);
|
|
}
|
|
|
|
read_fs(file, phdr.p_offset, phdr.p_filesz, (void*)phdr.p_vaddr);
|
|
for (size_t i = phdr.p_filesz; i < phdr.p_memsz; ++i) {
|
|
*(char*)(phdr.p_vaddr + i) = 0;
|
|
}
|
|
|
|
if (phdr.p_vaddr + phdr.p_memsz > heapBase) {
|
|
heapBase = phdr.p_vaddr + phdr.p_memsz;
|
|
}
|
|
|
|
if (phdr.p_vaddr < execBase) {
|
|
execBase = phdr.p_vaddr;
|
|
}
|
|
}
|
|
/* TODO: Should also be setting up TLS PHDRs. */
|
|
}
|
|
|
|
this_core->current_process->image.heap = (heapBase + 0xFFF) & (~0xFFF);
|
|
this_core->current_process->image.entry = header.e_entry;
|
|
|
|
close_fs(file);
|
|
|
|
// arch_set_...?
|
|
|
|
/* Map stack space */
|
|
uintptr_t userstack = 0x800000000000;
|
|
for (uintptr_t i = userstack - 64 * 0x400; i < userstack; i += 0x1000) {
|
|
union PML * page = mmu_get_page(i, MMU_GET_MAKE);
|
|
mmu_frame_allocate(page, MMU_FLAG_WRITABLE);
|
|
mmu_invalidate(i);
|
|
}
|
|
#define PUSH(type,val) do { \
|
|
userstack -= sizeof(type); \
|
|
while (userstack & (sizeof(type)-1)) userstack--; \
|
|
*((type*)userstack) = (val); \
|
|
} while (0)
|
|
#define PUSHSTR(s) do { \
|
|
ssize_t l = strlen(s); \
|
|
do { \
|
|
PUSH(char,s[l]); \
|
|
l--; \
|
|
} while (l>=0); \
|
|
} while (0)
|
|
|
|
/* XXX This should probably be done backwards so we can be
|
|
* sure that we're aligning the stack properly. It
|
|
* doesn't matter too much as our crt0+libc align it
|
|
* correctly for us and environ + auxv detection is
|
|
* based on the addresses of argv, not the actual
|
|
* stack pointer, but it's still weird. */
|
|
char * argv_ptrs[argc];
|
|
for (int i = 0; i < argc; ++i) {
|
|
PUSHSTR(argv[i]);
|
|
argv_ptrs[i] = (char*)userstack;
|
|
}
|
|
|
|
/* Now push envp */
|
|
int envc = 0;
|
|
char ** envpp = (char**)env;
|
|
while (*envpp) {
|
|
envc++;
|
|
envpp++;
|
|
}
|
|
char * envp_ptrs[envc];
|
|
for (int i = 0; i < envc; ++i) {
|
|
PUSHSTR(env[i]);
|
|
envp_ptrs[i] = (char*)userstack;
|
|
}
|
|
|
|
PUSH(uintptr_t, 0);
|
|
PUSH(uintptr_t, this_core->current_process->user);
|
|
PUSH(uintptr_t, 11); /* AT_UID */
|
|
PUSH(uintptr_t, this_core->current_process->real_user);
|
|
PUSH(uintptr_t, 12); /* AT_EUID */
|
|
PUSH(uintptr_t, 0);
|
|
|
|
PUSH(uintptr_t, 0); /* envp NULL */
|
|
for (int i = envc; i > 0; i--) {
|
|
PUSH(char*,envp_ptrs[i-1]);
|
|
}
|
|
char ** _envp = (char**)userstack;
|
|
PUSH(uintptr_t, 0); /* argv NULL */
|
|
for (int i = argc; i > 0; i--) {
|
|
PUSH(char*,argv_ptrs[i-1]);
|
|
}
|
|
char ** _argv = (char**)userstack;
|
|
PUSH(uintptr_t, argc);
|
|
|
|
arch_set_kernel_stack(this_core->current_process->image.stack);
|
|
arch_enter_user(header.e_entry, argc, _argv, _envp, userstack);
|
|
|
|
return -EINVAL;
|
|
}
|