|
|
|
@ -16,6 +16,24 @@ |
|
|
|
#include "core/hle/kernel/physical_memory.h" |
|
|
|
#include "lru_cache.h" |
|
|
|
#include <utility> |
|
|
|
using ModuleID = std::array<u8, 32>; // NSO build ID |
|
|
|
struct PatchCacheKey { |
|
|
|
ModuleID module_id; |
|
|
|
uintptr_t offset; |
|
|
|
bool operator==(const PatchCacheKey&) const = default; |
|
|
|
}; |
|
|
|
|
|
|
|
template <> |
|
|
|
struct std::hash<PatchCacheKey> { |
|
|
|
size_t operator()(const PatchCacheKey& key) const { |
|
|
|
// Simple XOR hash of first few bytes |
|
|
|
size_t hash = 0; |
|
|
|
for (size_t i = 0; i < key.module_id.size(); ++i) { |
|
|
|
hash ^= static_cast<size_t>(key.module_id[i]) << ((i % sizeof(size_t)) * 8); |
|
|
|
} |
|
|
|
return hash ^ std::hash<uintptr_t>{}(key.offset); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
namespace Core::NCE { |
|
|
|
|
|
|
|
@ -31,13 +49,15 @@ using EntryTrampolines = std::unordered_map<ModuleTextAddress, PatchTextAddress> |
|
|
|
|
|
|
|
class Patcher { |
|
|
|
public: |
|
|
|
void SetModuleID(const ModuleID& id) { |
|
|
|
module_id = id; |
|
|
|
} |
|
|
|
Patcher(const Patcher&) = delete; |
|
|
|
Patcher& operator=(const Patcher&) = delete; |
|
|
|
Patcher(Patcher&& other) noexcept; |
|
|
|
Patcher& operator=(Patcher&&) noexcept = delete; |
|
|
|
explicit Patcher(); |
|
|
|
~Patcher(); |
|
|
|
|
|
|
|
bool PatchText(const Kernel::PhysicalMemory& program_image, |
|
|
|
const Kernel::CodeSet::Segment& code); |
|
|
|
bool RelocateAndCopy(Common::ProcessAddress load_base, const Kernel::CodeSet::Segment& code, |
|
|
|
@ -50,7 +70,7 @@ public: |
|
|
|
|
|
|
|
private: |
|
|
|
using ModuleDestLabel = uintptr_t; |
|
|
|
|
|
|
|
ModuleID module_id{}; |
|
|
|
struct Trampoline { |
|
|
|
ptrdiff_t patch_offset; |
|
|
|
uintptr_t module_offset; |
|
|
|
@ -68,26 +88,25 @@ private: |
|
|
|
|
|
|
|
private: |
|
|
|
static constexpr size_t CACHE_SIZE = 16384; // Cache size for patch entries |
|
|
|
LRUCache<uintptr_t, PatchTextAddress> patch_cache{CACHE_SIZE, Settings::values.lru_cache_enabled.GetValue()}; |
|
|
|
LRUCache<PatchCacheKey, PatchTextAddress> patch_cache{CACHE_SIZE, Settings::values.lru_cache_enabled.GetValue()}; |
|
|
|
|
|
|
|
void BranchToPatch(uintptr_t module_dest) { |
|
|
|
if (patch_cache.isEnabled()) { |
|
|
|
LOG_DEBUG(Core_ARM, "LRU cache lookup for address {:#x}", module_dest); |
|
|
|
PatchCacheKey key{module_id, module_dest}; |
|
|
|
LOG_DEBUG(Core_ARM, "LRU cache lookup for module={}, offset={:#x}", fmt::ptr(module_id.data()), module_dest); |
|
|
|
// Try to get existing patch entry from cache |
|
|
|
if (auto* cached_patch = patch_cache.get(module_dest)) { |
|
|
|
LOG_WARNING(Core_ARM, "LRU cache hit for address {:#x}", module_dest); |
|
|
|
if (auto* cached_patch = patch_cache.get(key)) { |
|
|
|
LOG_WARNING(Core_ARM, "LRU cache hit for module offset {:#x}", module_dest); |
|
|
|
curr_patch->m_branch_to_patch_relocations.push_back({c.offset(), *cached_patch}); |
|
|
|
return; |
|
|
|
} |
|
|
|
LOG_DEBUG(Core_ARM, "LRU cache miss for address {:#x}, creating new patch", module_dest); |
|
|
|
|
|
|
|
// If not in cache, create new entry and cache it |
|
|
|
LOG_DEBUG(Core_ARM, "LRU cache miss for module offset {:#x}, creating new patch", module_dest); |
|
|
|
// Not in cache: create and store |
|
|
|
const auto patch_addr = c.offset(); |
|
|
|
curr_patch->m_branch_to_patch_relocations.push_back({patch_addr, module_dest}); |
|
|
|
patch_cache.put(module_dest, patch_addr); |
|
|
|
patch_cache.put(key, patch_addr); |
|
|
|
} else { |
|
|
|
LOG_DEBUG(Core_ARM, "LRU cache disabled - creating direct patch for address {:#x}", module_dest); |
|
|
|
// LRU disabled - use pre-LRU approach |
|
|
|
LOG_DEBUG(Core_ARM, "LRU cache disabled - direct patch for offset {:#x}", module_dest); |
|
|
|
curr_patch->m_branch_to_patch_relocations.push_back({c.offset(), module_dest}); |
|
|
|
} |
|
|
|
} |
|
|
|
|