Initial commit

This commit is contained in:
2021-10-10 14:39:17 +08:00
commit d25da95e1e
135 changed files with 19184 additions and 0 deletions

256
execformat/pe/format.h Normal file
View File

@ -0,0 +1,256 @@
#pragma once
#include "../../main.h"
#include "../../runtime/panic_assert.h"
#include <stddef.h>
#ifdef __cplusplus
extern "C" {
#endif
// PE Header Magic, comes after the MS-DOS stub and right before PE Header
#define EXECFORMAT_PE_HEADER_MAGIC "PE\0" // comes with a \0 itself
// https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
typedef struct {
uint16_t machineType;
uint16_t numSections;
uint32_t unixTimestamp;
uint32_t offsetSymbolTable;
uint32_t numSymbols;
uint16_t sizeofOptionalHeader;
uint16_t flags; // "Characteristics"
} PACKED execformat_pe_Header;
// Machine types
#define EXECFORMAT_PE_MACHINE_UNKNOWN 0x0u // The content of this field is assumed to be applicable to any machine type
#define EXECFORMAT_PE_MACHINE_AMD64 0x8664u // x64
#define EXECFORMAT_PE_MACHINE_I386 0x14cu // Intel 386 or later processors and compatible processors
#define EXECFORMAT_PE_MACHINE_EBC 0xebcu // EFI byte code
// Characteristics
#define EXECFORMAT_PE_FLAG_RELOCS_STRIPPED 0x0001u // base relocs removed and must be loaded at its preferred base address.
#define EXECFORMAT_PE_FLAG_EXECUTABLE_IMAGE 0x0002u // the image file is valid and can be run.
#define EXECFORMAT_PE_FLAG_LINE_NUMS_STRIPPED 0x0004u // COFF line numbers removed. deprecated and should be zero.
#define EXECFORMAT_PE_FLAG_LOCAL_SYMS_STRIPPED 0x0008u // COFF symbol table entries for local symbols removed. deprecated and should be zero.
#define EXECFORMAT_PE_FLAG_AGGRESSIVE_WS_TRIM 0x0010u // Aggressively trim working set. deprecated and later and must be zero.
#define EXECFORMAT_PE_FLAG_LARGE_ADDRESS_AWARE 0x0020u // Application can handle >2GB addresses.
#define EXECFORMAT_PE_FLAG_RESERVED_0x0040 0x0040u
#define EXECFORMAT_PE_FLAG_BYTES_REVERSED_LO 0x0080u // Little endian. This flag is deprecated and should be zero.
#define EXECFORMAT_PE_FLAG_32BIT_MACHINE 0x0100u // Machine is based on a 32-bit-word architecture.
#define EXECFORMAT_PE_FLAG_DEBUG_STRIPPED 0x0200u // Debugging information is removed from the image file.
#define EXECFORMAT_PE_FLAG_REMOVABLE_RUN_FROM_SWAP 0x0400u // If the image is on removable media, fully load it and copy it to the swap file.
#define EXECFORMAT_PE_FLAG_NET_RUN_FROM_SWAP 0x0800u // If the image is on network media, fully load it and copy it to the swap file.
#define EXECFORMAT_PE_FLAG_SYSTEM 0x1000u // The image file is a system file, not a user program.
#define EXECFORMAT_PE_FLAG_DLL 0x2000u // The image file is a dynamic-link library (DLL).
#define EXECFORMAT_PE_FLAG_UP_SYSTEM_ONLY 0x4000u // The file should be run only on a uniprocessor machine.
#define EXECFORMAT_PE_FLAG_BYTES_REVERSED_HI 0x8000u // Big endian. This flag is deprecated and should be zero.
// Optional Header Magic
#define EXECFORMAT_PE_OPTIONAL_HEADER_MAGIC_PE32 0x10bu // 2-byte magic for PE32 Optional Header, right following the PE Header.
#define EXECFORMAT_PE_OPTIONAL_HEADER_MAGIC_PE32P 0x20bu // 2-byte magic for PE32+ Optional Header, right following the PE Header.
typedef struct {
uint16_t magic; // either 0x10b for PE32, or 0x20b for PE32+
uint8_t majorLinkerVersion, minorLinkerVersion;
uint32_t sizeofText; // size of the .text section, or the sum of all text sections
uint32_t sizeofData; // size of initialized data sections
uint32_t sizeofBss; // size of uninitialized data
uint32_t offsetofEntryPoint; // offset of the entry point (starting address) relative to the image base
uint32_t offsetofText; // offset of the start of .text section relative to the image base
} PACKED execformat_pe_OptionalHeader_StandardFields_PE32P;
typedef struct {
uint16_t magic; // either 0x10b for PE32, or 0x20b for PE32+
uint8_t majorLinkerVersion, minorLinkerVersion;
uint32_t sizeofText; // size of the .text section, or the sum of all text sections
uint32_t sizeofData; // size of initialized data sections
uint32_t sizeofBss; // size of uninitialized data
uint32_t offsetofEntryPoint; // offset of the entry point (starting address) relative to the image base
uint32_t offsetofText; // offset of the start of .text section relative to the image base
uint32_t offsetofData; // only present in PE32 images
} PACKED execformat_pe_OptionalHeader_StandardFields_PE32;
// PE32+ struct with a 64-bit ImageBase
typedef struct {
uint64_t imageBase; // The preferred address of the first byte of image when loaded into memory; must be aligned to 64K.
uint32_t sectionAlignment; // The alignment (in bytes) of sections when they are loaded into memory.
uint32_t fileAlignment; // The alignment factor (in bytes) that is used to align the raw data of sections in the image file.
uint16_t majorOSVersion, minorOSVersion; // The version number of the required operating system.
uint16_t majorImageVersion, minorImageVersion;
uint16_t majorSubsystemVersion, minorSubsystemVersion;
uint32_t win32VersionValue; // Reserved, must be 0
uint32_t sizeofImage; // Size in bytes of the image, including all headers as the image is loaded into memory.
uint32_t sizeofHeaders; // Combined size of the MS-DOS stub, PE header, and section headers rounded up to FileAlignment.
uint32_t checksum; // Checksum of an unknown(???) algorithm
uint16_t subsystem; // Subsystem
uint16_t dllFlags; // DLL Characteristics
uint64_t sizeofStackReserve, sizeofStackCommit;
uint64_t sizeofHeapReserve, sizeofHeapCommit;
uint32_t loaderFlags; // Reserved, must be 0
uint32_t numRVAandSizes; // The number of data-directory entries in the remainder of the optional header. Each describes a location and size.
} PACKED execformat_pe_OptionalHeader_WindowsFields_PE32P;
// PE32 struct with a 32-bit ImageBase
typedef struct {
uint32_t imageBase; // The preferred address of the first byte of image when loaded into memory; must be aligned to 64K.
uint32_t sectionAlignment; // The alignment (in bytes) of sections when they are loaded into memory.
uint32_t fileAlignment; // The alignment factor (in bytes) that is used to align the raw data of sections in the image file.
uint16_t majorOSVersion, minorOSVersion; // The version number of the required operating system.
uint16_t majorImageVersion, minorImageVersion;
uint16_t majorSubsystemVersion, minorSubsystemVersion;
uint32_t win32VersionValue; // Reserved, must be 0
uint32_t sizeofImage; // Size in bytes of the image, including all headers as the image is loaded into memory.
uint32_t sizeofHeaders; // Combined size of the MS-DOS stub, PE header, and section headers rounded up to FileAlignment.
uint32_t checksum; // Checksum of an unknown(???) algorithm
uint16_t subsystem; // Subsystem
uint16_t dllFlags; // DLL Characteristics
uint32_t sizeofStackReserve, sizeofStackCommit;
uint32_t sizeofHeapReserve, sizeofHeapCommit;
uint32_t loaderFlags; // Reserved, must be 0
uint32_t numRVAandSizes; // The number of data-directory entries in the remainder of the optional header. Each describes a location and size.
} PACKED execformat_pe_OptionalHeader_WindowsFields_PE32;
// Subsystems
#define EXECFORMAT_PE_SUBSYSTEM_UNKNOWN 0
#define EXECFORMAT_PE_SUBSYSTEM_NATIVE 1 // Device drivers and native Windows processes
#define EXECFORMAT_PE_SUBSYSTEM_WINDOWS_GUI 2 // Windows graphical user interface (GUI) subsystem
#define EXECFORMAT_PE_SUBSYSTEM_WINDOWS_CUI 3 // Windows character subsystem
#define EXECFORMAT_PE_SUBSYSTEM_OS2_CUI 5 // OS/2 character subsystem
#define EXECFORMAT_PE_SUBSYSTEM_POSIX_CUI 7 // POSIX character subsystem
#define EXECFORMAT_PE_SUBSYSTEM_NATIVE_WINDOWS 8 // Native Win9x driver
#define EXECFORMAT_PE_SUBSYSTEM_WINDOWS_CE_GUI 9 // Windows CE
#define EXECFORMAT_PE_SUBSYSTEM_EFI_APPLICATION 10 // Extensible Firmware Interface (EFI) application
#define EXECFORMAT_PE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11 // EFI Boot Services driver
#define EXECFORMAT_PE_SUBSYSTEM_EFI_RUNTIME_DRIVER 12 // EFI Runtime driver
#define EXECFORMAT_PE_SUBSYSTEM_EFI_ROM 13 // EFI ROM image
#define EXECFORMAT_PE_SUBSYSTEM_XBOX 14 // XBOX
#define EXECFORMAT_PE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION 16 // Windows boot application
// DLL Characteristics
#define EXECFORMAT_PE_DLLFLAG_RESERVED_0x0001 0x0001u // Reserved, must be 0
#define EXECFORMAT_PE_DLLFLAG_RESERVED_0x0002 0x0002u // Reserved, must be 0
#define EXECFORMAT_PE_DLLFLAG_RESERVED_0x0004 0x0004u // Reserved, must be 0
#define EXECFORMAT_PE_DLLFLAG_RESERVED_0x0008 0x0008u // Reserved, must be 0
#define EXECFORMAT_PE_DLLFLAG_HIGH_ENTROPY_VA 0x0020u // Image can handle a high entropy 64-bit virtual address space.
#define EXECFORMAT_PE_DLLFLAG_DYNAMIC_BASE 0x0040u // DLL can be relocated at load time.
#define EXECFORMAT_PE_DLLFLAG_FORCE_INTEGRITY 0x0080u // Code Integrity checks are enforced.
#define EXECFORMAT_PE_DLLFLAG_NX_COMPAT 0x0100u // Image is NX compatible.
#define EXECFORMAT_PE_DLLFLAG_NO_ISOLATION 0x0200u // Isolation aware, but do not isolate the image.
#define EXECFORMAT_PE_DLLFLAG_NO_SEH 0x0400u // Does not use structured exception (SE) handling.
#define EXECFORMAT_PE_DLLFLAG_NO_BIND 0x0800u // Do not bind the image.
#define EXECFORMAT_PE_DLLFLAG_APPCONTAINER 0x1000u // Image must execute in an AppContainer.
#define EXECFORMAT_PE_DLLFLAG_WDM_DRIVER 0x2000u // A WDM driver.
#define EXECFORMAT_PE_DLLFLAG_GUARD_CF 0x4000u // Image supports Control Flow Guard.
#define EXECFORMAT_PE_DLLFLAG_TERMINAL_SERVER_AWARE 0x8000u // Terminal Server aware.
typedef struct {
uint32_t Offset;
uint32_t Size;
} PACKED execformat_pe_OptionalHeader_DataDirectory;
// Data Directory entry indexes
#define EXECFORMAT_PE_DATADIR_INDEX_EXPORT 0 // .edata
#define EXECFORMAT_PE_DATADIR_INDEX_IMPORT 1 // .idata
#define EXECFORMAT_PE_DATADIR_INDEX_RESOURCE 2 // .rsrc
#define EXECFORMAT_PE_DATADIR_INDEX_EXCEPTION 3 // .pdata
#define EXECFORMAT_PE_DATADIR_INDEX_CERTIFICATE 4
#define EXECFORMAT_PE_DATADIR_INDEX_BASE_RELOCATION 5 // .reloc
#define EXECFORMAT_PE_DATADIR_INDEX_DEBUG 6 // .debug
#define EXECFORMAT_PE_DATADIR_INDEX_ARCHITECTURE 7 // Reserved, must be 0
#define EXECFORMAT_PE_DATADIR_INDEX_GLOBAL_PTR 8 // The RVA of the value to be stored in the global pointer register. The size must be 0.
#define EXECFORMAT_PE_DATADIR_INDEX_TLS 9 // .tls
#define EXECFORMAT_PE_DATADIR_INDEX_LOAD_CONFIG 10
#define EXECFORMAT_PE_DATADIR_INDEX_BOUND_IMPORT 11
#define EXECFORMAT_PE_DATADIR_INDEX_IAT 12 // Import Address Table
#define EXECFORMAT_PE_DATADIR_INDEX_DELAY_IMPORT_DESC 13 // Delay Import Descriptor
#define EXECFORMAT_PE_DATADIR_INDEX_CLR_RUNTIME_HEADER 14 // Common Language Runtime header
#define EXECFORMAT_PE_DATADIR_INDEX_RESERVED_15 15 // Reserved, must be 0
typedef struct {
execformat_pe_OptionalHeader_StandardFields_PE32P std;
execformat_pe_OptionalHeader_WindowsFields_PE32P win;
execformat_pe_OptionalHeader_DataDirectory data[1];
} PACKED execformat_pe_OptionalHeader_PE32P;
typedef struct {
execformat_pe_OptionalHeader_StandardFields_PE32 std;
execformat_pe_OptionalHeader_WindowsFields_PE32 win;
execformat_pe_OptionalHeader_DataDirectory data[1];
} PACKED execformat_pe_OptionalHeader_PE32;
static inline void execformat_pe_OptionalHeader_CheckPacking() {
assert(sizeof(execformat_pe_OptionalHeader_StandardFields_PE32) == 28);
assert(sizeof(execformat_pe_OptionalHeader_StandardFields_PE32P) == 24);
assert(sizeof(execformat_pe_OptionalHeader_WindowsFields_PE32) == 68);
assert(sizeof(execformat_pe_OptionalHeader_WindowsFields_PE32P) == 88);
assert(offsetof(execformat_pe_OptionalHeader_PE32, std) == 0);
assert(offsetof(execformat_pe_OptionalHeader_PE32, win) == 28);
assert(offsetof(execformat_pe_OptionalHeader_PE32, data[0]) == 96);
assert(offsetof(execformat_pe_OptionalHeader_PE32P, std) == 0);
assert(offsetof(execformat_pe_OptionalHeader_PE32P, win) == 24);
assert(offsetof(execformat_pe_OptionalHeader_PE32P, data[0]) == 112);
}
typedef struct {
char name[8];
uint32_t virtualSize;
uint32_t virtualAddr; // Offset(RVA) instead of a real Virtual Address
uint32_t sizeofRawData;
uint32_t pointerToRawData; // The file pointer to the first page of the section within the file.
uint32_t pointerToRelocations;
uint32_t pointerToLineNumbers;
uint16_t numRelocations;
uint16_t numLineNumbers; // deprecated, should be 0
uint32_t flags; // The flags that describe the characteristics of the section.
} PACKED execformat_pe_SectionHeader;
// Section Characteristics
#define EXECFORMAT_PE_SECTIONFLAG_NO_PAD 0x00000008u // section should not be padded to the next boundary, obsolete.
#define EXECFORMAT_PE_SECTIONFLAG_CNT_CODE 0x00000020u // section contains executable code.
#define EXECFORMAT_PE_SECTIONFLAG_CNT_INITIALIZED_DATA 0x00000040u // section contains initialized data.
#define EXECFORMAT_PE_SECTIONFLAG_CNT_UNINITIALIZED_DATA 0x00000080u // section contains uninitialized data.
#define EXECFORMAT_PE_SECTIONFLAG_LNK_OTHER 0x00000100u // Reserved.
#define EXECFORMAT_PE_SECTIONFLAG_LNK_INFO 0x00000200u // section contains comments or other info. .drectve section has this type.
#define EXECFORMAT_PE_SECTIONFLAG_LNK_REMOVE 0x00000800u // section will not become part of the image. valid only for object files.
#define EXECFORMAT_PE_SECTIONFLAG_LNK_COMDAT 0x00001000u // The section contains COMDAT data. valid only for object files.
#define EXECFORMAT_PE_SECTIONFLAG_GPREL 0x00008000u // section contains data referenced through the global pointer (GP).
#define EXECFORMAT_PE_SECTIONFLAG_MEM_PURGEABLE 0x00020000u // Reserved.
#define EXECFORMAT_PE_SECTIONFLAG_MEM_16BIT 0x00020000u // Reserved.
#define EXECFORMAT_PE_SECTIONFLAG_MEM_LOCKED 0x00040000u // Reserved.
#define EXECFORMAT_PE_SECTIONFLAG_MEM_PRELOAD 0x00080000u // Reserved.
// Section Characteristics - Alignment (only for object files)
#define EXECFORMAT_PE_SECTIONFLAG_ALIGN_1BYTES 0x00100000u // Align data on a 1-byte boundary. Valid only for object files.
#define EXECFORMAT_PE_SECTIONFLAG_ALIGN_2BYTES 0x00200000u // Align data on a 2-byte boundary. Valid only for object files.
#define EXECFORMAT_PE_SECTIONFLAG_ALIGN_4BYTES 0x00300000u // Align data on a 4-byte boundary. Valid only for object files.
#define EXECFORMAT_PE_SECTIONFLAG_ALIGN_8BYTES 0x00400000u // Align data on a 8-byte boundary. Valid only for object files.
#define EXECFORMAT_PE_SECTIONFLAG_ALIGN_16BYTES 0x00500000u // Align data on a 16-byte boundary. Valid only for object files.
#define EXECFORMAT_PE_SECTIONFLAG_ALIGN_32BYTES 0x00600000u // Align data on a 32-byte boundary. Valid only for object files.
#define EXECFORMAT_PE_SECTIONFLAG_ALIGN_64BYTES 0x00700000u // Align data on a 64-byte boundary. Valid only for object files.
#define EXECFORMAT_PE_SECTIONFLAG_ALIGN_128BYTES 0x00800000u // Align data on a 128-byte boundary. Valid only for object files.
#define EXECFORMAT_PE_SECTIONFLAG_ALIGN_256BYTES 0x00900000u // Align data on a 256-byte boundary. Valid only for object files.
#define EXECFORMAT_PE_SECTIONFLAG_ALIGN_512BYTES 0x00a00000u // Align data on a 512-byte boundary. Valid only for object files.
#define EXECFORMAT_PE_SECTIONFLAG_ALIGN_1024BYTES 0x00b00000u // Align data on a 1024-byte boundary. Valid only for object files.
#define EXECFORMAT_PE_SECTIONFLAG_ALIGN_2048BYTES 0x00c00000u // Align data on a 2048-byte boundary. Valid only for object files.
#define EXECFORMAT_PE_SECTIONFLAG_ALIGN_4096BYTES 0x00d00000u // Align data on a 4096-byte boundary. Valid only for object files.
#define EXECFORMAT_PE_SECTIONFLAG_ALIGN_8192BYTES 0x00e00000u // Align data on a 8192-byte boundary. Valid only for object files.
#define EXECFORMAT_PE_SECTIONFLAG_ALIGN_MASK 0x00f00000u // Mask for section data alignment type.
// Section Characteristics - Memory
#define EXECFORMAT_PE_SECTIONFLAG_LNK_NRELOC_OVFL 0x01000000u // section contains extended relocations.
#define EXECFORMAT_PE_SECTIONFLAG_MEM_DISCARDABLE 0x02000000u // section can be discarded as needed.
#define EXECFORMAT_PE_SECTIONFLAG_MEM_NOT_CACHED 0x04000000u // section cannot be cached.
#define EXECFORMAT_PE_SECTIONFLAG_MEM_NOT_PAGED 0x08000000u // section cannot be paged out.
#define EXECFORMAT_PE_SECTIONFLAG_MEM_SHARED 0x10000000u // section can be shared in memory.
#define EXECFORMAT_PE_SECTIONFLAG_MEM_EXECUTE 0x20000000u // section can be executed as code.
#define EXECFORMAT_PE_SECTIONFLAG_MEM_READ 0x40000000u // section can be read.
#define EXECFORMAT_PE_SECTIONFLAG_MEM_WRITE 0x80000000u // section can be written to.
#ifdef __cplusplus
} // extern "C"
#endif

View File

@ -0,0 +1,106 @@
#include "format.h"
#include "struct.h"
#include "../../memory/memory.h"
#include <string.h>
static const char *caseMachine(uint16_t type) {
#define CASE(expr) \
case EXECFORMAT_PE_##expr: \
return #expr;
switch (type) {
CASE(MACHINE_UNKNOWN)
CASE(MACHINE_AMD64)
CASE(MACHINE_I386)
CASE(MACHINE_EBC)
default:
return "(Unknown)";
}
#undef CASE
}
static void caseFlags(uint32_t flags) {
#define CASE(expr) \
if (flags & EXECFORMAT_PE_FLAG_##expr) { \
printf("|" #expr); \
}
CASE(RELOCS_STRIPPED)
CASE(EXECUTABLE_IMAGE)
CASE(LINE_NUMS_STRIPPED)
CASE(LOCAL_SYMS_STRIPPED)
CASE(AGGRESSIVE_WS_TRIM)
CASE(LARGE_ADDRESS_AWARE)
CASE(RESERVED_0x0040)
CASE(BYTES_REVERSED_LO)
CASE(32BIT_MACHINE)
CASE(DEBUG_STRIPPED)
CASE(REMOVABLE_RUN_FROM_SWAP)
CASE(NET_RUN_FROM_SWAP)
CASE(SYSTEM)
CASE(DLL)
CASE(UP_SYSTEM_ONLY)
CASE(BYTES_REVERSED_HI)
printf("|\n");
#undef CASE
}
static void caseSectionFlags(uint32_t flags) {
#define CASE(expr) \
if (flags & EXECFORMAT_PE_SECTIONFLAG_##expr) { \
printf("|" #expr); \
}
CASE(NO_PAD)
CASE(CNT_CODE)
CASE(CNT_INITIALIZED_DATA)
CASE(CNT_UNINITIALIZED_DATA)
CASE(LNK_INFO)
CASE(LNK_REMOVE)
CASE(LNK_COMDAT)
CASE(GPREL)
CASE(LNK_NRELOC_OVFL)
CASE(MEM_DISCARDABLE)
CASE(MEM_NOT_CACHED)
CASE(MEM_NOT_PAGED)
CASE(MEM_SHARED)
CASE(MEM_EXECUTE)
CASE(MEM_READ)
CASE(MEM_WRITE)
printf("|\n");
#undef CASE
}
void execformat_pe_ReadSystemHeader(execformat_pe_PortableExecutable *pe) {
execformat_pe_OptionalHeader_CheckPacking();
uint64_t a;
asm volatile("leaq (%%rip), %0"
: "=r"(a));
io_Printf("Stack position: %llx, RIP=%llx\n", &a, a);
void *addr = (void *)paging_LoaderCodeAddress;
execformat_pe_PortableExecutable rpe;
if (pe == 0)
pe = &rpe;
execformat_pe_LoadMemory(pe, addr, paging_LoaderCodeSize);
printf("%s Executable, Machine Type=0x%x (%s), Flags=", pe->isPE32P ? "PE32+" : "PE32", pe->header->machineType, caseMachine(pe->header->machineType));
caseFlags(pe->header->flags);
printf("\nImageBase=%d (0x%x)\n", ((execformat_pe_OptionalHeader_PE32P *)pe->optional)->win.imageBase, ((execformat_pe_OptionalHeader_PE32P *)pe->optional)->win.imageBase);
printf("numRVAandSizes=%d (want %d)\n\n", pe->numDataDir, EXECFORMAT_PE_DATADIR_INDEX_CLR_RUNTIME_HEADER + 2);
printf("numSections=%d\n", pe->numSections);
char name[9];
for (int i = 0; i < pe->numSections; i++) {
name[8] = 0;
strncpy(name, pe->sections[i].name, 8);
printf(" %8s RVA0x%08x File0x%08x Len0x%08x ", name, pe->sections[i].virtualAddr, pe->sections[i].pointerToRawData, pe->sections[i].virtualSize);
caseSectionFlags(pe->sections[i].flags);
}
}

108
execformat/pe/reloc.c Normal file
View File

@ -0,0 +1,108 @@
#include "reloc.h"
#include "../../runtime/panic_assert.h"
#include <string.h>
static inline int __local_strncmp(const char *s1, const char *s2, size_t n) {
while (n && *s1 && (*s1 == *s2)) {
++s1;
++s2;
--n;
}
if (n == 0) {
return 0;
} else {
return (*(unsigned char *)s1 - *(unsigned char *)s2);
}
}
static const char *caseFlags(uint16_t type) {
#define CASE(expr) \
case EXECFORMAT_PE_BASERELOC_##expr: \
return #expr;
switch (type) {
CASE(ABSOLUTE)
CASE(HIGH)
CASE(LOW)
CASE(HIGHLOW)
CASE(HIGHADJ)
CASE(DIR64)
default:
return "(Unknown)";
}
#undef CASE
}
void execformat_pe_BaseRelocate(execformat_pe_PortableExecutable *pe, void *relocBase, void *relocEnd, uint64_t currentBase, uint64_t targetBase) {
if (pe->begin == 0)
return;
if (currentBase == 0) {
if (pe->isPE32P)
currentBase = ((execformat_pe_OptionalHeader_PE32P *)pe->optional)->win.imageBase;
else
currentBase = ((execformat_pe_OptionalHeader_PE32 *)pe->optional)->win.imageBase;
}
void *reloc = 0, *reloc_end;
if (relocBase != 0 && relocEnd != 0) {
reloc = relocBase;
reloc_end = relocEnd;
} else {
// find the .reloc section
for (int i = 0; i < pe->numSections; i++)
if (__local_strncmp(pe->sections[i].name, ".reloc", 6) == 0) {
reloc = pe->begin + pe->sections[i].virtualAddr;
reloc_end = reloc + pe->sections[i].virtualSize;
break;
}
if (reloc == 0)
return; // no .reloc section
}
uint64_t diff = targetBase - currentBase;
while (reloc < reloc_end) {
execformat_pe_BaseRelocBlock *block = reloc;
int entries = (block->blockSize - 8) / 2;
assert((block->blockSize - 8) % 2 == 0);
printf("BaseReloc Block RVA=0x%08x, entries=%d\n", block->pageOffset, entries);
for (int i = 0; i < entries; i++) {
void *target = pe->begin + block->pageOffset + (block->entries[i] & EXECFORMAT_PE_BASERELOC_OFFSET_MASK);
//uint64_t cur = 0, new = 0;
switch (block->entries[i] & EXECFORMAT_PE_BASERELOC_FLAG_MASK) {
case EXECFORMAT_PE_BASERELOC_HIGH:
//cur = *((uint16_t *)target);
*((uint16_t *)target) += (uint16_t)(diff >> 16);
//new = *((uint16_t *)target);
break;
case EXECFORMAT_PE_BASERELOC_LOW:
//cur = *((uint16_t *)target);
*((uint16_t *)target) += (uint16_t)(diff);
//new = *((uint16_t *)target);
break;
case EXECFORMAT_PE_BASERELOC_HIGHLOW:
//cur = *((uint32_t *)target);
*((uint32_t *)target) += (uint32_t)(diff);
//new = *((uint32_t *)target);
break;
case EXECFORMAT_PE_BASERELOC_DIR64:
//cur = *((uint64_t *)target);
*((uint64_t *)target) += (diff);
//new = *((uint64_t *)target);
break;
case EXECFORMAT_PE_BASERELOC_HIGHADJ:
i++;
break;
}
//printf(" Reloc Off0x%08llx CUR=0x%08llx(Off0x%08llx), NEW=0x%llx, Type=%s\n", target - pe->begin, cur, cur - currentBase, new, caseFlags(block->entries[i] & EXECFORMAT_PE_BASERELOC_FLAG_MASK));
}
reloc += block->blockSize;
}
}

30
execformat/pe/reloc.h Normal file
View File

@ -0,0 +1,30 @@
#include "format.h"
#include "struct.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
uint32_t pageOffset;
uint32_t blockSize; // the size in bytes of the Relocation Block, including the 2 header fields and the entries that follow
uint16_t entries[1];
} PACKED execformat_pe_BaseRelocBlock;
#define EXECFORMAT_PE_BASERELOC_ABSOLUTE (0x0000u) // The base relocation is skipped. This type can be used to pad a block.
#define EXECFORMAT_PE_BASERELOC_HIGH (0x1000u) // The base relocation adds the high 16 bits of the difference to the 16-bit field at offset.
#define EXECFORMAT_PE_BASERELOC_LOW (0x2000u) // The base relocation adds the low 16 bits of the difference to the 16-bit field at offset.
#define EXECFORMAT_PE_BASERELOC_HIGHLOW (0x3000u) // The base relocation applies all 32 bits of the difference to the 32-bit field at offset.
#define EXECFORMAT_PE_BASERELOC_HIGHADJ (0x4000u) // Weird, ignored (together with the next entry).
#define EXECFORMAT_PE_BASERELOC_DIR64 (0xa000u) // The base relocation applies the difference to the 64-bit field at offset.
#define EXECFORMAT_PE_BASERELOC_FLAG_MASK (0xf000u)
#define EXECFORMAT_PE_BASERELOC_OFFSET_MASK (0x0fffu)
void execformat_pe_BaseRelocate(execformat_pe_PortableExecutable *pe, void *relocBase, void *relocEnd, uint64_t currentBase, uint64_t targetBase);
#ifdef __cplusplus
} // extern "C"
#endif

50
execformat/pe/struct.c Normal file
View File

@ -0,0 +1,50 @@
#include "struct.h"
#include "../../runtime/panic_assert.h"
#include <string.h>
void execformat_pe_LoadMemory(execformat_pe_PortableExecutable *pe, void *image, uint32_t size) {
pe->begin = pe->pemagic = 0;
if (size < 2 || ((char *)image)[0] != 'M' || ((char *)image)[1] != 'Z')
return;
// look for the "PE\0\0" magic on a 8-byte boundary
for (int i = 8; i < size; i++) {
if (memcmp(image + i, EXECFORMAT_PE_HEADER_MAGIC, 4) == 0) {
pe->pemagic = image + i;
break;
}
}
if (pe->pemagic == 0)
return; // not found
pe->header = (execformat_pe_Header *)(pe->pemagic + 4);
pe->numSections = pe->header->numSections;
pe->optional = (void *)pe->header + sizeof(execformat_pe_Header);
pe->isPE32P = (*((uint16_t *)pe->optional) == EXECFORMAT_PE_OPTIONAL_HEADER_MAGIC_PE32P);
if (pe->isPE32P) {
pe->numDataDir = ((execformat_pe_OptionalHeader_PE32P *)pe->optional)->win.numRVAandSizes;
pe->sections =
pe->optional +
sizeof(execformat_pe_OptionalHeader_StandardFields_PE32P) +
sizeof(execformat_pe_OptionalHeader_WindowsFields_PE32P) +
sizeof(execformat_pe_OptionalHeader_DataDirectory) * pe->numDataDir;
assert((void *)pe->sections - pe->optional == pe->header->sizeofOptionalHeader && "PE32P OptionalHeader size mismatch");
} else {
pe->numDataDir = ((execformat_pe_OptionalHeader_PE32 *)pe->optional)->win.numRVAandSizes;
pe->sections =
pe->optional +
sizeof(execformat_pe_OptionalHeader_StandardFields_PE32) +
sizeof(execformat_pe_OptionalHeader_WindowsFields_PE32) +
sizeof(execformat_pe_OptionalHeader_DataDirectory) * pe->numDataDir;
assert((void *)pe->sections - pe->optional == pe->header->sizeofOptionalHeader && "PE32 OptionalHeader size mismatch");
}
pe->begin = image;
pe->size = size;
}

29
execformat/pe/struct.h Normal file
View File

@ -0,0 +1,29 @@
#pragma once
#include "../../main.h"
#include "format.h"
#include "stdbool.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct {
void * begin; // beginning of the image
char * pemagic; // PE magic "PE\0\0", after the MS-DOS stub
execformat_pe_Header * header; // PE headers, 4 bytes right after the magic
void * optional; // optional headers, converted to OptionalHeader_PE32/PE32P on access
execformat_pe_SectionHeader *sections; // start of the section header tables
uint32_t size; // size of the file in bytes
int numDataDir; // shorthand for ((execformat_pe_OptionalHeader_PE32P*)pe->optional)->win.numRVAandSizes
int numSections;
bool isPE32P;
} execformat_pe_PortableExecutable;
void execformat_pe_LoadMemory(execformat_pe_PortableExecutable *pe, void *image, uint32_t size);
#ifdef __cplusplus
} // extern "C"
#endif

View File

@ -0,0 +1,118 @@
#include "format.h"
#include "struct.h"
#include "reloc.h"
#include "stdio.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "sys/mman.h"
#include <assert.h>
#include <string.h>
#include <malloc.h>
const char *caseMachine(uint16_t type) {
#define CASE(expr) \
case EXECFORMAT_PE_##expr: \
return #expr;
switch (type) {
CASE(MACHINE_UNKNOWN)
CASE(MACHINE_AMD64)
CASE(MACHINE_I386)
CASE(MACHINE_EBC)
default:
return "(Unknown)";
}
#undef CASE
}
void caseFlags(uint32_t flags) {
#define CASE(expr) \
if (flags & EXECFORMAT_PE_FLAG_##expr) { \
printf("|" #expr); \
}
CASE(RELOCS_STRIPPED)
CASE(EXECUTABLE_IMAGE)
CASE(LINE_NUMS_STRIPPED)
CASE(LOCAL_SYMS_STRIPPED)
CASE(AGGRESSIVE_WS_TRIM)
CASE(LARGE_ADDRESS_AWARE)
CASE(RESERVED_0x0040)
CASE(BYTES_REVERSED_LO)
CASE(32BIT_MACHINE)
CASE(DEBUG_STRIPPED)
CASE(REMOVABLE_RUN_FROM_SWAP)
CASE(NET_RUN_FROM_SWAP)
CASE(SYSTEM)
CASE(DLL)
CASE(UP_SYSTEM_ONLY)
CASE(BYTES_REVERSED_HI)
printf("|\n");
#undef CASE
}
void caseSectionFlags(uint32_t flags) {
#define CASE(expr) \
if (flags & EXECFORMAT_PE_SECTIONFLAG_##expr) { \
printf("|" #expr); \
}
CASE(NO_PAD)
CASE(CNT_CODE)
CASE(CNT_INITIALIZED_DATA)
CASE(CNT_UNINITIALIZED_DATA)
CASE(LNK_INFO)
CASE(LNK_REMOVE)
CASE(LNK_COMDAT)
CASE(GPREL)
CASE(LNK_NRELOC_OVFL)
CASE(MEM_DISCARDABLE)
CASE(MEM_NOT_CACHED)
CASE(MEM_NOT_PAGED)
CASE(MEM_SHARED)
CASE(MEM_EXECUTE)
CASE(MEM_READ)
CASE(MEM_WRITE)
printf("|\n");
#undef CASE
}
int main(int argc, char *argv[]) {
execformat_pe_OptionalHeader_CheckPacking();
int f = open("../../Main.efi", O_RDONLY);
assert(f != -1 && "File open failed");
struct stat st;
fstat(f, &st);
void *maddr = mmap(0, st.st_size, PROT_READ, MAP_SHARED, f, 0);
void *addr = malloc(st.st_size);
memcpy(addr, maddr, st.st_size);
execformat_pe_PortableExecutable pe;
execformat_pe_LoadMemory(&pe, addr, st.st_size);
printf("%s Executable, Machine Type=0x%x (%s), Flags=", pe.isPE32P ? "PE32+" : "PE32", pe.header->machineType, caseMachine(pe.header->machineType));
caseFlags(pe.header->flags);
printf("\nImageBase=%d (0x%x)\n", ((execformat_pe_OptionalHeader_PE32P *)pe.optional)->win.imageBase, ((execformat_pe_OptionalHeader_PE32P *)pe.optional)->win.imageBase);
printf("numRVAandSizes=%d (want %d)\n\n", pe.numDataDir, EXECFORMAT_PE_DATADIR_INDEX_CLR_RUNTIME_HEADER + 2);
printf("numSections=%d\n", pe.numSections);
char name[9];
for (int i = 0; i < pe.numSections; i++) {
name[8] = 0;
strncpy(name, pe.sections[i].name, 8);
printf(" %8s Rva0x%08x File0x%08x Len0x%08x ", name, pe.sections[i].virtualAddr, pe.sections[i].pointerToRawData, pe.sections[i].virtualSize);
caseSectionFlags(pe.sections[i].flags);
}
execformat_pe_BaseRelocate(&pe, 0, 0xFFFFFFFFC0000000ull);
return 0;
}