ptos: ex: Add loader dispatch + ELF64 loader
Signed-off-by: Chloe M <chloe@mensia.org>
This commit is contained in:
@@ -0,0 +1,216 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2026, Chloe M.
|
||||||
|
* Provided under the BSD-3 clause.
|
||||||
|
*
|
||||||
|
* Description: ELF64 loader
|
||||||
|
* Author: Chloe M.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ex/elf64.h>
|
||||||
|
#include <ex/pool.h>
|
||||||
|
#include <hal/page.h>
|
||||||
|
#include <ptapi/ptmm.h>
|
||||||
|
#include <elf.h>
|
||||||
|
#include <mm/vm.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* When allocating ELF related pools */
|
||||||
|
#define ELF_POOL_TAG 'ELF'
|
||||||
|
|
||||||
|
/* Maximum loadable program headers */
|
||||||
|
#define MAX_PHDRS 64
|
||||||
|
|
||||||
|
/* Prototypes */
|
||||||
|
static PT_STATUS Elf64Load(MMU_VAS *Vas, VOID *Data, LOADER_PROGRAM *Result);
|
||||||
|
|
||||||
|
/* ELF64 operations */
|
||||||
|
LOADER_OPS gElf64Ops = {
|
||||||
|
.LoadProgram = Elf64Load
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Program data associated with a loader
|
||||||
|
*
|
||||||
|
* @LoadedSegments: Segments that have been loaded
|
||||||
|
* @SegmentCount: Number of loaded segments
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
MM_RANGE LoadedSegments[MAX_PHDRS];
|
||||||
|
USIZE SegmentCount;
|
||||||
|
} ELF64_PROGDATA;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Verify the header of an ELF64 binary to see if it is
|
||||||
|
* compatible with PTOS.
|
||||||
|
*/
|
||||||
|
static BOOLEAN
|
||||||
|
Elf64Verify(const Elf64_Ehdr *Eh)
|
||||||
|
{
|
||||||
|
if (Eh == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RtlMemCmp(&Eh->e_ident[EI_MAG0], ELFMAG, SELFMAG) != 0)
|
||||||
|
return false;
|
||||||
|
if (Eh->e_ident[EI_CLASS] != ELFCLASS64)
|
||||||
|
return false;
|
||||||
|
if (Eh->e_ident[EI_DATA] != ELFDATA2LSB)
|
||||||
|
return false;
|
||||||
|
if (Eh->e_version != EV_CURRENT)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Load an ELF64 program header
|
||||||
|
*
|
||||||
|
* @Vas: Virtual address space to map within
|
||||||
|
* @Phdr: Program header to load
|
||||||
|
* @Image: Image result is written here
|
||||||
|
*/
|
||||||
|
static PT_STATUS
|
||||||
|
ElfLoadPhdr(MMU_VAS *Vas, Elf64_Ehdr *Eh, Elf64_Phdr *Phdr, ELF64_PROGDATA *Image)
|
||||||
|
{
|
||||||
|
USIZE MapLength, MisAlign;
|
||||||
|
USIZE PageCount;
|
||||||
|
PT_STATUS Status;
|
||||||
|
MM_PROT Prot = PAGE_READ | PAGE_WRITE | PAGE_USER;
|
||||||
|
MM_RANGE AllocatedPages, MapRange;
|
||||||
|
VOID *Src, *Dest;
|
||||||
|
|
||||||
|
if (Vas == NULL || Eh == NULL) {
|
||||||
|
return STATUS_INVALID_PARAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Phdr == NULL || Image == NULL) {
|
||||||
|
return STATUS_INVALID_PARAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the permissions (TODO: Make unwritable by default) */
|
||||||
|
if (ISSET(Phdr->p_flags, PF_X))
|
||||||
|
Prot |= PAGE_EXEC;
|
||||||
|
|
||||||
|
/* Compute the number of pages to map */
|
||||||
|
MisAlign = Phdr->p_vaddr & (PAGESIZE - 1);
|
||||||
|
MapLength = ALIGN_UP(Phdr->p_memsz + MisAlign, PAGESIZE);
|
||||||
|
PageCount = MapLength / PAGESIZE;
|
||||||
|
|
||||||
|
/* Grab some pages for our segment data */
|
||||||
|
Status = MmAllocatePages(PageCount, &AllocatedPages);
|
||||||
|
if (PT_ERROR(Status)) {
|
||||||
|
return STATUS_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
MapRange.VirtualBase = Phdr->p_vaddr;
|
||||||
|
MapRange.PhysicalBase = AllocatedPages.PhysicalBase;
|
||||||
|
MapRange.Length = MapLength;
|
||||||
|
Status = MmMapRange(
|
||||||
|
Vas,
|
||||||
|
&MapRange,
|
||||||
|
Prot,
|
||||||
|
PAGESIZE_4K
|
||||||
|
);
|
||||||
|
|
||||||
|
/* TODO: Free previously allocated pages */
|
||||||
|
if (PT_ERROR(Status)) {
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
Src = PTR_OFFSET(Eh, Phdr->p_offset);
|
||||||
|
Dest = PMA_TO_VMA(AllocatedPages.PhysicalBase);
|
||||||
|
RtlMemCpy(Dest, Src, Phdr->p_filesz);
|
||||||
|
Image->LoadedSegments[Image->SegmentCount++] = MapRange;
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Load every program header of an ELF binary
|
||||||
|
*
|
||||||
|
* @Vas: Virtual address space to map within
|
||||||
|
* @Eh: ELF header
|
||||||
|
* @Image: Loaded program image
|
||||||
|
*/
|
||||||
|
static PT_STATUS
|
||||||
|
ElfLoad(MMU_VAS *Vas, Elf64_Ehdr *Eh, LOADER_PROGRAM *Image)
|
||||||
|
{
|
||||||
|
Elf64_Phdr *PhdrBase, *Phdr;
|
||||||
|
USIZE PhdrIndex;
|
||||||
|
PT_STATUS Status;
|
||||||
|
|
||||||
|
if (Vas == NULL || Eh == NULL) {
|
||||||
|
return STATUS_INVALID_PARAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Image == NULL) {
|
||||||
|
return STATUS_INVALID_PARAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Allocate our own program data */
|
||||||
|
Image->ProgramData = ExAllocatePoolWithTag(
|
||||||
|
POOL_NON_PAGED,
|
||||||
|
sizeof(ELF64_PROGDATA),
|
||||||
|
ELF_POOL_TAG
|
||||||
|
);
|
||||||
|
|
||||||
|
if (Image->ProgramData == NULL) {
|
||||||
|
return STATUS_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define PHDR_INDEX(INDEX) \
|
||||||
|
PTR_OFFSET(PhdrBase, (INDEX) * Eh->e_phentsize)
|
||||||
|
PhdrBase = PTR_OFFSET(Eh, Eh->e_phoff);
|
||||||
|
for (PhdrIndex = 0; PhdrIndex < Eh->e_phnum; ++PhdrIndex) {
|
||||||
|
Phdr = PHDR_INDEX(PhdrIndex);
|
||||||
|
|
||||||
|
switch (Phdr->p_type) {
|
||||||
|
case PT_LOAD:
|
||||||
|
/* TODO: Clean up on failure */
|
||||||
|
Status = ElfLoadPhdr(Vas, Eh, Phdr, Image->ProgramData);
|
||||||
|
if (PT_ERROR(Status)) {
|
||||||
|
ExFreePoolWithTag(Image->ProgramData, ELF_POOL_TAG);
|
||||||
|
Image->ProgramData = NULL;
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#undef PHDR_INDEX
|
||||||
|
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Load an ELF64 program
|
||||||
|
*/
|
||||||
|
static PT_STATUS
|
||||||
|
Elf64Load(MMU_VAS *Vas, VOID *Data, LOADER_PROGRAM *Result)
|
||||||
|
{
|
||||||
|
Elf64_Ehdr *Eh;
|
||||||
|
PT_STATUS Status;
|
||||||
|
|
||||||
|
if (Data == NULL || Result == NULL) {
|
||||||
|
return STATUS_INVALID_PARAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Verify that it is executable */
|
||||||
|
Eh = (Elf64_Ehdr *)Data;
|
||||||
|
if (!Elf64Verify(Eh)) {
|
||||||
|
return STATUS_NOT_EXEC;
|
||||||
|
}
|
||||||
|
|
||||||
|
Status = ElfLoad(
|
||||||
|
Vas,
|
||||||
|
Eh,
|
||||||
|
Result
|
||||||
|
);
|
||||||
|
|
||||||
|
if (PT_ERROR(Status)) {
|
||||||
|
return Status;
|
||||||
|
}
|
||||||
|
|
||||||
|
return STATUS_SUCCESS;
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2026, Chloe M.
|
||||||
|
* Provided under the BSD-3 clause.
|
||||||
|
*
|
||||||
|
* Description: Program loader dispatch
|
||||||
|
* Author: Chloe M.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ex/loader.h>
|
||||||
|
#include <ex/elf64.h>
|
||||||
|
|
||||||
|
/* Loader dispatch table */
|
||||||
|
static LOADER_OPS *LoaderOpsTable[] = {
|
||||||
|
[LOADER_ELF64] = &gElf64Ops
|
||||||
|
};
|
||||||
|
|
||||||
|
PT_STATUS
|
||||||
|
ExLoadProgramOf(LOADER_TYPE LoaderType, MMU_VAS *Vas, VOID *Data, LOADER_PROGRAM *Result)
|
||||||
|
{
|
||||||
|
LOADER_OPS *Ops;
|
||||||
|
|
||||||
|
if (Data == NULL || Result == NULL) {
|
||||||
|
return STATUS_INVALID_PARAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LoaderType >= NELEM(LoaderOpsTable)) {
|
||||||
|
return STATUS_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ops = LoaderOpsTable[LoaderType];
|
||||||
|
return Ops->LoadProgram(Vas, Data, Result);
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2026, Chloe M.
|
||||||
|
* Provided under the BSD-3 clause.
|
||||||
|
*
|
||||||
|
* Description: ELF64 loader
|
||||||
|
* Author: Chloe M.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _EX_ELF64_H_
|
||||||
|
#define _EX_ELF64_H_ 1
|
||||||
|
|
||||||
|
#include <ex/loader.h>
|
||||||
|
#include <ptdef.h>
|
||||||
|
|
||||||
|
/* Externs */
|
||||||
|
extern LOADER_OPS gElf64Ops;
|
||||||
|
|
||||||
|
#endif /* !_EX_ELF64_H_ */
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2026, Chloe M.
|
||||||
|
* Provided under the BSD-3 clause.
|
||||||
|
*
|
||||||
|
* Description: Program loader dispatch
|
||||||
|
* Author: Chloe M.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _EX_LOADER_H_
|
||||||
|
#define _EX_LOADER_H_ 1
|
||||||
|
|
||||||
|
#include <hal/mmu.h>
|
||||||
|
#include <ptapi/status.h>
|
||||||
|
#include <ptdef.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Valid loader types that can be dispatched
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
LOADER_ELF64
|
||||||
|
} LOADER_TYPE;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Represents a loaded program that has been loaded
|
||||||
|
* by a specific loader.
|
||||||
|
*
|
||||||
|
* @EntryPoint: Entrypoint of program
|
||||||
|
* @ProgramData: Loaded program data
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
UPTR EntryPoint;
|
||||||
|
VOID *ProgramData;
|
||||||
|
} LOADER_PROGRAM;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Loader operations associated with a specific
|
||||||
|
* loader.
|
||||||
|
*
|
||||||
|
* @LoadProgram: Callback used to invoke the loader
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
PT_STATUS(*LoadProgram)(MMU_VAS *Vas, VOID *Data, LOADER_PROGRAM *Result);
|
||||||
|
} LOADER_OPS;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Load a specific program using a specific loader type
|
||||||
|
*
|
||||||
|
* @LoaderType: Type of loader to invoke
|
||||||
|
* @Vas: Virtual address space to load in
|
||||||
|
* @Data: Program binary to load
|
||||||
|
* @Result: Loader program result is written here
|
||||||
|
*/
|
||||||
|
PT_STATUS ExLoadProgramOf(
|
||||||
|
LOADER_TYPE LoaderType, MMU_VAS *Vas,
|
||||||
|
VOID *Data, LOADER_PROGRAM *Result
|
||||||
|
);
|
||||||
|
|
||||||
|
#endif /* !_EX_LOADER_H_ */
|
||||||
Reference in New Issue
Block a user