/*
    * ElfLoader.cpp
    * ELF64 binary loader for user-mode processes
    * Copyright (c) 2025 Daniel Hammer
*/

#include "ElfLoader.hpp"
#include <Fs/Vfs.hpp>
#include <Memory/Heap.hpp>
#include <Memory/PageFrameAllocator.hpp>
#include <Memory/Paging.hpp>
#include <Memory/HHDM.hpp>
#include <Libraries/Memory.hpp>
#include <Terminal/Terminal.hpp>
#include <CppLib/Stream.hpp>

namespace Sched {

    struct Elf64Dynamic {
        int64_t d_tag;
        union {
            uint64_t d_val;
            uint64_t d_ptr;
        };
    };

    struct Elf64Rela {
        uint64_t r_offset;
        uint64_t r_info;
        int64_t r_addend;
    };

    struct Elf64Sym {
        uint32_t st_name;
        uint8_t  st_info;
        uint8_t  st_other;
        uint16_t st_shndx;
        uint64_t st_value;
        uint64_t st_size;
    };

    struct DynamicInfo {
        uint64_t hashVaddr = 0;
        uint64_t symtabVaddr = 0;
        uint64_t strtabVaddr = 0;
        uint64_t strtabSize = 0;
        uint64_t relaVaddr = 0;
        uint64_t relaSize = 0;
        uint64_t relaEnt = sizeof(Elf64Rela);
        uint64_t jmprelVaddr = 0;
        uint64_t pltrelSize = 0;
        uint64_t pltRelType = 0;
        uint64_t symEnt = sizeof(Elf64Sym);
    };

    static constexpr uint64_t DT_NULL     = 0;
    static constexpr uint64_t DT_PLTRELSZ = 2;
    static constexpr uint64_t DT_HASH     = 4;
    static constexpr uint64_t DT_STRTAB   = 5;
    static constexpr uint64_t DT_SYMTAB   = 6;
    static constexpr uint64_t DT_RELA     = 7;
    static constexpr uint64_t DT_RELASZ   = 8;
    static constexpr uint64_t DT_RELAENT  = 9;
    static constexpr uint64_t DT_STRSZ    = 10;
    static constexpr uint64_t DT_SYMENT   = 11;
    static constexpr uint64_t DT_PLTREL   = 20;
    static constexpr uint64_t DT_JMPREL   = 23;

    static constexpr uint32_t R_X86_64_64        = 1;
    static constexpr uint32_t R_X86_64_GLOB_DAT  = 6;
    static constexpr uint32_t R_X86_64_JUMP_SLOT = 7;
    static constexpr uint32_t R_X86_64_RELATIVE  = 8;

    static constexpr uint16_t SHN_UNDEF = 0;
    static constexpr uint8_t STB_WEAK   = 2;

    static constexpr uint32_t Elf64RelocType(uint64_t info) {
        return static_cast<uint32_t>(info);
    }

    static constexpr uint32_t Elf64RelocSym(uint64_t info) {
        return static_cast<uint32_t>(info >> 32);
    }

    static uint64_t AddSigned(uint64_t base, int64_t addend) {
        if (addend >= 0) {
            return base + static_cast<uint64_t>(addend);
        }

        return base - static_cast<uint64_t>(-addend);
    }

    static bool ValidateProgramHeaderTable(const Elf64Header* hdr, uint64_t fileSize) {
        if (hdr->e_phentsize != sizeof(Elf64ProgramHeader)) {
            Kt::KernelLogStream(Kt::ERROR, "ELF") << "Unexpected program header size";
            return false;
        }

        if (hdr->e_phoff > fileSize ||
            hdr->e_phoff + uint64_t(hdr->e_phnum) * hdr->e_phentsize > fileSize) {
            Kt::KernelLogStream(Kt::ERROR, "ELF") << "Program headers outside file";
            return false;
        }

        return true;
    }

    static bool ValidateLoadSegment(const Elf64ProgramHeader* phdr, uint64_t fileSize) {
        if (phdr->p_filesz > phdr->p_memsz) {
            Kt::KernelLogStream(Kt::ERROR, "ELF") << "Segment file size exceeds memory size";
            return false;
        }

        if (phdr->p_offset > fileSize || phdr->p_offset + phdr->p_filesz > fileSize) {
            Kt::KernelLogStream(Kt::ERROR, "ELF") << "Segment outside file";
            return false;
        }

        return true;
    }

    static bool ValidateLibElfHeader(const Elf64Header* hdr) {
        if (hdr->e_ident[0] != 0x7f ||
            hdr->e_ident[1] != 'E'  ||
            hdr->e_ident[2] != 'L'  ||
            hdr->e_ident[3] != 'F') {
            Kt::KernelLogStream(Kt::ERROR, "ELF") << "Invalid ELF magic";
            return false;
        }

        if (hdr->e_ident[4] != 2) {
            Kt::KernelLogStream(Kt::ERROR, "ELF") << "Not a 64-bit ELF";
            return false;
        }

        if (hdr->e_ident[5] != 1) {
            Kt::KernelLogStream(Kt::ERROR, "ELF") << "Not little-endian";
            return false;
        }

        if (hdr->e_type != ET_DYN) {
            Kt::KernelLogStream(Kt::ERROR, "ELF") << "Library is not ET_DYN (type=" << (uint64_t)hdr->e_type << ")";
            return false;
        }

        if (hdr->e_machine != EM_X86_64) {
            Kt::KernelLogStream(Kt::ERROR, "ELF") << "Not x86_64 (machine=" << (uint64_t)hdr->e_machine << ")";
            return false;
        }

        return true;
    }

    static bool VirtToFileOffset(const Elf64Header* hdr, uint64_t fileSize,
                                 uint64_t virtualAddress, uint64_t size, uint64_t* outOffset) {
        for (uint16_t i = 0; i < hdr->e_phnum; i++) {
            auto* phdr = (const Elf64ProgramHeader*)((const uint8_t*)hdr + hdr->e_phoff + i * hdr->e_phentsize);

            if (phdr->p_type != PT_LOAD || phdr->p_filesz == 0) {
                continue;
            }

            if (!ValidateLoadSegment(phdr, fileSize)) {
                return false;
            }

            if (virtualAddress < phdr->p_vaddr) {
                continue;
            }

            if (virtualAddress + size > phdr->p_vaddr + phdr->p_filesz) {
                continue;
            }

            *outOffset = phdr->p_offset + (virtualAddress - phdr->p_vaddr);
            return true;
        }

        Kt::KernelLogStream(Kt::ERROR, "ELF") << "Failed to translate virtual address " << kcp::hex
                                              << virtualAddress << kcp::dec << " to file offset";
        return false;
    }

    static bool WriteUser64(uint64_t pml4Phys, uint64_t virtualAddress, uint64_t value) {
        uint64_t physBase = Memory::VMM::Paging::GetPhysAddr(pml4Phys, virtualAddress);
        if (physBase == 0) {
            Kt::KernelLogStream(Kt::ERROR, "ELF") << "Relocation target not mapped";
            return false;
        }

        uint64_t physAddress = physBase + (virtualAddress & 0xFFFULL);
        *(uint64_t*)Memory::HHDM(physAddress) = value;
        return true;
    }

    static bool ApplyRelaBlock(uint64_t pml4Phys, uint64_t loadBias,
                               const Elf64Rela* relas, uint64_t relaCount,
                               const Elf64Sym* dynsym, uint64_t dynsymCount,
                               const char* dynstr, uint64_t dynstrSize) {
        for (uint64_t i = 0; i < relaCount; i++) {
            const Elf64Rela& rela = relas[i];
            uint32_t type = Elf64RelocType(rela.r_info);
            uint32_t symIndex = Elf64RelocSym(rela.r_info);
            uint64_t targetVA = loadBias + rela.r_offset;
            uint64_t value = 0;

            switch (type) {
                case 0:
                    continue;

                case R_X86_64_RELATIVE:
                    value = AddSigned(loadBias, rela.r_addend);
                    break;

                case R_X86_64_GLOB_DAT:
                case R_X86_64_JUMP_SLOT:
                case R_X86_64_64: {
                    if (symIndex >= dynsymCount) {
                        Kt::KernelLogStream(Kt::ERROR, "ELF") << "Relocation symbol index out of range";
                        return false;
                    }

                    const Elf64Sym& sym = dynsym[symIndex];
                    if (sym.st_shndx == SHN_UNDEF) {
                        uint8_t bind = sym.st_info >> 4;
                        if (bind == STB_WEAK) {
                            value = 0;
                            break;
                        }

                        const char* name = "<unnamed>";
                        if (dynstr != nullptr && sym.st_name < dynstrSize) {
                            name = dynstr + sym.st_name;
                        }
                        Kt::KernelLogStream(Kt::ERROR, "ELF") << "Unresolved dynamic symbol: " << name;
                        return false;
                    }

                    value = AddSigned(loadBias + sym.st_value, rela.r_addend);
                    break;
                }

                default:
                    Kt::KernelLogStream(Kt::ERROR, "ELF") << "Unsupported relocation type=" << (uint64_t)type;
                    return false;
            }

            if (!WriteUser64(pml4Phys, targetVA, value)) {
                return false;
            }
        }

        return true;
    }

    static bool ApplyDynamicRelocations(const uint8_t* fileData, uint64_t fileSize,
                                        const Elf64Header* hdr, uint64_t pml4Phys,
                                        uint64_t loadBias) {
        const Elf64ProgramHeader* dynamicPhdr = nullptr;

        for (uint16_t i = 0; i < hdr->e_phnum; i++) {
            auto* phdr = (const Elf64ProgramHeader*)(fileData + hdr->e_phoff + i * hdr->e_phentsize);
            if (phdr->p_type == PT_DYNAMIC) {
                dynamicPhdr = phdr;
                break;
            }
        }

        if (dynamicPhdr == nullptr) {
            return true;
        }

        if (dynamicPhdr->p_offset > fileSize || dynamicPhdr->p_offset + dynamicPhdr->p_filesz > fileSize) {
            Kt::KernelLogStream(Kt::ERROR, "ELF") << "Dynamic section outside file";
            return false;
        }

        DynamicInfo info;
        uint64_t dynCount = dynamicPhdr->p_filesz / sizeof(Elf64Dynamic);
        auto* dyn = (const Elf64Dynamic*)(fileData + dynamicPhdr->p_offset);

        for (uint64_t i = 0; i < dynCount; i++) {
            if (dyn[i].d_tag == DT_NULL) {
                break;
            }

            switch (dyn[i].d_tag) {
                case DT_HASH:     info.hashVaddr = dyn[i].d_ptr; break;
                case DT_SYMTAB:   info.symtabVaddr = dyn[i].d_ptr; break;
                case DT_STRTAB:   info.strtabVaddr = dyn[i].d_ptr; break;
                case DT_STRSZ:    info.strtabSize = dyn[i].d_val; break;
                case DT_RELA:     info.relaVaddr = dyn[i].d_ptr; break;
                case DT_RELASZ:   info.relaSize = dyn[i].d_val; break;
                case DT_RELAENT:  info.relaEnt = dyn[i].d_val; break;
                case DT_JMPREL:   info.jmprelVaddr = dyn[i].d_ptr; break;
                case DT_PLTRELSZ: info.pltrelSize = dyn[i].d_val; break;
                case DT_PLTREL:   info.pltRelType = dyn[i].d_val; break;
                case DT_SYMENT:   info.symEnt = dyn[i].d_val; break;
                default:
                    break;
            }
        }

        if (info.relaSize == 0 && info.pltrelSize == 0) {
            return true;
        }

        if (info.symEnt != sizeof(Elf64Sym)) {
            Kt::KernelLogStream(Kt::ERROR, "ELF") << "Unexpected dynamic symbol size";
            return false;
        }

        if (info.relaEnt != sizeof(Elf64Rela)) {
            Kt::KernelLogStream(Kt::ERROR, "ELF") << "Unexpected RELA entry size";
            return false;
        }

        if (info.symtabVaddr == 0 || info.hashVaddr == 0) {
            Kt::KernelLogStream(Kt::ERROR, "ELF") << "Missing dynamic symbol metadata";
            return false;
        }

        uint64_t hashOffset = 0;
        if (!VirtToFileOffset(hdr, fileSize, info.hashVaddr, sizeof(uint32_t) * 2, &hashOffset)) {
            return false;
        }

        auto* hashWords = (const uint32_t*)(fileData + hashOffset);
        uint64_t dynsymCount = hashWords[1];

        uint64_t dynsymOffset = 0;
        if (!VirtToFileOffset(hdr, fileSize, info.symtabVaddr,
                              dynsymCount * sizeof(Elf64Sym), &dynsymOffset)) {
            return false;
        }

        auto* dynsym = (const Elf64Sym*)(fileData + dynsymOffset);
        const char* dynstr = nullptr;
        if (info.strtabVaddr != 0 && info.strtabSize != 0) {
            uint64_t dynstrOffset = 0;
            if (!VirtToFileOffset(hdr, fileSize, info.strtabVaddr, info.strtabSize, &dynstrOffset)) {
                return false;
            }
            dynstr = (const char*)(fileData + dynstrOffset);
        }

        if (info.relaSize != 0) {
            uint64_t relaOffset = 0;
            if (!VirtToFileOffset(hdr, fileSize, info.relaVaddr, info.relaSize, &relaOffset)) {
                return false;
            }

            auto* relas = (const Elf64Rela*)(fileData + relaOffset);
            if (!ApplyRelaBlock(pml4Phys, loadBias, relas, info.relaSize / sizeof(Elf64Rela),
                                dynsym, dynsymCount, dynstr, info.strtabSize)) {
                return false;
            }
        }

        if (info.pltrelSize != 0) {
            if (info.pltRelType != DT_RELA) {
                Kt::KernelLogStream(Kt::ERROR, "ELF") << "Unsupported PLT relocation format";
                return false;
            }

            uint64_t jmprelOffset = 0;
            if (!VirtToFileOffset(hdr, fileSize, info.jmprelVaddr, info.pltrelSize, &jmprelOffset)) {
                return false;
            }

            auto* pltRelas = (const Elf64Rela*)(fileData + jmprelOffset);
            if (!ApplyRelaBlock(pml4Phys, loadBias, pltRelas, info.pltrelSize / sizeof(Elf64Rela),
                                dynsym, dynsymCount, dynstr, info.strtabSize)) {
                return false;
            }
        }

        return true;
    }

    static bool ValidateElfHeader(const Elf64Header* hdr) {
        // Check ELF magic: 0x7f 'E' 'L' 'F'
        if (hdr->e_ident[0] != 0x7f ||
            hdr->e_ident[1] != 'E'  ||
            hdr->e_ident[2] != 'L'  ||
            hdr->e_ident[3] != 'F') {
            Kt::KernelLogStream(Kt::ERROR, "ELF") << "Invalid ELF magic";
            return false;
        }

        // Class must be ELFCLASS64 (2)
        if (hdr->e_ident[4] != 2) {
            Kt::KernelLogStream(Kt::ERROR, "ELF") << "Not a 64-bit ELF";
            return false;
        }

        // Data encoding must be ELFDATA2LSB (1) - little endian
        if (hdr->e_ident[5] != 1) {
            Kt::KernelLogStream(Kt::ERROR, "ELF") << "Not little-endian";
            return false;
        }

        if (hdr->e_type != ET_EXEC) {
            Kt::KernelLogStream(Kt::ERROR, "ELF") << "Not an executable (type=" << (uint64_t)hdr->e_type << ")";
            return false;
        }

        if (hdr->e_machine != EM_X86_64) {
            Kt::KernelLogStream(Kt::ERROR, "ELF") << "Not x86_64 (machine=" << (uint64_t)hdr->e_machine << ")";
            return false;
        }

        return true;
    }

    uint64_t ElfLoad(const char* vfsPath, uint64_t pml4Phys) {
        Fs::Vfs::BackendFile file = {-1, -1};
        if (Fs::Vfs::OpenBackendFile(vfsPath, file) < 0) {
            return 0;
        }

        uint64_t fileSize = Fs::Vfs::GetBackendFileSize(file);
        if (fileSize < sizeof(Elf64Header)) {
            Kt::KernelLogStream(Kt::ERROR, "ELF") << "File too small (" << fileSize << " bytes)";
            Fs::Vfs::CloseBackendFile(file);
            return 0;
        }

        // Read entire file into a heap buffer
        uint8_t* fileData = (uint8_t*)Memory::g_heap->Request(fileSize);
        if (fileData == nullptr) {
            Kt::KernelLogStream(Kt::ERROR, "ELF") << "Failed to allocate " << fileSize << " bytes for file";
            Fs::Vfs::CloseBackendFile(file);
            return 0;
        }

        Fs::Vfs::ReadBackendFile(file, fileData, 0, fileSize);
        Fs::Vfs::CloseBackendFile(file);

        // Prevent the optimizer from reordering the VfsRead store past the
        // header validation reads that follow.
        asm volatile("" ::: "memory");

        // Validate ELF header
        Elf64Header* hdr = (Elf64Header*)fileData;
        if (!ValidateElfHeader(hdr)) {
            Memory::g_heap->Free(fileData);
            return 0;
        }

        // Process program headers
        for (uint16_t i = 0; i < hdr->e_phnum; i++) {
            Elf64ProgramHeader* phdr = (Elf64ProgramHeader*)(fileData + hdr->e_phoff + i * hdr->e_phentsize);

            if (phdr->p_type != PT_LOAD) {
                continue;
            }

            if (phdr->p_memsz == 0) {
                continue;
            }

            // Allocate pages and map them in the process PML4 with User bit
            uint64_t segBase = phdr->p_vaddr & ~0xFFFULL;
            uint64_t segEnd = (phdr->p_vaddr + phdr->p_memsz + 0xFFF) & ~0xFFFULL;
            uint64_t numPages = (segEnd - segBase) / 0x1000;

            for (uint64_t p = 0; p < numPages; p++) {
                void* page = Memory::g_pfa->AllocateZeroed();
                if (page == nullptr) {
                    Kt::KernelLogStream(Kt::ERROR, "ELF") << "Out of physical pages";
                    Memory::g_heap->Free(fileData);
                    return 0;
                }

                uint64_t physAddr = Memory::SubHHDM((uint64_t)page);
                uint64_t virtAddr = segBase + p * 0x1000;

                // Map into the process's PML4 with User bit set
                if (!Memory::VMM::Paging::MapUserIn(pml4Phys, physAddr, virtAddr)) {
                    Kt::KernelLogStream(Kt::ERROR, "ELF") << "Failed to map page";
                    Memory::g_heap->Free(fileData);
                    return 0;
                }

                // Copy file data that overlaps this page (via HHDM)
                uint64_t pageStart = virtAddr;
                uint64_t pageEnd = virtAddr + 0x1000;

                uint64_t segFileStart = phdr->p_vaddr;
                uint64_t segFileEnd = phdr->p_vaddr + phdr->p_filesz;

                uint64_t copyStart = (pageStart > segFileStart) ? pageStart : segFileStart;
                uint64_t copyEnd = (pageEnd < segFileEnd) ? pageEnd : segFileEnd;

                if (copyStart < copyEnd) {
                    uint64_t dstOffset = copyStart - pageStart;
                    uint64_t srcOffset = copyStart - phdr->p_vaddr + phdr->p_offset;
                    uint64_t copySize = copyEnd - copyStart;

                    uint8_t* dst = (uint8_t*)Memory::HHDM(physAddr) + dstOffset;
                    uint8_t* src = fileData + srcOffset;
                    memcpy(dst, src, copySize);
                }
            }
        }

        uint64_t entryPoint = hdr->e_entry;
        Memory::g_heap->Free(fileData);

        return entryPoint;
    }

    uint64_t ElfLoadLib(const char* vfsPath, uint64_t pml4Phys, int slot) {
        Fs::Vfs::BackendFile file = {-1, -1};
        if (Fs::Vfs::OpenBackendFile(vfsPath, file) < 0) {
            return 0;
        }

        uint64_t fileSize = Fs::Vfs::GetBackendFileSize(file);
        if (fileSize < sizeof(Elf64Header)) {
            Kt::KernelLogStream(Kt::ERROR, "ELF") << "File too small (" << fileSize << " bytes)";
            Fs::Vfs::CloseBackendFile(file);
            return 0;
        }

        // Read entire file into a heap buffer
        uint8_t* fileData = (uint8_t*)Memory::g_heap->Request(fileSize);
        if (fileData == nullptr) {
            Kt::KernelLogStream(Kt::ERROR, "ELF") << "Failed to allocate " << fileSize << " bytes for file";
            Fs::Vfs::CloseBackendFile(file);
            return 0;
        }

        Fs::Vfs::ReadBackendFile(file, fileData, 0, fileSize);
        Fs::Vfs::CloseBackendFile(file);

        asm volatile("" ::: "memory");

        Elf64Header* hdr = (Elf64Header*)fileData;

        if (!ValidateLibElfHeader(hdr) || !ValidateProgramHeaderTable(hdr, fileSize)) {
            Memory::g_heap->Free(fileData);
            return 0;
        }

        uint64_t slotBase = LIB_BASE + (uint64_t(slot) * LIB_MAX_SIZE);
        uint64_t minLoadStart = ~0ULL;
        uint64_t maxLoadEnd = 0;
        bool hasLoadSegment = false;

        for (uint16_t i = 0; i < hdr->e_phnum; i++) {
            auto* phdr = (Elf64ProgramHeader*)(fileData + hdr->e_phoff + i * hdr->e_phentsize);

            if (phdr->p_type != PT_LOAD || phdr->p_memsz == 0) {
                continue;
            }

            if (!ValidateLoadSegment(phdr, fileSize)) {
                Memory::g_heap->Free(fileData);
                return 0;
            }

            uint64_t segStart = phdr->p_vaddr & ~0xFFFULL;
            uint64_t segEnd = (phdr->p_vaddr + phdr->p_memsz + 0xFFFULL) & ~0xFFFULL;
            if (segStart < minLoadStart) minLoadStart = segStart;
            if (segEnd > maxLoadEnd) maxLoadEnd = segEnd;
            hasLoadSegment = true;
        }

        if (!hasLoadSegment) {
            Kt::KernelLogStream(Kt::ERROR, "ELF") << "Library has no loadable segments";
            Memory::g_heap->Free(fileData);
            return 0;
        }

        if (maxLoadEnd - minLoadStart > LIB_MAX_SIZE) {
            Kt::KernelLogStream(Kt::ERROR, "ELF") << "Library image exceeds slot size";
            Memory::g_heap->Free(fileData);
            return 0;
        }

        uint64_t loadBias = slotBase - minLoadStart;

        // Process program headers
        for (uint16_t i = 0; i < hdr->e_phnum; i++) {
            Elf64ProgramHeader* phdr = (Elf64ProgramHeader*)(fileData + hdr->e_phoff + i * hdr->e_phentsize);

            if (phdr->p_type != PT_LOAD) {
                continue;
            }

            if (phdr->p_memsz == 0) {
                continue;
            }

            uint64_t targetSegStart = loadBias + phdr->p_vaddr;
            uint64_t targetSegFileEnd = targetSegStart + phdr->p_filesz;  // only file data
            uint64_t targetSegMemEnd = targetSegStart + phdr->p_memsz;    // includes bss

            // Align segment start down, segment end up to page boundaries
            uint64_t pageSegStart = targetSegStart & ~0xFFFULL;
            uint64_t pageSegEnd = (targetSegMemEnd + 0xFFFULL) & ~0xFFFULL;
            uint64_t numPages = (pageSegEnd - pageSegStart) / 0x1000;

            for (uint64_t p = 0; p < numPages; p++) {
                void* page = Memory::g_pfa->AllocateZeroed();
                if (page == nullptr) {
                    Kt::KernelLogStream(Kt::ERROR, "ELF") << "Out of physical pages";
                    Memory::g_heap->Free(fileData);
                    return 0;
                }

                uint64_t physAddr = Memory::SubHHDM((uint64_t)page);
                uint64_t pageStart = pageSegStart + p * 0x1000;
                uint64_t pageEnd = pageStart + 0x1000;

                // Check that we're within the library region
                if (pageStart < slotBase || pageEnd > slotBase + LIB_MAX_SIZE) {
                    Kt::KernelLogStream(Kt::ERROR, "ELF") << "Segment outside library region";
                    Memory::g_heap->Free(fileData);
                    return 0;
                }

                if (!Memory::VMM::Paging::MapUserIn(pml4Phys, physAddr, pageStart)) {
                    Kt::KernelLogStream(Kt::ERROR, "ELF") << "Failed to map page";
                    Memory::g_heap->Free(fileData);
                    return 0;
                }

                // Calculate overlap between this page and the file-data portion of the segment
                uint64_t copyStart = (pageStart > targetSegStart) ? pageStart : targetSegStart;
                uint64_t copyEnd = (pageEnd < targetSegFileEnd) ? pageEnd : targetSegFileEnd;

                if (copyStart < copyEnd) {
                    // Source stays segment-relative, but the destination must be page-relative.
                    // Using a segment-relative destination offset leaves page 2+ zero-filled and
                    // writes past the newly allocated page in the kernel mapping.
                    uint64_t srcOffset = phdr->p_offset + (copyStart - targetSegStart);
                    uint64_t pageDataOffset = copyStart - pageStart;
                    uint64_t copySize = copyEnd - copyStart;

                    uint8_t* dst = (uint8_t*)Memory::HHDM(physAddr) + pageDataOffset;
                    uint8_t* src = fileData + srcOffset;
                    memcpy(dst, src, copySize);
                }
            }
        }

        if (!ApplyDynamicRelocations(fileData, fileSize, hdr, pml4Phys, loadBias)) {
            Memory::g_heap->Free(fileData);
            return 0;
        }

        Memory::g_heap->Free(fileData);

        return loadBias;
    }

}
