From c8f99ad6eda76d86088f8462fe1a7f4312b62e91 Mon Sep 17 00:00:00 2001 From: belliash Date: Sun, 6 Nov 2022 19:59:38 +0100 Subject: [PATCH] Initial PE/COFF support for loading image files --- sdk/xtdk/xtimage.h | 19 +- sdk/xtdk/xtstruct.h | 2 +- xtldr/includes/blmod.h | 14 +- xtldr/includes/blproto.h | 8 + xtldr/modules/pecoff/CMakeLists.txt | 3 +- xtldr/modules/pecoff/includes/pecoff.h | 28 +++ xtldr/modules/pecoff/pecoff.c | 265 ++++++++++++++++++++++++- xtldr/xtldr.c | 5 + 8 files changed, 328 insertions(+), 16 deletions(-) create mode 100644 xtldr/modules/pecoff/includes/pecoff.h diff --git a/sdk/xtdk/xtimage.h b/sdk/xtdk/xtimage.h index 893019e..e723564 100644 --- a/sdk/xtdk/xtimage.h +++ b/sdk/xtdk/xtimage.h @@ -96,8 +96,8 @@ #define PECOFF_IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16 /* PE/COFF image HDR magic */ -#define PECOFF_IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10B -#define PECOFF_IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20B +#define PECOFF_IMAGE_PE_OPTIONAL_HDR32_MAGIC 0x10B +#define PECOFF_IMAGE_PE_OPTIONAL_HDR64_MAGIC 0x20B #define PECOFF_IMAGE_ROM_OPTIONAL_HDR_MAGIC 0x107 /* PE/COFF directory entries */ @@ -137,8 +137,8 @@ #define PECOFF_IMAGE_SIZEOF_SECTION_HEADER 40 #define PECOFF_IMAGE_SIZEOF_ROM_OPTIONAL_HEADER 56 #define PECOFF_IMAGE_SIZEOF_STD_OPTIONAL_HEADER 28 -#define PECOFF_IMAGE_SIZEOF_NT_OPTIONAL32_HEADER 224 -#define PECOFF_IMAGE_SIZEOF_NT_OPTIONAL64_HEADER 240 +#define PECOFF_IMAGE_SIZEOF_PE_OPTIONAL32_HEADER 224 +#define PECOFF_IMAGE_SIZEOF_PE_OPTIONAL64_HEADER 240 /* PE/COFF image section characteristics */ #define PECOFF_IMAGE_SCN_TYPE_REG 0x00000000 @@ -190,11 +190,10 @@ typedef struct _PECOFF_IMAGE_CONTEXT { PVOID Data; - PVOID BaseAddress; - ULONG NtSignature; UINT64 FileSize; + UINT ImagePages; UINT ImageSize; - UINT Pages; + PVOID VirtualAddress; } PECOFF_IMAGE_CONTEXT, *PPECOFF_IMAGE_CONTEXT; /* PE/COFF directory format */ @@ -411,13 +410,13 @@ typedef struct _PECOFF_IMAGE_ROM_OPTIONAL_HEADER ULONG GpValue; } PECOFF_IMAGE_ROM_OPTIONAL_HEADER, *PPECOFF_IMAGE_ROM_OPTIONAL_HEADER; -/* PE/COFF NT image header */ -typedef struct _PECOFF_IMAGE_NT_HEADER +/* PE/COFF PE image header */ +typedef struct _PECOFF_IMAGE_PE_HEADER { ULONG Signature; PECOFF_IMAGE_FILE_HEADER FileHeader; PECOFF_IMAGE_OPTIONAL_HEADER OptionalHeader; -} PECOFF_IMAGE_NT_HEADER, *PPECOFF_IMAGE_NT_HEADER; +} PECOFF_IMAGE_PE_HEADER, *PPECOFF_IMAGE_PE_HEADER; /* PE/COFF ROM image header */ typedef struct _PECOFF_IMAGE_ROM_HEADER { diff --git a/sdk/xtdk/xtstruct.h b/sdk/xtdk/xtstruct.h index 81d5bba..c230a57 100644 --- a/sdk/xtdk/xtstruct.h +++ b/sdk/xtdk/xtstruct.h @@ -156,9 +156,9 @@ typedef struct _PECOFF_IMAGE_IMPORT_DESCRIPTOR PECOFF_IMAGE_IMPORT_DESCRIPTOR, * typedef struct _PECOFF_IMAGE_LOAD_CONFIG_CODE_INTEGRITY PECOFF_IMAGE_LOAD_CONFIG_CODE_INTEGRITY, *PPECOFF_IMAGE_LOAD_CONFIG_CODE_INTEGRITY; typedef struct _PECOFF_IMAGE_LOAD_CONFIG_DIRECTORY32 PECOFF_IMAGE_LOAD_CONFIG_DIRECTORY32, *PPECOFF_IMAGE_LOAD_CONFIG_DIRECTORY32; typedef struct _PECOFF_IMAGE_LOAD_CONFIG_DIRECTORY64 PECOFF_IMAGE_LOAD_CONFIG_DIRECTORY64, *PPECOFF_IMAGE_LOAD_CONFIG_DIRECTORY64; -typedef struct _PECOFF_IMAGE_NT_HEADER PECOFF_IMAGE_NT_HEADER, *PPECOFF_IMAGE_NT_HEADER; typedef struct _PECOFF_IMAGE_OPTIONAL_HEADER PECOFF_IMAGE_OPTIONAL_HEADER, *PPECOFF_IMAGE_OPTIONAL_HEADER; typedef struct _PECOFF_IMAGE_OS2_HEADER PECOFF_IMAGE_OS2_HEADER, *PPECOFF_IMAGE_OS2_HEADER; +typedef struct _PECOFF_IMAGE_PE_HEADER PECOFF_IMAGE_PE_HEADER, *PPECOFF_IMAGE_PE_HEADER; typedef struct _PECOFF_IMAGE_RESOURCE_DATA_ENTRY PECOFF_IMAGE_RESOURCE_DATA_ENTRY, *PPECOFF_IMAGE_RESOURCE_DATA_ENTRY; typedef struct _PECOFF_IMAGE_RESOURCE_DIRECTORY PECOFF_IMAGE_RESOURCE_DIRECTORY, *PPECOFF_IMAGE_RESOURCE_DIRECTORY; typedef struct _PECOFF_IMAGE_RESOURCE_DIRECTORY_ENTRY PECOFF_IMAGE_RESOURCE_DIRECTORY_ENTRY, *PPECOFF_IMAGE_RESOURCE_DIRECTORY_ENTRY; diff --git a/xtldr/includes/blmod.h b/xtldr/includes/blmod.h index 65c3b0a..3194e93 100644 --- a/xtldr/includes/blmod.h +++ b/xtldr/includes/blmod.h @@ -2,7 +2,7 @@ * PROJECT: ExectOS * COPYRIGHT: See COPYING.md in the top level directory * FILE: xtldr/includes/blmod.h - * DESCRIPTION: Top level header for XTLDR modules + * DESCRIPTION: Top level header for XTLDR modules support * DEVELOPERS: Rafal Kupiec */ @@ -14,4 +14,16 @@ #include +/* Structures forward declarations */ +typedef struct _XT_PECOFFF_IMAGE_PROTOCOL XT_PECOFF_IMAGE_PROTOCOL, *PXT_PECOFF_IMAGE_PROTOCOL; + +/* Pointers to the routines provided by the modules */ +typedef EFI_STATUS (*PXT_PECOFF_PROTOCOL_LOAD)(IN PEFI_FILE_HANDLE FileHandle, IN PVOID VirtualAddress, OUT PPECOFF_IMAGE_CONTEXT *Image); + +/* EFI XT PE/COFF Image Protocol */ +typedef struct _XT_PECOFFF_IMAGE_PROTOCOL +{ + PXT_PECOFF_PROTOCOL_LOAD Load; +} XT_PECOFF_IMAGE_PROTOCOL, *PXT_PECOFF_IMAGE_PROTOCOL; + #endif /* __XTLDR_BLMOD_H */ diff --git a/xtldr/includes/blproto.h b/xtldr/includes/blproto.h index cc25a89..c0dc5c3 100644 --- a/xtldr/includes/blproto.h +++ b/xtldr/includes/blproto.h @@ -14,6 +14,10 @@ /* Loader protocol routine pointers */ +typedef EFI_STATUS (*PBL_ALLOCATE_PAGES)(IN UINT64 Size, OUT PEFI_PHYSICAL_ADDRESS Memory); +typedef EFI_STATUS (*PBL_ALLOCATE_POOL)(IN UINT_PTR Size, OUT PVOID *Memory); +typedef EFI_STATUS (*PBL_FREE_PAGES)(IN UINT64 Size, IN EFI_PHYSICAL_ADDRESS Memory); +typedef EFI_STATUS (*PBL_FREE_POOL)(IN PVOID Memory); typedef VOID (*PBL_DBG_PRINT)(IN PUINT16 Format, IN ...); typedef VOID (*PBL_EFI_PRINT)(IN PUINT16 Format, IN ...); typedef EFI_STATUS (*PBL_CLOSE_VOLUME)(IN PEFI_HANDLE VolumeHandle); @@ -22,6 +26,10 @@ typedef EFI_STATUS (*PBL_OPEN_VOLUME)(IN PEFI_DEVICE_PATH_PROTOCOL DevicePath, O /* EFI XT Boot Loader Protocol */ typedef struct _XT_BOOT_LOADER_PROTOCOL { + PBL_ALLOCATE_PAGES AllocatePages; + PBL_ALLOCATE_POOL AllocatePool; + PBL_FREE_PAGES FreePages; + PBL_FREE_POOL FreePool; PBL_DBG_PRINT DbgPrint; PBL_EFI_PRINT EfiPrint; PBL_CLOSE_VOLUME CloseVolume; diff --git a/xtldr/modules/pecoff/CMakeLists.txt b/xtldr/modules/pecoff/CMakeLists.txt index aed8c80..f1d05a9 100644 --- a/xtldr/modules/pecoff/CMakeLists.txt +++ b/xtldr/modules/pecoff/CMakeLists.txt @@ -4,7 +4,8 @@ PROJECT(XTLDR_PECOFF) # Specify include directories include_directories( ${EXECTOS_SOURCE_DIR}/sdk/xtdk - ${XTLDR_SOURCE_DIR}/includes) + ${XTLDR_SOURCE_DIR}/includes + ${XTLDR_PECOFF_SOURCE_DIR}/includes) # Specify list of source code files list(APPEND XTLDR_PECOFF_SOURCE diff --git a/xtldr/modules/pecoff/includes/pecoff.h b/xtldr/modules/pecoff/includes/pecoff.h new file mode 100644 index 0000000..94e1799 --- /dev/null +++ b/xtldr/modules/pecoff/includes/pecoff.h @@ -0,0 +1,28 @@ +/** + * PROJECT: ExectOS + * COPYRIGHT: See COPYING.md in the top level directory + * FILE: xtldr/modules/pecoff/includes/pecoff.h + * DESCRIPTION: PE/COFF executable file format support header + * DEVELOPERS: Rafal Kupiec + */ + +#ifndef __XTLDR_MODULES_PECOFF_H +#define __XTLDR_MODULES_PECOFF_H + +#include + + +EFI_STATUS PeLoadImage(IN PEFI_FILE_HANDLE FileHandle, + IN PVOID VirtualAddress, + OUT PPECOFF_IMAGE_CONTEXT *Image); + +EFI_STATUS +PepReadImageHeader(IN PUCHAR ImageData, + IN SIZE_T FileSize, + OUT PPECOFF_IMAGE_PE_HEADER *PeHeader); + +EFI_STATUS +BlXtLdrModuleMain(EFI_HANDLE ImageHandle, + PEFI_SYSTEM_TABLE SystemTable); + +#endif /* __XTLDR_MODULES_PECOFF_H */ diff --git a/xtldr/modules/pecoff/pecoff.c b/xtldr/modules/pecoff/pecoff.c index 2559ea0..a423967 100644 --- a/xtldr/modules/pecoff/pecoff.c +++ b/xtldr/modules/pecoff/pecoff.c @@ -6,7 +6,7 @@ * DEVELOPERS: Rafal Kupiec */ -#include +#include /* EFI Image Handle */ @@ -18,6 +18,260 @@ PEFI_SYSTEM_TABLE EfiSystemTable; /* EFI XT Loader Protocol */ PXT_BOOT_LOADER_PROTOCOL EfiXtLdrProtocol; +/* XTOS PE/COFF Image Protocol */ +XT_PECOFF_IMAGE_PROTOCOL XtPeCoffProtocol; + +/** + * Loads a PE/COFF image file. + * + * @param FileHandle + * The handle of the opened portable executable (PE) file. + * + * @param VirtualAddress + * Optional virtual address pointing to the memory area where PE/COFF file will be loaded. + * + * @return This routine returns status code. + * + * @since XT 1.0 + */ +EFI_STATUS PeLoadImage(IN PEFI_FILE_HANDLE FileHandle, + IN PVOID VirtualAddress, + OUT PPECOFF_IMAGE_CONTEXT *Image) +{ + EFI_GUID FileInfoGuid = EFI_FILE_INFO_PROTOCOL_GUID; + PPECOFF_IMAGE_SECTION_HEADER SectionHeader; + PPECOFF_IMAGE_PE_HEADER PeHeader; + PPECOFF_IMAGE_CONTEXT ImageData; + EFI_PHYSICAL_ADDRESS Address; + PEFI_FILE_INFO FileInfo; + SIZE_T Size, Pages; + EFI_STATUS Status; + UINT_PTR ReadSize; + UINT SectionSize; + PUCHAR Data; + UINT Index; + + /* Set required size for getting file information */ + Size = sizeof(EFI_FILE_INFO) + 32; + + /* Allocate necessary amount of memory */ + Status = EfiXtLdrProtocol->AllocatePool(Size, (PVOID *)&FileInfo); + if(Status != STATUS_EFI_SUCCESS) + { + /* Memory allocation failure */ + EfiXtLdrProtocol->DbgPrint(L"ERROR: Memory pool allocation failure\n"); + return Status; + } + + /* First attempt to get file information */ + Status = FileHandle->GetInfo(FileHandle, &FileInfoGuid, &Size, FileInfo); + if(Status == STATUS_EFI_BUFFER_TOO_SMALL) + { + /* Buffer it too small, but EFI tells the required size, let's reallocate */ + EfiXtLdrProtocol->FreePool(&FileInfo); + Status = EfiXtLdrProtocol->AllocatePool(Size, (PVOID *)&FileInfo); + if(Status != STATUS_EFI_SUCCESS) + { + /* Memory allocation failure */ + EfiXtLdrProtocol->DbgPrint(L"ERROR: Memory pool allocation failure\n"); + return Status; + } + /* Second attempt to get file information */ + Status = FileHandle->GetInfo(FileHandle, &FileInfoGuid, &Size, FileInfo); + } + if(Status != STATUS_EFI_SUCCESS) + { + /* Unable to get file information */ + EfiXtLdrProtocol->DbgPrint(L"ERROR: Failed to get file information\n"); + return Status; + } + + /* Allocate memory for storing image data */ + Status = EfiXtLdrProtocol->AllocatePool(sizeof(PECOFF_IMAGE_CONTEXT), (PVOID *)&ImageData); + if(Status != STATUS_EFI_SUCCESS) + { + /* Memory allocation failure */ + EfiXtLdrProtocol->DbgPrint(L"ERROR: Memory pool allocation failure\n"); + return Status; + } + + /* Store file size and free memory */ + ImageData->FileSize = FileInfo->FileSize; + EfiXtLdrProtocol->FreePool(FileInfo); + + /* Calculate number of pages */ + Pages = EFI_SIZE_TO_PAGES(ImageData->FileSize); + + /* Allocate pages */ + Status = EfiXtLdrProtocol->AllocatePages(Pages, &Address); + if(Status != STATUS_EFI_SUCCESS) + { + /* Pages allocation failure */ + EfiXtLdrProtocol->DbgPrint(L"ERROR: Pages allocation failure\n"); + EfiXtLdrProtocol->FreePool(ImageData); + return Status; + } + + /* Read PE/COFF image */ + ReadSize = Pages * EFI_PAGE_SIZE; + Data = (PUCHAR)(UINT_PTR)Address; + Status = FileHandle->Read(FileHandle, &ReadSize, Data); + if(Status != STATUS_EFI_SUCCESS) + { + /* Failed to read data */ + EfiXtLdrProtocol->DbgPrint(L"ERROR: Unable to read PE/COFF image file\n"); + EfiXtLdrProtocol->FreePages(Pages, (EFI_PHYSICAL_ADDRESS)(UINT_PTR)Data); + EfiXtLdrProtocol->FreePool(ImageData); + return Status; + } + + /* Read and validate headers */ + Status = PepReadImageHeader(Data, ImageData->FileSize, &PeHeader); + if(Status != STATUS_EFI_SUCCESS) + { + /* Header validation failed, probably broken or invalid PE/COFF image */ + EfiXtLdrProtocol->DbgPrint(L"ERROR: PE/COFF image validation failed\n"); + EfiXtLdrProtocol->FreePages(Pages, (EFI_PHYSICAL_ADDRESS)(UINT_PTR)Data); + EfiXtLdrProtocol->FreePool(ImageData); + return Status; + } + + /* Store image size and calculate number of image pages */ + ImageData->ImageSize = PeHeader->OptionalHeader.SizeOfImage; + ImageData->ImagePages = EFI_SIZE_TO_PAGES(ImageData->ImageSize); + + /* Allocate image pages */ + Status = EfiXtLdrProtocol->AllocatePages(ImageData->ImagePages, &Address); + if(Status != STATUS_EFI_SUCCESS) + { + /* Pages reallocation failure */ + EfiXtLdrProtocol->DbgPrint(L"ERROR: Pages reallocation failure\n"); + EfiXtLdrProtocol->FreePool(ImageData); + return Status; + } + + /* Store image data and base address */ + ImageData->Data = (PUINT8)(UINT_PTR)Address; + if(VirtualAddress) + { + /* Virtual address passed to this routine */ + ImageData->VirtualAddress = VirtualAddress; + } + else + { + /* Virtual address not specified, use physical address */ + ImageData->VirtualAddress = (PVOID)(UINT_PTR)Address; + } + + /* Copy all sections */ + RtlCopyMemory(ImageData->Data, Data, PeHeader->OptionalHeader.SizeOfHeaders); + + /* Find section header */ + SectionHeader = (PPECOFF_IMAGE_SECTION_HEADER)((PUCHAR)&PeHeader->OptionalHeader + + PeHeader->FileHeader.SizeOfOptionalHeader); + + /* Load each section into memory */ + for(Index = 0; Index < PeHeader->FileHeader.NumberOfSections; Index++) + { + EfiXtLdrProtocol->EfiPrint(L"TEST: %lx\n", SectionHeader[Index].Misc.VirtualSize); + + /* Check section raw data size and section virtual size */ + if(SectionHeader[Index].SizeOfRawData < SectionHeader[Index].Misc.VirtualSize) + { + /* Use raw data size if it is smaller than virtual size */ + SectionSize = SectionHeader[Index].SizeOfRawData; + } + else + { + /* User virtual size otherwise */ + SectionSize = SectionHeader[Index].Misc.VirtualSize; + } + + /* Make sure section is available */ + if(SectionSize > 0 && SectionHeader[Index].PointerToRawData != 0) + { + /* Copy section */ + RtlCopyMemory((PUINT8)ImageData->Data + SectionHeader[Index].VirtualAddress, + Data + SectionHeader[Index].PointerToRawData, SectionSize); + } + + /* Check if raw size is shorter than virtual size */ + if(SectionSize < SectionHeader[Index].Misc.VirtualSize) + { + /* Fill remaining space with zeroes */ + RtlZeroMemory((PUINT8)ImageData->Data + SectionHeader[Index].VirtualAddress + SectionSize, + SectionHeader[Index].Misc.VirtualSize - SectionSize); + } + } + + /* Free pages */ + EfiXtLdrProtocol->FreePages((EFI_PHYSICAL_ADDRESS)(UINT_PTR)Data, Pages); + + /* Store image data */ + *Image = ImageData; + + /* Return SUCCESS */ + return STATUS_EFI_SUCCESS; +} + +/** + * Reads and validate a PE/COFF image headers + * + * @param ImageData + * A pointer to the buffer containing PE/COFF image contents. + * + * @param FileSize + * An input PE/COFF image file size. + * + * @param PeHeader + * Pointer to the memory area where PE header will be saved. + * + * @return This routine returns a status code. + * + * @since XT 1.0 + */ +EFI_STATUS +PepReadImageHeader(IN PUCHAR ImageData, + IN SIZE_T FileSize, + OUT PPECOFF_IMAGE_PE_HEADER *PeHeader) +{ + PPECOFF_IMAGE_DOS_HEADER DosHeader; + + /* Validate file size */ + if(FileSize < sizeof(PECOFF_IMAGE_DOS_HEADER)) + { + EfiXtLdrProtocol->DbgPrint(L"WARNING: PE/COFF image shorter than DOS header\n"); + return STATUS_EFI_END_OF_FILE; + } + + /* Validate DOS header */ + DosHeader = (PPECOFF_IMAGE_DOS_HEADER)ImageData; + if(DosHeader->e_magic != PECOFF_IMAGE_DOS_SIGNATURE) + { + EfiXtLdrProtocol->DbgPrint(L"WARNING: Invalid DOS signature found\n"); + return STATUS_EFI_INCOMPATIBLE_VERSION; + } + + /* Validate NT/XT header */ + *PeHeader = (PPECOFF_IMAGE_PE_HEADER)(ImageData + DosHeader->e_lfanew); + if((*PeHeader)->Signature != PECOFF_IMAGE_NT_SIGNATURE && (*PeHeader)->Signature != PECOFF_IMAGE_XT_SIGNATURE) + { + EfiXtLdrProtocol->DbgPrint(L"WARNING: Invalid NT/XT signature found\n"); + return STATUS_EFI_INCOMPATIBLE_VERSION; + } + + /* Validate optional header */ + if((*PeHeader)->OptionalHeader.Magic != PECOFF_IMAGE_PE_OPTIONAL_HDR32_MAGIC && + (*PeHeader)->OptionalHeader.Magic != PECOFF_IMAGE_PE_OPTIONAL_HDR64_MAGIC) + { + EfiXtLdrProtocol->DbgPrint(L"WARNING: Invalid optional header signature found\n"); + return STATUS_EFI_INCOMPATIBLE_VERSION; + } + + /* Return SUCCESS */ + return STATUS_EFI_SUCCESS; +} + /** * This routine is the entry point of the XT EFI boot loader module. * @@ -35,6 +289,8 @@ EFI_STATUS BlXtLdrModuleMain(EFI_HANDLE ImageHandle, PEFI_SYSTEM_TABLE SystemTable) { + EFI_GUID Guid = XT_PECOFF_IMAGE_PROTOCOL_GUID; + EFI_HANDLE Handle = NULL; EFI_STATUS Status; /* Set the system table and image handle */ @@ -49,6 +305,9 @@ BlXtLdrModuleMain(EFI_HANDLE ImageHandle, return STATUS_EFI_PROTOCOL_ERROR; } - /* Return success */ - return STATUS_EFI_SUCCESS; + /* Set routines available via PE/COFF image protocol */ + XtPeCoffProtocol.Load = PeLoadImage; + + /* Register PE/COFF protocol */ + return EfiSystemTable->BootServices->InstallProtocolInterface(&Handle, &Guid, EFI_NATIVE_INTERFACE, &XtPeCoffProtocol); } diff --git a/xtldr/xtldr.c b/xtldr/xtldr.c index 6a749ec..050133c 100644 --- a/xtldr/xtldr.c +++ b/xtldr/xtldr.c @@ -260,6 +260,10 @@ BlRegisterXtLoaderProtocol() EFI_HANDLE Handle = NULL; /* Set all routines available via loader protocol */ + EfiLdrProtocol.AllocatePages = BlEfiMemoryAllocatePages; + EfiLdrProtocol.AllocatePool = BlEfiMemoryAllocatePool; + EfiLdrProtocol.FreePages = BlEfiMemoryFreePages; + EfiLdrProtocol.FreePool = BlEfiMemoryFreePool; EfiLdrProtocol.DbgPrint = BlDbgPrint; EfiLdrProtocol.EfiPrint = BlEfiPrint; @@ -327,6 +331,7 @@ BlStartXtLoader(IN EFI_HANDLE ImageHandle, BlDbgPrint(L"ERROR: Failed to register XTLDR loader protocol\n"); } + /* Load XTLDR modules */ Status = BlLoadEfiModules(); if(Status != STATUS_EFI_SUCCESS) {