From cbb440b8e99f7ee2fc9137a0cab3a767e6c5e9fc Mon Sep 17 00:00:00 2001 From: Chloe M Date: Tue, 14 Jul 2026 22:13:17 -0400 Subject: [PATCH] ptos: ex: Add loader dispatch + ELF64 loader Signed-off-by: Chloe M --- service/ptos/ex/elf64.c | 216 ++++++++++++++++++++++++++++++++++ service/ptos/ex/loader.c | 32 +++++ service/ptos/head/ex/elf64.h | 18 +++ service/ptos/head/ex/loader.h | 58 +++++++++ 4 files changed, 324 insertions(+) create mode 100644 service/ptos/ex/elf64.c create mode 100644 service/ptos/ex/loader.c create mode 100644 service/ptos/head/ex/elf64.h create mode 100644 service/ptos/head/ex/loader.h diff --git a/service/ptos/ex/elf64.c b/service/ptos/ex/elf64.c new file mode 100644 index 0000000..0618243 --- /dev/null +++ b/service/ptos/ex/elf64.c @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2026, Chloe M. + * Provided under the BSD-3 clause. + * + * Description: ELF64 loader + * Author: Chloe M. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* 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; +} diff --git a/service/ptos/ex/loader.c b/service/ptos/ex/loader.c new file mode 100644 index 0000000..926bf22 --- /dev/null +++ b/service/ptos/ex/loader.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2026, Chloe M. + * Provided under the BSD-3 clause. + * + * Description: Program loader dispatch + * Author: Chloe M. + */ + +#include +#include + +/* 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); +} diff --git a/service/ptos/head/ex/elf64.h b/service/ptos/head/ex/elf64.h new file mode 100644 index 0000000..61d1e82 --- /dev/null +++ b/service/ptos/head/ex/elf64.h @@ -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 +#include + +/* Externs */ +extern LOADER_OPS gElf64Ops; + +#endif /* !_EX_ELF64_H_ */ diff --git a/service/ptos/head/ex/loader.h b/service/ptos/head/ex/loader.h new file mode 100644 index 0000000..a56f74f --- /dev/null +++ b/service/ptos/head/ex/loader.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 +#include +#include + +/* + * 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_ */