ELF: refactor loader

This commit is contained in:
Matheus
2022-08-27 20:27:14 -03:00
parent 023937809d
commit edba446ba4
3 changed files with 147 additions and 215 deletions
+9
View File
@@ -0,0 +1,9 @@
#ifndef _ARCH_ELFMAGIC_INCLUDE
#define _ARCH_ELFMAGIC_INCLUDE
#define ARCH_ELF_BITS 2
#define ARCH_ELF_ISA 0x3E
#define ARCH_ELF_ENDIANNESS 1
#endif
+14 -4
View File
@@ -7,9 +7,6 @@
#include <stdint.h> #include <stdint.h>
#define ELF_MAGIC 0x464c457f // 0x7F ELF #define ELF_MAGIC 0x464c457f // 0x7F ELF
#define ELF_BITS_64 2
#define ELF_ISA_X86_64 0x3E
#define ELF_ENDIANNESS_LITTLE 1
#define ELF_RELOCATABLE 1 #define ELF_RELOCATABLE 1
#define ELF_EXECUTABLE 2 #define ELF_EXECUTABLE 2
@@ -26,7 +23,7 @@
#define ELF_FLAG_WRITABLE 2 #define ELF_FLAG_WRITABLE 2
#define ELF_FLAG_READABLE 4 #define ELF_FLAG_READABLE 4
#define STACK_TOP 0x800000000000 #define STACK_TOP (void*)0x800000000000
#define AT_NULL 0 #define AT_NULL 0
#define AT_PHDR 3 #define AT_PHDR 3
@@ -34,6 +31,19 @@
#define AT_PHNUM 5 #define AT_PHNUM 5
#define AT_ENTRY 9 #define AT_ENTRY 9
typedef struct{
uint64_t a_type;
uint64_t a_val;
} auxv64_t;
typedef struct {
auxv64_t phdr;
auxv64_t phnum;
auxv64_t phent;
auxv64_t entry;
auxv64_t null;
} auxv64_list;
typedef struct{ typedef struct{
uint32_t magic; uint32_t magic;
uint8_t bits; uint8_t bits;
+109 -196
View File
@@ -4,16 +4,7 @@
#include <errno.h> #include <errno.h>
#include <string.h> #include <string.h>
#include <arch/cls.h> #include <arch/cls.h>
#include <arch/elfmagic.h>
typedef struct{
uint64_t a_type;
uint64_t a_val;
} auxv64_t;
typedef struct {
auxv64_t phdr;
auxv64_t null;
} auxv64_list;
static size_t phflagtommuflag(size_t phflags){ static size_t phflagtommuflag(size_t phflags){
@@ -33,31 +24,71 @@ static size_t phflagtommuflag(size_t phflags){
} }
static bool check_header64(elf_header64* header){ static bool check_header64(elf_header64* header){
// XXX throw this into arch
if(header->magic != ELF_MAGIC) if(header->magic != ELF_MAGIC)
return false; return false;
if(header->bits != ELF_BITS_64) if(header->bits != ARCH_ELF_BITS)
return false; return false;
if(header->endianness != ELF_ENDIANNESS_LITTLE) if(header->endianness != ARCH_ELF_ENDIANNESS)
return false; return false;
if(header->isa != ELF_ISA_X86_64) if(header->isa != ARCH_ELF_ISA)
return false; return false;
return true; return true;
} }
static void* stacksetup(char* interp, char** argv, char** env, elf_header64 header, elf_ph64* phs){ static int load(vnode_t* node, elf_ph64 ph){
size_t mmuflags = phflagtommuflag(ph.flags);
// allocate memory for the section
if(!vmm_allocnowat((void*)ph.memaddr, mmuflags, ph.msize))
return ENOMEM;
// set up temporary mmu flags to copy data
size_t pagesize = ph.msize / PAGE_SIZE + (ph.msize % PAGE_SIZE ? 1 : 0);
arch_mmu_changeflags(arch_getcls()->context->context, (void*)ph.memaddr, ARCH_MMU_MAP_READ | ARCH_MMU_MAP_WRITE, pagesize);
int err;
size_t readc = vfs_read(&err, node, (void*)ph.memaddr, ph.fsize, ph.offset);
if(err)
return err;
if(readc != ph.fsize)
return EINVAL;
// zero if needed
size_t zerocount = ph.msize - ph.fsize;
if(zerocount){
void* zeroaddr = (void*)ph.memaddr + ph.fsize;
memset(zeroaddr, 0, zerocount);
}
// reset proper mmu flags
arch_mmu_changeflags(arch_getcls()->context->context, (void*)ph.memaddr, mmuflags, pagesize);
return 0;
}
static void* stacksetup(char** argv, char** env, auxv64_list auxv){
size_t argc = 0; size_t argc = 0;
size_t envc = 0; size_t envc = 0;
size_t initialsize = 0; size_t initialsize = 0;
size_t argdatasize = 0; size_t argdatasize = 0;
size_t envdatasize = 0; size_t envdatasize = 0;
size_t auxvsize = 5*16; // FIXME better way of keeping track of this
while(argv[argc]){ while(argv[argc]){
argdatasize += strlen(argv[argc]) + 1; argdatasize += strlen(argv[argc]) + 1;
@@ -87,46 +118,18 @@ static void* stacksetup(char* interp, char** argv, char** env, elf_header64 head
char* argdatastart = STACK_TOP - argdatasize; char* argdatastart = STACK_TOP - argdatasize;
char* envdatastart = argdatastart - envdatasize; char* envdatastart = argdatastart - envdatasize;
auxv64_t* auxvstart = (void*)(((uintptr_t)envdatastart & (~0xF)) - auxvsize); auxv64_list* auxvstart = (void*)(((uintptr_t)envdatastart & (~0xF)) - sizeof(auxv));
// count + 1 because of a null entry // count + 1 because of a null entry
char** envstart = (void*)auxvstart - (envc + 1)*sizeof(char*); char** envstart = (void*)auxvstart - (envc + 1)*sizeof(char*);
char** argstart = (void*)envstart - (argc + 1)*sizeof(char*); char** argstart = (void*)envstart - (argc + 1)*sizeof(char*);
size_t* argcptr = (void*)argstart - sizeof(size_t); size_t* argcptr = (void*)argstart - sizeof(size_t);
// auxv
// AT_PHDR
auxvstart->a_type = AT_PHDR;
auxvstart->a_val = (uint64_t)phs;
auxvstart++;
// AT_PHENT
auxvstart->a_type = AT_PHENT;
auxvstart->a_val = header.ph_size;
auxvstart++;
// AT_PHNUM
auxvstart->a_type = AT_PHNUM;
auxvstart->a_val = header.ph_count;
auxvstart++;
// AT_ENTRY
auxvstart->a_type = AT_ENTRY;
auxvstart->a_val = header.entry;
auxvstart++;
// AT_NULL
auxvstart->a_type = AT_NULL;
auxvstart->a_val = 0;
// copy the data // copy the data
memcpy(auxvstart, &auxv, sizeof(auxv));
for(size_t i = 0; i < argc; ++i){ for(size_t i = 0; i < argc; ++i){
strcpy(argdatastart, argv[i]); strcpy(argdatastart, argv[i]);
@@ -155,205 +158,115 @@ static void* stacksetup(char* interp, char** argv, char** env, elf_header64 head
int elf_load(thread_t* thread, vnode_t* node, char** argv, char** env){ int elf_load(thread_t* thread, vnode_t* node, char** argv, char** env){
elf_header64 header; elf_header64 header;
elf_header64 progheader; elf_ph64 phbuff;
vnode_t* prognode = node;
elf_ph64 ph;
int err = 0; int err = 0;
int readc = vfs_read(&err, node, &header, sizeof(elf_header64), 0); int readc = vfs_read(&err, node, &header, sizeof(elf_header64), 0);
char* interp = NULL;
if(err) if(err)
return err; return err;
if(readc != sizeof(elf_header64) || (!check_header64(&header))) if(readc != sizeof(elf_header64) || (!check_header64(&header)))
return EINVAL; return EINVAL;
progheader = header; // incase an interpreter exists auxv64_list auxv;
// find out if this needs an interpreter auxv.null.a_type = AT_NULL;
// and also save the executable's program headers auxv.phdr.a_type = AT_PHDR;
// for auxv auxv.phnum.a_type = AT_PHNUM;
auxv.phent.a_type = AT_PHENT;
auxv.entry.a_type = AT_ENTRY;
elf_ph64* progphs; auxv.phnum.a_val = header.ph_count;
auxv.phent.a_val = header.ph_size;
auxv.entry.a_val = header.entry;
char* interp = NULL; // interpreter name location in program image
for(size_t pos = header.ph_pos, ph = 0; ph < header.ph_count; ++ph, pos += header.ph_size){
readc = vfs_read(&err, node, &phbuff, header.ph_size, pos);
for(size_t currentph = 0; currentph < header.ph_count; ++currentph){
readc = vfs_read(&err, node, &ph, header.ph_size, header.ph_pos + currentph*header.ph_size);
if(err) if(err)
return err; return err;
if(readc != header.ph_size) if(readc != header.ph_size)
return EINVAL; return EINVAL;
if(ph.type == PH_TYPE_PHDR) switch(phbuff.type){
progphs = (elf_ph64*)ph.memaddr; case PH_TYPE_INTERPRETER:
interp = (char*)phbuff.memaddr;
if(ph.type == PH_TYPE_INTERPRETER){ break;
case PH_TYPE_PHDR:
// load the name and elf header of the interpreter auxv.phdr.a_val = phbuff.memaddr;
break;
progheader = header; case PH_TYPE_LOAD:
err = load(node, phbuff);
interp = alloc(ph.fsize + 1); if(err)
if(!interp) return err;
return ENOMEM; break;
readc = vfs_read(&err, node, interp, ph.fsize, ph.offset);
if(err){
free(interp);
return err;
}
if(ph.fsize != readc){
free(interp);
return EINVAL;
}
err = vfs_open(&node, thread->proc->root, interp);
if(err){
free(interp);
return err;
}
readc = vfs_read(&err, node, &header, sizeof(elf_header64), 0);
if(err)
goto _fail;
if(readc != sizeof(elf_header64) || (!check_header64(&header))){
err = EINVAL;
goto _fail;
}
} }
}
// load program into memory
for(size_t currentph = 0; currentph < header.ph_count; ++currentph){
// read ph
readc = vfs_read(&err, node, &ph, header.ph_size, header.ph_pos + currentph*header.ph_size);
if(err) goto _fail;
if(readc != header.ph_size){
err = EINVAL;
goto _fail;
}
if(ph.type != PH_TYPE_LOAD)
continue;
if(!vmm_allocnowat((void*)ph.memaddr, phflagtommuflag(ph.flags), ph.msize)){
err = ENOMEM;
goto _fail;
}
// set up temporary mmu flags to copy data
arch_mmu_changeflags(arch_getcls()->context->context, ph.memaddr, ARCH_MMU_MAP_READ | ARCH_MMU_MAP_WRITE, ph.msize / PAGE_SIZE + (ph.msize % PAGE_SIZE ? 1 : 0));
// read program data
readc = vfs_read(&err, node, (void*)ph.memaddr, ph.fsize, ph.offset);
if(err)
goto _fail;
if(readc != ph.fsize){
err = EINVAL;
goto _fail;
}
// zero if needed
size_t zerocount = ph.msize - ph.fsize;
if(zerocount){
void* zeroaddr = ph.memaddr + ph.fsize;
memset(zeroaddr, 0, zerocount);
}
// reset proper mmu flags
arch_mmu_changeflags(arch_getcls()->context->context, ph.memaddr, phflagtommuflag(ph.flags), ph.msize / PAGE_SIZE + (ph.msize % PAGE_SIZE ? 1 : 0));
} }
// also load the executable memory image if we're using an interpreter // load the interpreter if needed
vnode_t* interfile;
if(interp){ if(interp){
for(size_t currentph = 0; currentph < progheader.ph_count; ++currentph){
// read ph err = vfs_open(&interfile, thread->proc->root, interp);
readc = vfs_read(&err, prognode, &ph, progheader.ph_size, progheader.ph_pos + currentph*progheader.ph_size); if(err)
return err;
if(err) goto _fail; readc = vfs_read(&err, interfile, &header, sizeof(elf_header64), 0);
if(readc != progheader.ph_size){ if(err)
err = EINVAL; goto _interpfail;
goto _fail;
}
if(ph.type != PH_TYPE_LOAD) if(readc != sizeof(elf_header64)){
continue; err = EINVAL;
goto _interpfail;
}
if(!vmm_allocnowat((void*)ph.memaddr, phflagtommuflag(ph.flags), ph.msize)){ for(size_t pos = header.ph_pos, ph = 0; ph < header.ph_count; ++ph, pos += header.ph_size){
err = ENOMEM;
goto _fail;
}
// set up temporary mmu flags to copy data readc = vfs_read(&err, interfile, &phbuff, header.ph_size, pos);
arch_mmu_changeflags(arch_getcls()->context->context, ph.memaddr, ARCH_MMU_MAP_READ | ARCH_MMU_MAP_WRITE, ph.msize / PAGE_SIZE + (ph.msize % PAGE_SIZE ? 1 : 0));
// read program data
readc = vfs_read(&err, prognode, (void*)ph.memaddr, ph.fsize, ph.offset);
if(err) if(err)
goto _fail; goto _interpfail;
if(readc != ph.fsize){ if(readc != header.ph_size){
err = EINVAL; err = EINVAL;
goto _fail; goto _interpfail;
} }
// zero if needed if(phbuff.type != PH_TYPE_LOAD)
continue;
size_t zerocount = ph.msize - ph.fsize; err = load(interfile, phbuff);
if(zerocount){ if(err)
void* zeroaddr = ph.memaddr + ph.fsize; goto _interpfail;
memset(zeroaddr, 0, zerocount);
}
// reset proper mmu flags }
arch_mmu_changeflags(arch_getcls()->context->context, ph.memaddr, phflagtommuflag(ph.flags), ph.msize / PAGE_SIZE + (ph.msize % PAGE_SIZE ? 1 : 0)); vfs_close(interfile);
}
} }
void* stack = stacksetup(interp, argv, env, progheader, progphs); void* stack = stacksetup(argv, env, auxv);
arch_regs_setupuser(thread->regs, (void*)header.entry, stack, true); arch_regs_setupuser(thread->regs, (void*)header.entry, stack, true);
return 0; return 0;
_fail: _interpfail:
if(interp){ vfs_close(interfile);
free(interp); return err;
vfs_close(node);
}
return err;
} }