1
0
mirror of https://github.com/RPCS3/rpcs3.git synced 2024-11-22 02:32:36 +01:00

Make it work with LLVM

Allow multiple entries for a single code.
This commit is contained in:
Eladash 2022-12-09 20:06:50 +02:00 committed by Ivan
parent f572e29a13
commit 7df093c94b
2 changed files with 95 additions and 40 deletions

View File

@ -703,8 +703,6 @@ static usz apply_modification(std::basic_string<u32>& applied, const patch_engin
} }
case patch_type::code_alloc: case patch_type::code_alloc:
{ {
relocate_instructions_at = 0;
const u32 out_branch = vm::try_get_addr(dst + (offset & -4)).first; const u32 out_branch = vm::try_get_addr(dst + (offset & -4)).first;
// Allow only if points to a PPU executable instruction // Allow only if points to a PPU executable instruction
@ -715,6 +713,13 @@ static usz apply_modification(std::basic_string<u32>& applied, const patch_engin
const u32 alloc_size = utils::align(static_cast<u32>(p.value.long_value + 1) * 4, 0x10000); const u32 alloc_size = utils::align(static_cast<u32>(p.value.long_value + 1) * 4, 0x10000);
// Check if should maybe reuse previous code cave allocation (0 size)
if (alloc_size - 4 != 0)
{
// Nope
relocate_instructions_at = 0;
}
// Always executable // Always executable
u64 flags = vm::alloc_executable | vm::alloc_unwritable; u64 flags = vm::alloc_executable | vm::alloc_unwritable;
@ -738,7 +743,7 @@ static usz apply_modification(std::basic_string<u32>& applied, const patch_engin
// Range allowed for absolute branches to operate at // Range allowed for absolute branches to operate at
// It takes into account that we need to put a branch for return at the end of memory space // It takes into account that we need to put a branch for return at the end of memory space
const u32 addr = p.alloc_addr = alloc_map->alloc(alloc_size, nullptr, 0x10000, flags); const u32 addr = p.alloc_addr = (relocate_instructions_at ? relocate_instructions_at : alloc_map->alloc(alloc_size, nullptr, 0x10000, flags));
if (!addr) if (!addr)
{ {
@ -751,8 +756,12 @@ static usz apply_modification(std::basic_string<u32>& applied, const patch_engin
// NOP filled // NOP filled
std::fill_n(vm::get_super_ptr<u32>(addr), p.value.long_value, 0x60000000); std::fill_n(vm::get_super_ptr<u32>(addr), p.value.long_value, 0x60000000);
// Register code // Check if already registered by previous code allocation
ppu_register_range(addr, alloc_size); if (relocate_instructions_at != addr)
{
// Register code
ppu_register_range(addr, alloc_size);
}
resval = out_branch & -4; resval = out_branch & -4;
@ -772,8 +781,6 @@ static usz apply_modification(std::basic_string<u32>& applied, const patch_engin
continue; continue;
} }
// Write address of the allocated memory to the code entry
*vm::get_super_ptr<u32>(resval) = addr;
relocate_instructions_at = addr; relocate_instructions_at = addr;
break; break;
} }

View File

@ -617,34 +617,20 @@ struct ppu_far_jumps_t
bool with_toc; bool with_toc;
std::string module_name; std::string module_name;
ppu_intrp_func_t func; ppu_intrp_func_t func;
};
u32 get_target(u32 pc, ppu_thread* ppu = nullptr) const
ppu_far_jumps_t(int) noexcept {}
std::unordered_map<u32, all_info_t> vals;
::jit_runtime rt;
mutable shared_mutex mutex;
// Get target address, 'ppu' is used in ppu_far_jump in order to modify registers
u32 get_target(const u32 pc, ppu_thread* ppu = nullptr)
{
reader_lock lock(mutex);
if (auto it = vals.find(pc); it != vals.end())
{ {
all_info_t& all_info = it->second; u32 direct_target = this->target;
u32 target = all_info.target;
bool link = all_info.link; bool to_link = this->link;
bool from_opd = all_info.with_toc; bool from_opd = this->with_toc;
if (!all_info.module_name.empty()) if (!this->module_name.empty())
{ {
target = ppu_get_exported_func_addr(target, all_info.module_name); direct_target = ppu_get_exported_func_addr(direct_target, this->module_name);
} }
if (from_opd && !vm::check_addr<sizeof(ppu_func_opd_t)>(target)) if (from_opd && !vm::check_addr<sizeof(ppu_func_opd_t)>(direct_target))
{ {
// Avoid reading unmapped memory under mutex // Avoid reading unmapped memory under mutex
from_opd = false; from_opd = false;
@ -652,11 +638,11 @@ struct ppu_far_jumps_t
if (from_opd) if (from_opd)
{ {
auto& opd = vm::_ref<ppu_func_opd_t>(target); auto& opd = vm::_ref<ppu_func_opd_t>(direct_target);
target = opd.addr; direct_target = opd.addr;
// We modify LR to custom values here // We modify LR to custom values here
link = false; to_link = false;
if (ppu) if (ppu)
{ {
@ -670,20 +656,71 @@ struct ppu_far_jumps_t
saved_info.saved_lr = std::exchange(ppu->lr, g_fxo->get<ppu_function_manager>().func_addr(FIND_FUNC(ppu_return_from_far_jump), true)); saved_info.saved_lr = std::exchange(ppu->lr, g_fxo->get<ppu_function_manager>().func_addr(FIND_FUNC(ppu_return_from_far_jump), true));
saved_info.saved_r2 = std::exchange(ppu->gpr[2], opd.rtoc); saved_info.saved_r2 = std::exchange(ppu->gpr[2], opd.rtoc);
} }
} }
if (link && ppu) if (to_link && ppu)
{ {
ppu->lr = pc + 4; ppu->lr = pc + 4;
} }
return target; return direct_target;
}
};
ppu_far_jumps_t(int) noexcept {}
std::map<u32, all_info_t> vals;
::jit_runtime rt;
mutable shared_mutex mutex;
// Get target address, 'ppu' is used in ppu_far_jump in order to modify registers
u32 get_target(u32 pc, ppu_thread* ppu = nullptr)
{
reader_lock lock(mutex);
if (auto it = vals.find(pc); it != vals.end())
{
all_info_t& all_info = it->second;
return all_info.get_target(pc, ppu);
} }
return {}; return {};
} }
// Get function patches in range (entry -> target)
std::vector<std::pair<u32, u32>> get_targets(u32 pc, u32 size)
{
std::vector<std::pair<u32, u32>> targets;
reader_lock lock(mutex);
auto it = vals.lower_bound(pc);
if (it == vals.end())
{
return targets;
}
if (it->first >= pc + size)
{
return targets;
}
for (auto end = vals.lower_bound(pc + size); it != end; it++)
{
all_info_t& all_info = it->second;
if (u32 target = all_info.get_target(it->first))
{
targets.emplace_back(it->first, target);
}
}
return targets;
}
// Generate a mini-function which updates PC (for LLVM) and jumps to ppu_far_jump to handle redirections
template <bool Locked = true> template <bool Locked = true>
ppu_intrp_func_t gen_jump(u32 pc) ppu_intrp_func_t gen_jump(u32 pc)
{ {
@ -1019,7 +1056,7 @@ void ppu_thread::dump_regs(std::string& ret) const
if (const_value != reg) if (const_value != reg)
{ {
// Expectation of pretictable code path has not been met (such as a branch directly to the instruction) // Expectation of predictable code path has not been met (such as a branch directly to the instruction)
is_const = false; is_const = false;
} }
@ -3478,14 +3515,25 @@ bool ppu_initialize(const ppu_module& info, bool check_only)
} }
} }
if (jit) if (g_fxo->is_init<ppu_far_jumps_t>())
{ {
const auto far_jump = ppu_get_far_jump(func.addr) ? g_fxo->get<ppu_far_jumps_t>().gen_jump(func.addr) : nullptr; auto targets = g_fxo->get<ppu_far_jumps_t>().get_targets(func.addr, func.size);
if (far_jump) for (auto [source, target] : targets)
{
auto far_jump = ensure(g_fxo->get<ppu_far_jumps_t>().gen_jump(source));
if (source == func.addr && jit)
{
jit->update_global_mapping(fmt::format("__0x%x", func.addr - reloc), reinterpret_cast<u64>(far_jump));
}
ppu_register_function_at(source, 4, far_jump);
}
if (!targets.empty())
{ {
// Replace the function with ppu_far_jump // Replace the function with ppu_far_jump
jit->update_global_mapping(fmt::format("__0x%x", func.addr - reloc), reinterpret_cast<u64>(far_jump));
fpos++; fpos++;
continue; continue;
} }