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

Reimplement/fix PRX patches

This commit is contained in:
Eladash 2021-02-08 17:04:50 +02:00 committed by Ivan
parent 48296c2ba6
commit e26ae9899c
4 changed files with 56 additions and 60 deletions

View File

@ -505,39 +505,26 @@ void patch_engine::append_title_patches(const std::string& title_id)
load(m_map, get_patches_path() + title_id + "_patch.yml"); load(m_map, get_patches_path() + title_id + "_patch.yml");
} }
std::basic_string<u32> patch_engine::apply(const std::string& name, u8* dst) static std::basic_string<u32> apply_modification(const patch_engine::patch_info& patch, u8* dst, u32 filesz, u32 min_addr)
{
return apply_patch<false>(name, dst, 0, 0);
}
std::basic_string<u32> patch_engine::apply_with_ls_check(const std::string& name, u8* dst, u32 filesz, u32 ls_addr)
{
return apply_patch<true>(name, dst, filesz, ls_addr);
}
template <bool CheckLS>
static std::basic_string<u32> apply_modification(const patch_engine::patch_info& patch, u8* dst, u32 filesz, u32 ls_addr)
{ {
std::basic_string<u32> applied; std::basic_string<u32> applied;
for (const auto& p : patch.data_list) for (const auto& p : patch.data_list)
{ {
u32 offset = p.offset; u32 offset = p.offset;
u32 resval = 0;
if constexpr (CheckLS) if (offset < min_addr || offset - min_addr >= filesz)
{ {
if (offset < ls_addr || offset >= (ls_addr + filesz)) // This patch is out of range for this segment
{ continue;
// This patch is out of range for this segment
continue;
}
offset -= ls_addr;
} }
offset -= min_addr;
auto ptr = dst + offset; auto ptr = dst + offset;
u32 resval = -1;
switch (p.type) switch (p.type)
{ {
case patch_type::invalid: case patch_type::invalid:
@ -584,10 +571,7 @@ static std::basic_string<u32> apply_modification(const patch_engine::patch_info&
case patch_type::be32: case patch_type::be32:
{ {
*reinterpret_cast<be_t<u32, 1>*>(ptr) = static_cast<u32>(p.value.long_value); *reinterpret_cast<be_t<u32, 1>*>(ptr) = static_cast<u32>(p.value.long_value);
if (offset % 4 == 0) resval = offset;
// Possibly an executable instruction
if constexpr (!CheckLS)
resval = offset;
break; break;
} }
case patch_type::bef32: case patch_type::bef32:
@ -598,6 +582,14 @@ static std::basic_string<u32> apply_modification(const patch_engine::patch_info&
case patch_type::be64: case patch_type::be64:
{ {
*reinterpret_cast<be_t<u64, 1>*>(ptr) = static_cast<u64>(p.value.long_value); *reinterpret_cast<be_t<u64, 1>*>(ptr) = static_cast<u64>(p.value.long_value);
if (offset % 4)
{
break;
}
resval = offset;
applied.push_back((offset + 7) & -4); // Two 32-bit locations
break; break;
} }
case patch_type::bef64: case patch_type::bef64:
@ -607,14 +599,14 @@ static std::basic_string<u32> apply_modification(const patch_engine::patch_info&
} }
} }
// Possibly an executable instruction
applied.push_back(resval); applied.push_back(resval);
} }
return applied; return applied;
} }
template <bool CheckLS> std::basic_string<u32> patch_engine::apply(const std::string& name, u8* dst, u32 filesz, u32 min_addr)
std::basic_string<u32> patch_engine::apply_patch(const std::string& name, u8* dst, u32 filesz, u32 ls_addr)
{ {
if (m_map.find(name) == m_map.cend()) if (m_map.find(name) == m_map.cend())
{ {
@ -719,7 +711,7 @@ std::basic_string<u32> patch_engine::apply_patch(const std::string& name, u8* ds
m_applied_groups.insert(patch.patch_group); m_applied_groups.insert(patch.patch_group);
} }
auto applied = apply_modification<CheckLS>(patch, dst, filesz, ls_addr); auto applied = apply_modification(patch, dst, filesz, min_addr);
applied_total += applied; applied_total += applied;

View File

@ -129,16 +129,9 @@ public:
void append_title_patches(const std::string& title_id); void append_title_patches(const std::string& title_id);
// Apply patch (returns the number of entries applied) // Apply patch (returns the number of entries applied)
std::basic_string<u32> apply(const std::string& name, u8* dst); std::basic_string<u32> apply(const std::string& name, u8* dst, u32 filesz = UINT32_MAX, u32 min_addr = 0);
// Apply patch with a check that the address exists in SPU local storage
std::basic_string<u32> apply_with_ls_check(const std::string& name, u8* dst, u32 filesz, u32 ls_addr);
private: private:
// Internal: Apply patch (returns the number of entries applied)
template <bool CheckLS>
std::basic_string<u32> apply_patch(const std::string& name, u8* dst, u32 filesz, u32 ls_addr);
// Database // Database
patch_map m_map; patch_map m_map;

View File

@ -315,18 +315,18 @@ static void ppu_initialize_modules(ppu_linkage_info* link)
ppu_loader.trace("** &0x%08X: %s (size=0x%x, align=0x%x)", variable.first, variable.second.name, variable.second.size, variable.second.align); ppu_loader.trace("** &0x%08X: %s (size=0x%x, align=0x%x)", variable.first, variable.second.name, variable.second.size, variable.second.align);
// Allocate HLE variable // Allocate HLE variable
if (variable.second.size >= 4096 || variable.second.align >= 4096) if (variable.second.size >= 0x10000 || variable.second.align >= 0x10000)
{ {
variable.second.addr = vm::alloc(variable.second.size, vm::main, std::max<u32>(variable.second.align, 0x10000)); variable.second.addr = vm::alloc(variable.second.size, vm::main, std::max<u32>(variable.second.align, 0x10000));
} }
else else
{ {
const u32 next = utils::align(alloc_addr, variable.second.align); const u32 next = utils::align(alloc_addr, variable.second.align);
const u32 end = next + variable.second.size; const u32 end = next + variable.second.size - 1;
if (!next || (end >> 12 != alloc_addr >> 12)) if (!next || (end >> 16 != alloc_addr >> 16))
{ {
alloc_addr = vm::alloc(4096, vm::main); alloc_addr = vm::alloc(0x10000, vm::main);
} }
else else
{ {
@ -773,12 +773,12 @@ static void ppu_check_patch_spu_images(const ppu_segment& seg)
for (const auto& prog : obj.progs) for (const auto& prog : obj.progs)
{ {
// Apply the patch // Apply the patch
applied += g_fxo->get<patch_engine>()->apply_with_ls_check(hash, (elf_header + prog.p_offset), prog.p_filesz, prog.p_vaddr); applied += g_fxo->get<patch_engine>()->apply(hash, (elf_header + prog.p_offset), prog.p_filesz, prog.p_vaddr);
if (!Emu.GetTitleID().empty()) if (!Emu.GetTitleID().empty())
{ {
// Alternative patch // Alternative patch
applied += g_fxo->get<patch_engine>()->apply_with_ls_check(Emu.GetTitleID() + '-' + hash, (elf_header + prog.p_offset), prog.p_filesz, prog.p_vaddr); applied += g_fxo->get<patch_engine>()->apply(Emu.GetTitleID() + '-' + hash, (elf_header + prog.p_offset), prog.p_filesz, prog.p_vaddr);
} }
} }
@ -1084,21 +1084,33 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
sha1_finish(&sha, prx->sha1); sha1_finish(&sha, prx->sha1);
// Format patch name // Format patch name
std::string hash("PRX-0000000000000000000000000000000000000000"); std::string hash = fmt::format("PRX-%s", fmt::base57(prx->sha1));
for (u32 i = 0; i < 20; i++)
{
constexpr auto pal = "0123456789abcdef";
hash[4 + i * 2] = pal[prx->sha1[i] >> 4];
hash[5 + i * 2] = pal[prx->sha1[i] & 15];
}
// Apply the patch std::basic_string<u32> applied;
auto applied = g_fxo->get<patch_engine>()->apply(hash, vm::g_base_addr);
if (!Emu.GetTitleID().empty()) for (usz i = 0; i < prx->segs.size(); i++)
{ {
// Alternative patch const auto& seg = prx->segs[i];
applied += g_fxo->get<patch_engine>()->apply(Emu.GetTitleID() + '-' + hash, vm::g_base_addr);
if (!seg.size) continue;
const std::string hash_seg = fmt::format("%s-%u", hash, i);
// Apply the patch
auto _applied = g_fxo->get<patch_engine>()->apply(hash_seg, vm::get_super_ptr(seg.addr), seg.size);
if (!Emu.GetTitleID().empty())
{
// Alternative patch
_applied += g_fxo->get<patch_engine>()->apply(Emu.GetTitleID() + '-' + hash_seg, vm::get_super_ptr(seg.addr), seg.size);
}
// Rebase patch offsets
std::for_each(_applied.begin(), _applied.end(), [&](u32& res) { if (res != umax) res += seg.addr; });
applied += _applied;
ppu_loader.success("PRX library hash: %s (<- %u)", hash_seg, _applied.size());
} }
// Embedded SPU elf patching // Embedded SPU elf patching
@ -1109,8 +1121,6 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
prx->analyse(toc, 0, end, applied); prx->analyse(toc, 0, end, applied);
ppu_loader.success("PRX library hash: %s (<- %u)", hash, applied.size());
try_spawn_ppu_if_exclusive_program(*prx); try_spawn_ppu_if_exclusive_program(*prx);
return prx; return prx;

View File

@ -385,15 +385,16 @@ extern void ppu_register_range(u32 addr, u32 size)
return; return;
} }
size = utils::align(size + addr % 0x10000, 0x10000);
addr &= -0x10000;
// Register executable range at // Register executable range at
utils::memory_commit(&ppu_ref(addr), size * 2, utils::protection::rw); utils::memory_commit(&ppu_ref(addr), u64{size} * 2, utils::protection::rw);
vm::page_protect(addr, utils::align(size, 0x10000), 0, vm::page_executable); vm::page_protect(addr, size, 0, vm::page_executable);
const u64 fallback = reinterpret_cast<uptr>(ppu_fallback); const u64 fallback = reinterpret_cast<uptr>(ppu_fallback);
const u64 seg_base = addr; const u64 seg_base = addr;
size &= ~3; // Loop assumes `size = n * 4`, enforce that by rounding down
while (size) while (size)
{ {
if (g_cfg.core.ppu_decoder == ppu_decoder_type::llvm) if (g_cfg.core.ppu_decoder == ppu_decoder_type::llvm)