mirror of https://git.suyu.dev/suyu/suyu
				
				
				
			
			You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			102 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			C++
		
	
			
		
		
	
	
			102 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			C++
		
	
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
 | 
						|
// SPDX-License-Identifier: GPL-2.0-or-later
 | 
						|
 | 
						|
#pragma once
 | 
						|
 | 
						|
#include <span>
 | 
						|
#include <unordered_map>
 | 
						|
#include <vector>
 | 
						|
 | 
						|
#pragma GCC diagnostic push
 | 
						|
#pragma GCC diagnostic ignored "-Wshorten-64-to-32"
 | 
						|
#include <oaknut/code_block.hpp>
 | 
						|
#include <oaknut/oaknut.hpp>
 | 
						|
#pragma GCC diagnostic pop
 | 
						|
 | 
						|
#include "common/common_types.h"
 | 
						|
#include "core/hle/kernel/code_set.h"
 | 
						|
#include "core/hle/kernel/k_typed_address.h"
 | 
						|
#include "core/hle/kernel/physical_memory.h"
 | 
						|
 | 
						|
namespace Core::NCE {
 | 
						|
 | 
						|
enum class PatchMode : u32 {
 | 
						|
    None,
 | 
						|
    PreText,  ///< Patch section is inserted before .text
 | 
						|
    PostData, ///< Patch section is inserted after .data
 | 
						|
};
 | 
						|
 | 
						|
using ModuleTextAddress = u64;
 | 
						|
using PatchTextAddress = u64;
 | 
						|
using EntryTrampolines = std::unordered_map<ModuleTextAddress, PatchTextAddress>;
 | 
						|
 | 
						|
class Patcher {
 | 
						|
public:
 | 
						|
    explicit Patcher();
 | 
						|
    ~Patcher();
 | 
						|
 | 
						|
    void PatchText(const Kernel::PhysicalMemory& program_image,
 | 
						|
                   const Kernel::CodeSet::Segment& code);
 | 
						|
    void RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code,
 | 
						|
                         Kernel::PhysicalMemory& program_image, EntryTrampolines* out_trampolines);
 | 
						|
    size_t GetSectionSize() const noexcept;
 | 
						|
 | 
						|
    [[nodiscard]] PatchMode GetPatchMode() const noexcept {
 | 
						|
        return mode;
 | 
						|
    }
 | 
						|
 | 
						|
private:
 | 
						|
    using ModuleDestLabel = uintptr_t;
 | 
						|
 | 
						|
    struct Trampoline {
 | 
						|
        ptrdiff_t patch_offset;
 | 
						|
        uintptr_t module_offset;
 | 
						|
    };
 | 
						|
 | 
						|
    void WriteLoadContext();
 | 
						|
    void WriteSaveContext();
 | 
						|
    void LockContext();
 | 
						|
    void UnlockContext();
 | 
						|
    void WriteSvcTrampoline(ModuleDestLabel module_dest, u32 svc_id);
 | 
						|
    void WriteMrsHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg,
 | 
						|
                         oaknut::SystemReg src_reg);
 | 
						|
    void WriteMsrHandler(ModuleDestLabel module_dest, oaknut::XReg src_reg);
 | 
						|
    void WriteCntpctHandler(ModuleDestLabel module_dest, oaknut::XReg dest_reg);
 | 
						|
 | 
						|
private:
 | 
						|
    void BranchToPatch(uintptr_t module_dest) {
 | 
						|
        m_branch_to_patch_relocations.push_back({c.offset(), module_dest});
 | 
						|
    }
 | 
						|
 | 
						|
    void BranchToModule(uintptr_t module_dest) {
 | 
						|
        m_branch_to_module_relocations.push_back({c.offset(), module_dest});
 | 
						|
        c.dw(0);
 | 
						|
    }
 | 
						|
 | 
						|
    void WriteModulePc(uintptr_t module_dest) {
 | 
						|
        m_write_module_pc_relocations.push_back({c.offset(), module_dest});
 | 
						|
        c.dx(0);
 | 
						|
    }
 | 
						|
 | 
						|
private:
 | 
						|
    // List of patch instructions we have generated.
 | 
						|
    std::vector<u32> m_patch_instructions{};
 | 
						|
 | 
						|
    // Relocation type for relative branch from module to patch.
 | 
						|
    struct Relocation {
 | 
						|
        ptrdiff_t patch_offset;  ///< Offset in bytes from the start of the patch section.
 | 
						|
        uintptr_t module_offset; ///< Offset in bytes from the start of the text section.
 | 
						|
    };
 | 
						|
 | 
						|
    oaknut::VectorCodeGenerator c;
 | 
						|
    std::vector<Trampoline> m_trampolines;
 | 
						|
    std::vector<Relocation> m_branch_to_patch_relocations{};
 | 
						|
    std::vector<Relocation> m_branch_to_module_relocations{};
 | 
						|
    std::vector<Relocation> m_write_module_pc_relocations{};
 | 
						|
    oaknut::Label m_save_context{};
 | 
						|
    oaknut::Label m_load_context{};
 | 
						|
    PatchMode mode{PatchMode::None};
 | 
						|
};
 | 
						|
 | 
						|
} // namespace Core::NCE
 |