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:
{
relocate_instructions_at = 0;
const u32 out_branch = vm::try_get_addr(dst + (offset & -4)).first;
// 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);
// Check if should maybe reuse previous code cave allocation (0 size)
if (alloc_size - 4 != 0)
{
// Nope
relocate_instructions_at = 0;
}
// Always executable
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
// 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)
{
@ -751,8 +756,12 @@ static usz apply_modification(std::basic_string<u32>& applied, const patch_engin
// NOP filled
std::fill_n(vm::get_super_ptr<u32>(addr), p.value.long_value, 0x60000000);
// Check if already registered by previous code allocation
if (relocate_instructions_at != addr)
{
// Register code
ppu_register_range(addr, alloc_size);
}
resval = out_branch & -4;
@ -772,8 +781,6 @@ static usz apply_modification(std::basic_string<u32>& applied, const patch_engin
continue;
}
// Write address of the allocated memory to the code entry
*vm::get_super_ptr<u32>(resval) = addr;
relocate_instructions_at = addr;
break;
}

View File

@ -617,34 +617,20 @@ struct ppu_far_jumps_t
bool with_toc;
std::string module_name;
ppu_intrp_func_t func;
};
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)
u32 get_target(u32 pc, ppu_thread* ppu = nullptr) const
{
reader_lock lock(mutex);
u32 direct_target = this->target;
if (auto it = vals.find(pc); it != vals.end())
bool to_link = this->link;
bool from_opd = this->with_toc;
if (!this->module_name.empty())
{
all_info_t& all_info = it->second;
u32 target = all_info.target;
bool link = all_info.link;
bool from_opd = all_info.with_toc;
if (!all_info.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
from_opd = false;
@ -652,11 +638,11 @@ struct ppu_far_jumps_t
if (from_opd)
{
auto& opd = vm::_ref<ppu_func_opd_t>(target);
target = opd.addr;
auto& opd = vm::_ref<ppu_func_opd_t>(direct_target);
direct_target = opd.addr;
// We modify LR to custom values here
link = false;
to_link = false;
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_r2 = std::exchange(ppu->gpr[2], opd.rtoc);
}
}
if (link && ppu)
if (to_link && ppu)
{
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 {};
}
// 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>
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)
{
// 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;
}
@ -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
jit->update_global_mapping(fmt::format("__0x%x", func.addr - reloc), reinterpret_cast<u64>(far_jump));
fpos++;
continue;
}