:: commit 8b01ea34300fbb8e527b0f862a6b3d12e1241517

czapek <32851089+48cf@users.noreply.github.com> — 2025-01-07 23:35

parents: be2bf8358d

lib: Add support for loading PE images (#442)

* lib: Add support for loading PE images

* lib/pe: Use standard integer types

* lib/pe: Fix section alignment

* lib/pe: Properly iterate base relocation blocks

* lib/pe: Add missing machine types

* protos/limine: Add kernel format detection logic

* lib/pe: Fix integer to pointer conversion errors

* lib/elf: Add previously removed check

* lib/pe: Fix base relocation block iteration code

* lib/pe: Add missing machine types to pe_bits
diff --git a/common/lib/elf.c b/common/lib/elf.c
index 6812ab1e..659e3adc 100644
--- a/common/lib/elf.c
+++ b/common/lib/elf.c
@@ -183,7 +183,6 @@ int elf_bits(uint8_t *elf) {
     struct elf64_hdr *hdr = (void *)elf;
 
     if (strncmp((char *)hdr->ident, "\177ELF", 4)) {
-        printv("elf: Not a valid ELF file.\n");
         return -1;
     }
 
@@ -626,7 +625,7 @@ static uint64_t elf64_max_align(uint8_t *elf) {
     return ret;
 }
 
-static void elf64_get_ranges(uint8_t *elf, uint64_t slide, struct elf_range **_ranges, uint64_t *_ranges_count) {
+static void elf64_get_ranges(uint8_t *elf, uint64_t slide, struct mem_range **_ranges, uint64_t *_ranges_count) {
     struct elf64_hdr *hdr = (void *)elf;
 
     uint64_t ranges_count = 0;
@@ -657,7 +656,7 @@ static void elf64_get_ranges(uint8_t *elf, uint64_t slide, struct elf_range **_r
         panic(true, "elf: No higher half PHDRs exist");
     }
 
-    struct elf_range *ranges = ext_mem_alloc(ranges_count * sizeof(struct elf_range));
+    struct mem_range *ranges = ext_mem_alloc(ranges_count * sizeof(struct mem_range));
 
     size_t r = 0;
     for (uint16_t i = 0; i < hdr->ph_num; i++) {
@@ -678,7 +677,18 @@ static void elf64_get_ranges(uint8_t *elf, uint64_t slide, struct elf_range **_r
 
         ranges[r].base = load_addr & ~(phdr->p_align - 1);
         ranges[r].length = ALIGN_UP(this_top - ranges[r].base, phdr->p_align);
-        ranges[r].permissions = phdr->p_flags & 0b111;
+
+        if (phdr->p_flags & ELF_PF_X) {
+            ranges[r].permissions |= MEM_RANGE_X;
+        }
+
+        if (phdr->p_flags & ELF_PF_W) {
+            ranges[r].permissions |= MEM_RANGE_W;
+        }
+
+        if (phdr->p_flags & ELF_PF_R) {
+            ranges[r].permissions |= MEM_RANGE_R;
+        }
 
         r++;
     }
@@ -687,7 +697,7 @@ static void elf64_get_ranges(uint8_t *elf, uint64_t slide, struct elf_range **_r
     *_ranges = ranges;
 }
 
-bool elf64_load(uint8_t *elf, uint64_t *entry_point, uint64_t *_slide, uint32_t alloc_type, bool kaslr, struct elf_range **ranges, uint64_t *ranges_count, uint64_t *physical_base, uint64_t *virtual_base, uint64_t *_image_size, uint64_t *_image_size_before_bss, bool *is_reloc) {
+bool elf64_load(uint8_t *elf, uint64_t *entry_point, uint64_t *_slide, uint32_t alloc_type, bool kaslr, struct mem_range **ranges, uint64_t *ranges_count, uint64_t *physical_base, uint64_t *virtual_base, uint64_t *_image_size, uint64_t *_image_size_before_bss, bool *is_reloc) {
     struct elf64_hdr *hdr = (void *)elf;
 
     elf64_validate(hdr);
diff --git a/common/lib/elf.h b/common/lib/elf.h
index 3a8cb33c..7a1d66a5 100644
--- a/common/lib/elf.h
+++ b/common/lib/elf.h
@@ -4,6 +4,7 @@
 #include <stdint.h>
 #include <stdbool.h>
 #include <lib/elsewhere.h>
+#include <lib/misc.h>
 
 #define FIXED_HIGHER_HALF_OFFSET_64 ((uint64_t)0xffffffff80000000)
 
@@ -11,12 +12,6 @@
 #define ELF_PF_W 2
 #define ELF_PF_R 4
 
-struct elf_range {
-    uint64_t base;
-    uint64_t length;
-    uint64_t permissions;
-};
-
 struct elf_section_hdr_info {
     uint32_t section_entry_size;
     uint32_t str_section_idx;
@@ -30,7 +25,7 @@ struct elf_section_hdr_info elf64_section_hdr_info(uint8_t *elf);
 struct elf_section_hdr_info elf32_section_hdr_info(uint8_t *elf);
 
 bool elf64_load_section(uint8_t *elf, void *buffer, const char *name, size_t limit, uint64_t slide);
-bool elf64_load(uint8_t *elf, uint64_t *entry_point, uint64_t *_slide, uint32_t alloc_type, bool kaslr, struct elf_range **ranges, uint64_t *ranges_count, uint64_t *physical_base, uint64_t *virtual_base, uint64_t *image_size, uint64_t *image_size_before_bss, bool *is_reloc);
+bool elf64_load(uint8_t *elf, uint64_t *entry_point, uint64_t *_slide, uint32_t alloc_type, bool kaslr, struct mem_range **ranges, uint64_t *ranges_count, uint64_t *physical_base, uint64_t *virtual_base, uint64_t *image_size, uint64_t *image_size_before_bss, bool *is_reloc);
 
 bool elf32_load_elsewhere(uint8_t *elf, uint64_t *entry_point,
                           struct elsewhere_range **ranges);
diff --git a/common/lib/misc.h b/common/lib/misc.h
index f1790c53..dc7a6f2c 100644
--- a/common/lib/misc.h
+++ b/common/lib/misc.h
@@ -104,4 +104,14 @@ noreturn void loongarch_spinup(uint64_t entry, uint64_t sp, uint64_t pgdl,
 
 #define no_unwind __attribute__((section(".no_unwind")))
 
+#define MEM_RANGE_X 1
+#define MEM_RANGE_W 2
+#define MEM_RANGE_R 4
+
+struct mem_range {
+    uint64_t base;
+    uint64_t length;
+    uint64_t permissions;
+};
+
 #endif
diff --git a/common/lib/pe.c b/common/lib/pe.c
new file mode 100644
index 00000000..db01126b
--- /dev/null
+++ b/common/lib/pe.c
@@ -0,0 +1,390 @@
+#include <stdint.h>
+#include <stddef.h>
+#include <lib/misc.h>
+#include <lib/libc.h>
+#include <lib/pe.h>
+#include <lib/print.h>
+#include <lib/rand.h>
+#include <mm/pmm.h>
+
+#define FIXED_HIGHER_HALF_OFFSET_64 ((uint64_t)0xffffffff80000000)
+
+#define IMAGE_DOS_SIGNATURE 0x5a4d
+
+typedef struct _IMAGE_DOS_HEADER {
+    uint16_t e_magic;
+    uint16_t e_cblp;
+    uint16_t e_cp;
+    uint16_t e_crlc;
+    uint16_t e_cparhdr;
+    uint16_t e_minalloc;
+    uint16_t e_maxalloc;
+    uint16_t e_ss;
+    uint16_t e_sp;
+    uint16_t e_csum;
+    uint16_t e_ip;
+    uint16_t e_cs;
+    uint16_t e_lfarlc;
+    uint16_t e_ovno;
+    uint16_t e_res[4];
+    uint16_t e_oemid;
+    uint16_t e_oeminfo;
+    uint16_t e_res2[10];
+    uint32_t e_lfanew;
+} IMAGE_DOS_HEADER;
+
+#define IMAGE_FILE_MACHINE_I386 0x14c
+#define IMAGE_FILE_MACHINE_AMD64 0x8664
+#define IMAGE_FILE_MACHINE_ARM64 0xaa64
+#define IMAGE_FILE_MACHINE_RISCV64 0x5064
+#define IMAGE_FILE_MACHINE_LOONGARCH64 0x6264
+
+#define IMAGE_FILE_RELOCS_STRIPPED 1
+#define IMAGE_FILE_EXECUTABLE_IMAGE 2
+
+typedef struct {
+    uint16_t Machine;
+    uint16_t NumberOfSections;
+    uint32_t TimeDateStamp;
+    uint32_t PointerToSymbolTable;
+    uint32_t NumberOfSymbols;
+    uint16_t SizeOfOptionalHeader;
+    uint16_t Characteristics;
+} IMAGE_FILE_HEADER;
+
+typedef struct {
+    uint32_t VirtualAddress;
+    uint32_t Size;
+} IMAGE_DATA_DIRECTORY;
+
+#define IMAGE_NT_OPTIONAL_HDR32_MAGIC 0x10b
+#define IMAGE_NT_OPTIONAL_HDR64_MAGIC 0x20b
+
+#define IMAGE_DIRECTORY_ENTRY_EXPORT 0
+#define IMAGE_DIRECTORY_ENTRY_IMPORT 1
+#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2
+#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3
+#define IMAGE_DIRECTORY_ENTRY_SECURITY 4
+#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5
+#define IMAGE_DIRECTORY_ENTRY_DEBUG 6
+#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7
+#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8
+#define IMAGE_DIRECTORY_ENTRY_TLS 9
+#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10
+#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11
+#define IMAGE_DIRECTORY_ENTRY_IAT 12
+#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13
+#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14
+
+typedef struct {
+    uint16_t Magic;
+    uint8_t MajorLinkerVersion;
+    uint8_t MinorLinkerVersion;
+    uint32_t SizeOfCode;
+    uint32_t SizeOfInitializedData;
+    uint32_t SizeOfUninitializedData;
+    uint32_t AddressOfEntryPoint;
+    uint32_t BaseOfCode;
+    uint64_t ImageBase;
+    uint32_t SectionAlignment;
+    uint32_t FileAlignment;
+    uint16_t MajorOperatingSystemVersion;
+    uint16_t MinorOperatingSystemVersion;
+    uint16_t MajorImageVersion;
+    uint16_t MinorImageVersion;
+    uint16_t MajorSubsystemVersion;
+    uint16_t MinorSubsystemVersion;
+    uint32_t Win32VersionValue;
+    uint32_t SizeOfImage;
+    uint32_t SizeOfHeaders;
+    uint32_t CheckSum;
+    uint16_t Subsystem;
+    uint16_t DllCharacteristics;
+    uint64_t SizeOfStackReserve;
+    uint64_t SizeOfStackCommit;
+    uint64_t SizeOfHeapReserve;
+    uint64_t SizeOfHeapCommit;
+    uint32_t LoaderFlags;
+    uint32_t NumberOfRvaAndSizes;
+    IMAGE_DATA_DIRECTORY DataDirectory[16];
+} IMAGE_OPTIONAL_HEADER64;
+
+#define IMAGE_NT_SIGNATURE 0x4550
+
+typedef struct {
+    uint32_t Signature;
+    IMAGE_FILE_HEADER FileHeader;
+    IMAGE_OPTIONAL_HEADER64 OptionalHeader;
+} IMAGE_NT_HEADERS64;
+
+#define IMAGE_SCN_MEM_DISCARDABLE 0x2000000
+#define IMAGE_SCN_MEM_EXECUTE 0x20000000
+#define IMAGE_SCN_MEM_READ 0x40000000
+#define IMAGE_SCN_MEM_WRITE 0x80000000
+
+typedef struct {
+    char Name[8];
+    uint32_t VirtualSize;
+    uint32_t VirtualAddress;
+    uint32_t SizeOfRawData;
+    uint32_t PointerToRawData;
+    uint32_t PointerToRelocations;
+    uint32_t PointerToLinenumbers;
+    uint16_t NumberOfRelocations;
+    uint16_t NumberOfLinenumbers;
+    uint32_t Characteristics;
+} IMAGE_SECTION_HEADER;
+
+typedef struct {
+    union {
+        uint32_t Characteristics;
+        uint32_t OriginalFirstThunk;
+    };
+    uint32_t TimeDateStamp;
+    uint32_t ForwarderChain;
+    uint32_t Name;
+    uint32_t FirstThunk;
+} IMAGE_IMPORT_DESCRIPTOR;
+
+#define IMAGE_REL_BASED_ABSOLUTE 0
+#define IMAGE_REL_BASED_HIGHLOW 3
+#define IMAGE_REL_BASED_DIR64 10
+
+typedef struct {
+    uint32_t VirtualAddress;
+    uint32_t SizeOfBlock;
+} IMAGE_BASE_RELOCATION_BLOCK;
+
+static void pe64_validate(uint8_t *image) {
+    IMAGE_DOS_HEADER *dos_hdr = (IMAGE_DOS_HEADER *)image;
+
+    if (dos_hdr->e_magic != IMAGE_DOS_SIGNATURE) {
+        panic(true, "pe: Not a valid PE file");
+    }
+
+    IMAGE_NT_HEADERS64 *nt_hdrs = (IMAGE_NT_HEADERS64 *)(image + dos_hdr->e_lfanew);
+
+    if (nt_hdrs->Signature != IMAGE_NT_SIGNATURE) {
+        panic(true, "pe: Not a valid PE file");
+    }
+
+#if defined(__x86_64__) || defined(__i386__)
+    if (nt_hdrs->FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64) {
+        panic(true, "pe: Not an x86-64 PE file");
+    }
+#elif defined(__aarch64__)
+    if (nt_hdrs->FileHeader.Machine != IMAGE_FILE_MACHINE_ARM64) {
+        panic(true, "pe: Not an ARM64 PE file");
+    }
+#elif defined (__riscv) && (__riscv_xlen == 64)
+    if (nt_hdrs->FileHeader.Machine != IMAGE_FILE_MACHINE_RISCV64) {
+        panic(true, "pe: Not a RISC-V PE file");
+    }
+#elif defined (__loongarch__) && (__loongarch_grlen == 64)
+    if (nt_hdrs->FileHeader.Machine != IMAGE_FILE_MACHINE_LOONGARCH64) {
+        panic(true, "pe: Not a loongarch64 PE file");
+    }
+#else
+#error Unknown architecture
+#endif
+}
+
+int pe_bits(uint8_t *image) {
+    IMAGE_DOS_HEADER *dos_hdr = (IMAGE_DOS_HEADER *)image;
+
+    if (dos_hdr->e_magic != IMAGE_DOS_SIGNATURE) {
+        return -1;
+    }
+
+    IMAGE_NT_HEADERS64 *nt_hdrs = (IMAGE_NT_HEADERS64 *)(image + dos_hdr->e_lfanew);
+
+    if (nt_hdrs->Signature != IMAGE_NT_SIGNATURE) {
+        return -1;
+    }
+
+    switch (nt_hdrs->FileHeader.Machine) {
+        case IMAGE_FILE_MACHINE_I386:
+            return 32;
+        case IMAGE_FILE_MACHINE_AMD64:
+        case IMAGE_FILE_MACHINE_ARM64:
+        case IMAGE_FILE_MACHINE_RISCV64:
+        case IMAGE_FILE_MACHINE_LOONGARCH64:
+            return 64;
+    }
+
+    return -1;
+}
+
+bool pe64_load(uint8_t *image, uint64_t *entry_point, uint64_t *_slide, uint32_t alloc_type, bool kaslr, struct mem_range **_ranges, uint64_t *_ranges_count, uint64_t *physical_base, uint64_t *virtual_base, uint64_t *_image_size, uint64_t *image_size_before_bss, bool *_is_reloc) {
+    pe64_validate(image);
+
+    IMAGE_DOS_HEADER *dos_hdr = (IMAGE_DOS_HEADER *)image;
+    IMAGE_NT_HEADERS64 *nt_hdrs = (IMAGE_NT_HEADERS64 *)(image + dos_hdr->e_lfanew);
+    IMAGE_SECTION_HEADER *sections = (IMAGE_SECTION_HEADER *)((uintptr_t)&nt_hdrs->OptionalHeader + nt_hdrs->FileHeader.SizeOfOptionalHeader);
+
+    bool is_reloc = true;
+
+    if (nt_hdrs->FileHeader.Characteristics & IMAGE_FILE_RELOCS_STRIPPED) {
+        is_reloc = false;
+    }
+
+    if (_is_reloc) {
+        *_is_reloc = is_reloc;
+    }
+
+    uint64_t image_base = nt_hdrs->OptionalHeader.ImageBase;
+    uint64_t image_size = nt_hdrs->OptionalHeader.SizeOfImage;
+    uint64_t alignment = nt_hdrs->OptionalHeader.SectionAlignment;
+
+    bool lower_to_higher = false;
+
+    if (image_base < FIXED_HIGHER_HALF_OFFSET_64) {
+        if (!is_reloc) {
+            panic(true, "pe: Lower half images are not allowed");
+        }
+
+        lower_to_higher = true;
+    }
+
+    uint64_t slide = 0;
+    size_t try_count = 0;
+    size_t max_simulated_tries = 0x10000;
+
+    if (lower_to_higher) {
+        slide = FIXED_HIGHER_HALF_OFFSET_64 - image_base;
+    }
+
+    *physical_base = (uintptr_t)ext_mem_alloc_type_aligned(image_size, alloc_type, alignment);
+    *virtual_base = image_base;
+
+    memcpy((void *)(uintptr_t)*physical_base, image, nt_hdrs->OptionalHeader.SizeOfHeaders);
+
+    if (_image_size) {
+        *_image_size = image_size;
+    }
+
+    if (is_reloc && kaslr) {
+again:
+        slide = (rand32() & ~(alignment - 1)) + (lower_to_higher ? FIXED_HIGHER_HALF_OFFSET_64 - image_base : 0);
+
+        if (*virtual_base + slide + image_size < 0xffffffff80000000 /* this comparison relies on overflow */) {
+            if (++try_count == max_simulated_tries) {
+                panic(true, "pe: Image wants to load too high");
+            }
+            goto again;
+        }
+    }
+
+    for (size_t i = 0; i < nt_hdrs->FileHeader.NumberOfSections; i++) {
+        IMAGE_SECTION_HEADER *section = &sections[i];
+
+        uintptr_t section_base = *physical_base + section->VirtualAddress;
+        uint32_t section_raw_size = section->VirtualSize < section->SizeOfRawData ? section->VirtualSize : section->SizeOfRawData;
+
+        memcpy((void *)section_base, image + section->PointerToRawData, section_raw_size);
+    }
+
+    IMAGE_DATA_DIRECTORY *import_dir = &nt_hdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
+    IMAGE_DATA_DIRECTORY *reloc_dir = &nt_hdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
+
+    if (import_dir->Size != 0) {
+        IMAGE_IMPORT_DESCRIPTOR *import_desc = (IMAGE_IMPORT_DESCRIPTOR *)((uintptr_t)*physical_base + import_dir->VirtualAddress);
+
+        if (import_desc->Name != 0) {
+            panic(true, "pe: Kernel must not have any imports");
+        }
+    }
+
+    if (reloc_dir->VirtualAddress != 0) {
+        size_t reloc_block_offset = 0;
+
+        while (reloc_dir->Size - reloc_block_offset >= sizeof(IMAGE_BASE_RELOCATION_BLOCK)) {
+            IMAGE_BASE_RELOCATION_BLOCK *block = (IMAGE_BASE_RELOCATION_BLOCK *)((uintptr_t)*physical_base + reloc_dir->VirtualAddress + reloc_block_offset);
+
+            uintptr_t block_base = *physical_base + block->VirtualAddress;
+            size_t entries = (block->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION_BLOCK)) / sizeof(uint16_t);
+            uint16_t *relocs = (uint16_t *)(block + 1);
+
+            for (size_t i = 0; i < entries; i++) {
+                uint16_t type = relocs[i] >> 12;
+                uint16_t offset = relocs[i] & 0xfff;
+
+                if (type == IMAGE_REL_BASED_ABSOLUTE) {
+                    continue;
+                }
+
+                switch (type) {
+                    case IMAGE_REL_BASED_HIGHLOW:
+                        *(uint32_t *)(block_base + offset) += slide;
+                        break;
+                    case IMAGE_REL_BASED_DIR64:
+                        *(uint64_t *)(block_base + offset) += slide;
+                        break;
+                    default:
+                        panic(true, "pe: Unsupported relocation type %u", type);
+                }
+            }
+
+            reloc_block_offset += block->SizeOfBlock;
+        }
+    }
+
+    if (image_size_before_bss) {
+        *image_size_before_bss = image_size;
+    }
+
+    *virtual_base += slide;
+    *entry_point = *virtual_base + nt_hdrs->OptionalHeader.AddressOfEntryPoint;
+
+    if (_slide) {
+        *_slide = slide;
+    }
+
+    if (_ranges && _ranges_count) {
+        size_t range_count = 0;
+
+        for (size_t i = 0; i < nt_hdrs->FileHeader.NumberOfSections; i++) {
+            IMAGE_SECTION_HEADER *section = &sections[i];
+
+            if (section->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) {
+                continue;
+            }
+
+            range_count++;
+        }
+
+        struct mem_range *ranges = ext_mem_alloc(range_count * sizeof(struct mem_range));
+
+        *_ranges = ranges;
+        *_ranges_count = range_count;
+
+        for (size_t i = 0, j = 0; i < nt_hdrs->FileHeader.NumberOfSections; i++) {
+            IMAGE_SECTION_HEADER *section = &sections[i];
+
+            if (section->Characteristics & IMAGE_SCN_MEM_DISCARDABLE) {
+                continue;
+            }
+
+            uintptr_t misalign = section->VirtualAddress % alignment;
+
+            ranges[j].base = *virtual_base + ALIGN_DOWN(section->VirtualAddress, alignment);
+            ranges[j].length = ALIGN_UP(section->VirtualSize + misalign, alignment);
+
+            if (section->Characteristics & IMAGE_SCN_MEM_EXECUTE) {
+                ranges[j].permissions |= MEM_RANGE_X;
+            }
+
+            if (section->Characteristics & IMAGE_SCN_MEM_WRITE) {
+                ranges[j].permissions |= MEM_RANGE_W;
+            }
+
+            if (section->Characteristics & IMAGE_SCN_MEM_READ) {
+                ranges[j].permissions |= MEM_RANGE_R;
+            }
+
+            j++;
+        }
+    }
+
+    return true;
+}
diff --git a/common/lib/pe.h b/common/lib/pe.h
new file mode 100644
index 00000000..920f48d4
--- /dev/null
+++ b/common/lib/pe.h
@@ -0,0 +1,12 @@
+#ifndef LIB__PE_H__
+#define LIB__PE_H__
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <lib/misc.h>
+
+int pe_bits(uint8_t *image);
+
+bool pe64_load(uint8_t *image, uint64_t *entry_point, uint64_t *_slide, uint32_t alloc_type, bool kaslr, struct mem_range **ranges, uint64_t *ranges_count, uint64_t *physical_base, uint64_t *virtual_base, uint64_t *image_size, uint64_t *image_size_before_bss, bool *is_reloc);
+
+#endif
diff --git a/common/protos/limine.c b/common/protos/limine.c
index 0b713df7..c3e7cd92 100644
--- a/common/protos/limine.c
+++ b/common/protos/limine.c
@@ -8,6 +8,7 @@
 #include <lib/acpi.h>
 #include <lib/config.h>
 #include <lib/time.h>
+#include <lib/pe.h>
 #include <lib/print.h>
 #include <lib/real.h>
 #include <lib/libc.h>
@@ -34,6 +35,21 @@
 #include <protos/limine.h>
 #include <limine.h>
 
+enum executable_format {
+    EXECUTABLE_FORMAT_ELF,
+    EXECUTABLE_FORMAT_PE,
+};
+
+static enum executable_format detect_kernel_format(uint8_t *kernel) {
+    if (elf_bits(kernel) != -1) {
+        return EXECUTABLE_FORMAT_ELF;
+    } else if (pe_bits(kernel) != -1) {
+        return EXECUTABLE_FORMAT_PE;
+    } else {
+        panic(true, "limine: Unknown kernel executable format");
+    }
+}
+
 #define SUPPORTED_BASE_REVISION 3
 
 #define MAX_REQUESTS 128
@@ -142,7 +158,7 @@ static void limine_memcpy_to_64(uint64_t dst, void *src, size_t count) {
 #endif
 
 static pagemap_t build_pagemap(int base_revision,
-                               bool nx, struct elf_range *ranges, size_t ranges_count,
+                               bool nx, struct mem_range *ranges, size_t ranges_count,
                                uint64_t physical_base, uint64_t virtual_base,
                                uint64_t direct_map_offset) {
     pagemap_t pagemap = new_pagemap(paging_mode);
@@ -162,8 +178,8 @@ static pagemap_t build_pagemap(int base_revision,
         }
 
         uint64_t pf =
-            (ranges[i].permissions & ELF_PF_X ? 0 : (nx ? VMM_FLAG_NOEXEC : 0)) |
-            (ranges[i].permissions & ELF_PF_W ? VMM_FLAG_WRITE : 0);
+            (ranges[i].permissions & MEM_RANGE_X ? 0 : (nx ? VMM_FLAG_NOEXEC : 0)) |
+            (ranges[i].permissions & MEM_RANGE_W ? VMM_FLAG_WRITE : 0);
 
         map_pages(pagemap, virt, phys, pf, ranges[i].length);
     }
@@ -448,19 +464,34 @@ noreturn void limine_load(char *config, char *cmdline) {
 
     // ELF loading
     uint64_t entry_point = 0;
-    struct elf_range *ranges;
+    struct mem_range *ranges;
     uint64_t ranges_count;
 
     uint64_t image_size_before_bss;
     bool is_reloc;
 
-    if (!elf64_load(kernel, &entry_point, &slide,
-                   MEMMAP_KERNEL_AND_MODULES, kaslr,
-                   &ranges, &ranges_count,
-                   &physical_base, &virtual_base, NULL,
-                   &image_size_before_bss,
-                   &is_reloc)) {
-        panic(true, "limine: ELF64 load failure");
+    enum executable_format kernel_format = detect_kernel_format(kernel);
+    switch (kernel_format) {
+        case EXECUTABLE_FORMAT_ELF:
+            if (!elf64_load(kernel, &entry_point, &slide,
+                            MEMMAP_KERNEL_AND_MODULES, kaslr,
+                            &ranges, &ranges_count,
+                            &physical_base, &virtual_base, NULL,
+                            &image_size_before_bss,
+                            &is_reloc)) {
+                panic(true, "limine: ELF64 load failure");
+            }
+            break;
+        case EXECUTABLE_FORMAT_PE:
+            if (!pe64_load(kernel, &entry_point, &slide,
+                            MEMMAP_KERNEL_AND_MODULES, kaslr,
+                            &ranges, &ranges_count,
+                            &physical_base, &virtual_base, NULL,
+                            &image_size_before_bss,
+                            &is_reloc)) {
+                panic(true, "limine: PE64 load failure");
+            }
+            break;
     }
 
     kaslr = kaslr && is_reloc;
@@ -515,7 +546,7 @@ noreturn void limine_load(char *config, char *cmdline) {
     uint64_t *limine_reqs = NULL;
     requests = ext_mem_alloc(MAX_REQUESTS * sizeof(void *));
     requests_count = 0;
-    if (base_revision == 0 && elf64_load_section(kernel, &limine_reqs, ".limine_reqs", 0, slide)) {
+    if (base_revision == 0 && kernel_format == EXECUTABLE_FORMAT_ELF && elf64_load_section(kernel, &limine_reqs, ".limine_reqs", 0, slide)) {
         for (size_t i = 0; ; i++) {
             if (limine_reqs[i] == 0) {
                 break;
tab: 248 wrap: offon