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:
parent
f572e29a13
commit
7df093c94b
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user