From c80342e8d431cfbf33c685088cfec4b3fd762116 Mon Sep 17 00:00:00 2001 From: kd-11 Date: Sat, 31 Aug 2024 05:56:54 +0300 Subject: [PATCH] aarch64: Support calloc patch blocks --- Utilities/bin_patch.cpp | 10 +++++--- Utilities/ppu_patch.h | 20 +++++++++++++++ rpcs3/Emu/CPU/Backends/AArch64/AArch64JIT.cpp | 25 ++++++++++++++++--- rpcs3/Emu/CPU/Backends/AArch64/AArch64JIT.h | 9 ++++--- rpcs3/Emu/Cell/PPUTranslator.cpp | 13 +++++++++- 5 files changed, 65 insertions(+), 12 deletions(-) create mode 100644 Utilities/ppu_patch.h diff --git a/Utilities/bin_patch.cpp b/Utilities/bin_patch.cpp index f990d423ab..a52eff0db7 100644 --- a/Utilities/bin_patch.cpp +++ b/Utilities/bin_patch.cpp @@ -1,7 +1,9 @@ #include "bin_patch.h" +#include "ppu_patch.h" #include "File.h" #include "Config.h" #include "version.h" +#include "Emu/IdManager.h" #include "Emu/Memory/vm.h" #include "Emu/System.h" #include "Emu/VFS.h" @@ -896,10 +898,6 @@ void patch_engine::append_title_patches(std::string_view title_id) load(m_map, fmt::format("%s%s_patch.yml", get_patches_path(), title_id)); } -void ppu_register_range(u32 addr, u32 size); -bool ppu_form_branch_to_code(u32 entry, u32 target, bool link = false, bool with_toc = false, std::string module_name = {}); -u32 ppu_generate_id(std::string_view name); - void unmap_vm_area(std::shared_ptr& ptr) { if (ptr && ptr->flags & (1ull << 62)) @@ -1216,6 +1214,10 @@ static usz apply_modification(std::basic_string& applied, patch_engine::pat continue; } + // Record the insertion point as a faux block. + g_fxo->need(); + g_fxo->get().block_addresses.insert(resval); + relocate_instructions_at = addr; break; } diff --git a/Utilities/ppu_patch.h b/Utilities/ppu_patch.h new file mode 100644 index 0000000000..667242dac5 --- /dev/null +++ b/Utilities/ppu_patch.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include +#include +#include + +// Patch utilities specific to PPU code +struct ppu_patch_block_registry_t +{ + ppu_patch_block_registry_t() = default; + ppu_patch_block_registry_t(const ppu_patch_block_registry_t&) = delete; + ppu_patch_block_registry_t& operator=(const ppu_patch_block_registry_t&) = delete; + + std::unordered_set block_addresses{}; +}; + +void ppu_register_range(u32 addr, u32 size); +bool ppu_form_branch_to_code(u32 entry, u32 target, bool link = false, bool with_toc = false, std::string module_name = {}); +u32 ppu_generate_id(std::string_view name); diff --git a/rpcs3/Emu/CPU/Backends/AArch64/AArch64JIT.cpp b/rpcs3/Emu/CPU/Backends/AArch64/AArch64JIT.cpp index 4efd1085e9..91aedffd85 100644 --- a/rpcs3/Emu/CPU/Backends/AArch64/AArch64JIT.cpp +++ b/rpcs3/Emu/CPU/Backends/AArch64/AArch64JIT.cpp @@ -1,6 +1,5 @@ #include "stdafx.h" - #include "AArch64JIT.h" #include "AArch64ASM.h" @@ -415,9 +414,9 @@ namespace aarch64 ensure(llvm::isa(where)); irb->SetInsertPoint(llvm::dyn_cast(where)); - if (instruction_info.callee_is_GHC && // Calls to C++ ABI will always return - !instruction_info.is_indirect && // We don't know enough when calling indirectly to know if we'll return or not - instruction_info.callee_name.find("-pp-") == umax) // Skip branch patch-points as those are just indirect calls. TODO: Move this to instruction decode. + if (instruction_info.callee_is_GHC && // Calls to C++ ABI will always return + !instruction_info.is_indirect && // We don't know enough when calling indirectly to know if we'll return or not + !is_faux_function(instruction_info.callee_name)) // Ignore branch patch-points and imposter functions. Their behavior is unreliable. { // We're making a one-way call. This branch shouldn't even bother linking as it will never return here. ASMBlock c; @@ -487,6 +486,24 @@ namespace aarch64 return false; } + bool GHC_frame_preservation_pass::is_faux_function(const std::string& function_name) + { + // Is it a branch patch-point? + if (function_name.find("-pp-") != umax) + { + return true; + } + + // Now we search the known imposters list + if (m_config.faux_function_list.empty()) + { + return false; + } + + const auto& x = m_config.faux_function_list; + return std::find(x.begin(), x.end(), function_name) != x.end(); + } + void GHC_frame_preservation_pass::process_leaf_function(llvm::IRBuilder<>* irb, llvm::Function& f) { for (auto &bb : f) diff --git a/rpcs3/Emu/CPU/Backends/AArch64/AArch64JIT.h b/rpcs3/Emu/CPU/Backends/AArch64/AArch64JIT.h index ed2a2b08e5..701fe85be9 100644 --- a/rpcs3/Emu/CPU/Backends/AArch64/AArch64JIT.h +++ b/rpcs3/Emu/CPU/Backends/AArch64/AArch64JIT.h @@ -41,12 +41,13 @@ namespace aarch64 struct config_t { - bool debug_info = false; // Record debug information - bool use_stack_frames = true; // Allocate a stack frame for each function. The gateway can alternatively manage a global stack to use as scratch. - bool optimize = true; // Optimize instructions when possible. Set to false when debugging. + bool debug_info = false; // Record debug information + bool use_stack_frames = true; // Allocate a stack frame for each function. The gateway can alternatively manage a global stack to use as scratch. + bool optimize = true; // Optimize instructions when possible. Set to false when debugging. u32 hypervisor_context_offset = 0; // Offset within the "thread" object where we can find the hypervisor context (registers configured at gateway). std::function exclusion_callback; // [Optional] Callback run on each function before transform. Return "true" to exclude from frame processing. std::vector> base_register_lookup; // [Optional] Function lookup table to determine the location of the "thread" context. + std::vector faux_function_list; // [Optional] List of faux block names to treat as untrusted - typically fake functions representing codecaves. }; protected: @@ -64,6 +65,8 @@ namespace aarch64 bool is_inlined_call(const llvm::CallInst* ci); + bool is_faux_function(const std::string& function_name); + gpr get_base_register_for_call(const std::string& callee_name, gpr default_reg = gpr::x19); void process_leaf_function(llvm::IRBuilder<>* irb, llvm::Function& f); diff --git a/rpcs3/Emu/Cell/PPUTranslator.cpp b/rpcs3/Emu/Cell/PPUTranslator.cpp index 1af6ed02c6..c2102af38f 100644 --- a/rpcs3/Emu/Cell/PPUTranslator.cpp +++ b/rpcs3/Emu/Cell/PPUTranslator.cpp @@ -18,6 +18,8 @@ #ifdef ARCH_ARM64 #include "Emu/CPU/Backends/AArch64/AArch64JIT.h" +#include "Emu/IdManager.h" +#include "Utilities/ppu_patch.h" #endif using namespace llvm; @@ -48,13 +50,22 @@ PPUTranslator::PPUTranslator(LLVMContext& context, Module* _module, const ppu_mo { "__", aarch64::x19 } // Probably link table entries }; + // Build list of imposter functions built by the patch manager. + g_fxo->need(); + std::vector faux_functions_list; + for (const auto& a : g_fxo->get().block_addresses) + { + faux_functions_list.push_back(fmt::format("__0x%x", a)); + } + aarch64::GHC_frame_preservation_pass::config_t config = { .debug_info = false, // Set to "true" to insert debug frames on x27 .use_stack_frames = false, // We don't need this since the PPU GW allocates global scratch on the stack .hypervisor_context_offset = ::offset32(&ppu_thread::hv_ctx), .exclusion_callback = {}, // Unused, we don't have special exclusion functions on PPU - .base_register_lookup = base_reg_lookup + .base_register_lookup = base_reg_lookup, + .faux_function_list = std::move(faux_functions_list) }; // Create transform pass