mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-07-10 00:41:26 +12:00
SPU: Embedded PRX/overlay image patching and dumping
This commit is contained in:
parent
d5f7e7b179
commit
983547af7e
3 changed files with 123 additions and 72 deletions
|
@ -694,6 +694,112 @@ static auto ppu_load_imports(std::vector<ppu_reloc>& relocs, ppu_linkage_info* l
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ppu_check_patch_spu_images(const ppu_segment& seg)
|
||||||
|
{
|
||||||
|
const std::string_view seg_view{vm::get_super_ptr<char>(seg.addr), seg.size};
|
||||||
|
|
||||||
|
for (std::size_t i = seg_view.find("\177ELF"); i < seg.size; i = seg_view.find("\177ELF", i + 4))
|
||||||
|
{
|
||||||
|
const auto elf_header = vm::get_super_ptr<u8>(seg.addr + i);
|
||||||
|
|
||||||
|
// Try to load SPU image
|
||||||
|
const spu_exec_object obj(fs::file(elf_header, seg.size - i));
|
||||||
|
|
||||||
|
if (obj != elf_error::ok)
|
||||||
|
{
|
||||||
|
// This address does not have an SPU elf
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Segment info dump
|
||||||
|
std::vector<std::string> names;
|
||||||
|
std::string dump;
|
||||||
|
|
||||||
|
std::size_t applied = 0;
|
||||||
|
|
||||||
|
// Executable hash
|
||||||
|
sha1_context sha2;
|
||||||
|
sha1_starts(&sha2);
|
||||||
|
u8 sha1_hash[20];
|
||||||
|
|
||||||
|
for (const auto& prog : obj.progs)
|
||||||
|
{
|
||||||
|
// Only hash the data, we are not loading it
|
||||||
|
sha1_update(&sha2, reinterpret_cast<const uchar*>(&prog.p_vaddr), sizeof(prog.p_vaddr));
|
||||||
|
sha1_update(&sha2, reinterpret_cast<const uchar*>(&prog.p_memsz), sizeof(prog.p_memsz));
|
||||||
|
sha1_update(&sha2, reinterpret_cast<const uchar*>(&prog.p_filesz), sizeof(prog.p_filesz));
|
||||||
|
|
||||||
|
fmt::append(dump, "\n\tSegment: p_type=0x%x, p_vaddr=0x%llx, p_filesz=0x%llx, p_memsz=0x%llx, p_offset=0x%llx", prog.p_type, prog.p_vaddr, prog.p_filesz, prog.p_memsz, prog.p_offset);
|
||||||
|
|
||||||
|
if (prog.p_type == 0x1u /* LOAD */ && prog.p_filesz > 0u)
|
||||||
|
{
|
||||||
|
sha1_update(&sha2, (elf_header + prog.p_offset), prog.p_filesz);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (prog.p_type == 0x4u /* NOTE */ && prog.p_filesz > 0u)
|
||||||
|
{
|
||||||
|
std::unique_ptr<u8[]> buf(new u8[prog.p_filesz + 0x15]{0});
|
||||||
|
std::memcpy(buf.get(), (elf_header + prog.p_offset), prog.p_filesz);
|
||||||
|
|
||||||
|
sha1_update(&sha2, buf.get(), prog.p_filesz);
|
||||||
|
|
||||||
|
// We assume that the string SPUNAME exists 0x14 bytes into the NOTE segment
|
||||||
|
// If NOTE section is not big enough for a whole string it will get truncated
|
||||||
|
// If NOTE section is too small to contain a name it won't extract a name
|
||||||
|
names.emplace_back(reinterpret_cast<char*>(buf.get() + 0x14));
|
||||||
|
|
||||||
|
if (!names.back().empty())
|
||||||
|
{
|
||||||
|
fmt::append(dump, "\n\tSPUNAME: '%s'", names.back());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
names.resize(names.size() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sha1_finish(&sha2, sha1_hash);
|
||||||
|
|
||||||
|
// Format patch name
|
||||||
|
std::string hash("SPU-0000000000000000000000000000000000000000");
|
||||||
|
for (u32 i = 0; i < sizeof(sha1_hash); i++)
|
||||||
|
{
|
||||||
|
constexpr auto pal = "0123456789abcdef";
|
||||||
|
hash[4 + i * 2] = pal[sha1_hash[i] >> 4];
|
||||||
|
hash[5 + i * 2] = pal[sha1_hash[i] & 15];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_cfg.core.spu_debug)
|
||||||
|
{
|
||||||
|
std::string final_name;
|
||||||
|
for (const auto& name : names)
|
||||||
|
{
|
||||||
|
final_name += vfs::escape(name, true);
|
||||||
|
final_name += '-'; // Each name proceeded by '-' and also the last one
|
||||||
|
}
|
||||||
|
|
||||||
|
fs::file dump_file(fs::get_cache_dir() + "/spu_progs/" + final_name + hash.substr(4), fs::rewrite);
|
||||||
|
obj.save(dump_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to patch each segment, will only succeed if the address exists in SPU local storage
|
||||||
|
for (const auto& prog : obj.progs)
|
||||||
|
{
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
if (!Emu.GetTitleID().empty())
|
||||||
|
{
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ppu_loader.success("SPU executable hash: %s (<- %u)%s", hash, applied, dump);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::string& path)
|
std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::string& path)
|
||||||
{
|
{
|
||||||
// Create new PRX object
|
// Create new PRX object
|
||||||
|
@ -966,6 +1072,12 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
|
||||||
applied += g_fxo->get<patch_engine>()->apply(Emu.GetTitleID() + '-' + hash, vm::g_base_addr);
|
applied += g_fxo->get<patch_engine>()->apply(Emu.GetTitleID() + '-' + hash, vm::g_base_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Embedded SPU elf patching
|
||||||
|
for (const auto& seg : prx->segs)
|
||||||
|
{
|
||||||
|
ppu_check_patch_spu_images(seg);
|
||||||
|
}
|
||||||
|
|
||||||
ppu_loader.success("PRX library hash: %s (<- %u)", hash, applied);
|
ppu_loader.success("PRX library hash: %s (<- %u)", hash, applied);
|
||||||
|
|
||||||
if (Emu.IsReady() && g_fxo->get<ppu_module>()->segs.empty())
|
if (Emu.IsReady() && g_fxo->get<ppu_module>()->segs.empty())
|
||||||
|
@ -1133,77 +1245,9 @@ void ppu_load_exec(const ppu_exec_object& elf)
|
||||||
ppu_initialize_modules(link);
|
ppu_initialize_modules(link);
|
||||||
|
|
||||||
// Embedded SPU elf patching
|
// Embedded SPU elf patching
|
||||||
for (u32 i = _main->segs[0].addr; i < (_main->segs[0].addr + _main->segs[0].size); i += 128)
|
for (const auto& seg : _main->segs)
|
||||||
{
|
{
|
||||||
uchar* elf_header = vm::_ptr<u8>(i);
|
ppu_check_patch_spu_images(seg);
|
||||||
const spu_exec_object obj(fs::file(vm::base(vm::cast(i, HERE)), (_main->segs[0].addr + _main->segs[0].size) - i));
|
|
||||||
|
|
||||||
if (obj != elf_error::ok)
|
|
||||||
{
|
|
||||||
// This address does not have an SPU elf
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Segment info dump
|
|
||||||
std::string dump;
|
|
||||||
|
|
||||||
applied = 0;
|
|
||||||
|
|
||||||
// Executable hash
|
|
||||||
sha1_context sha2;
|
|
||||||
sha1_starts(&sha2);
|
|
||||||
u8 sha1_hash[20];
|
|
||||||
|
|
||||||
for (const auto& prog : obj.progs)
|
|
||||||
{
|
|
||||||
// Only hash the data, we are not loading it
|
|
||||||
sha1_update(&sha2, reinterpret_cast<const uchar*>(&prog.p_vaddr), sizeof(prog.p_vaddr));
|
|
||||||
sha1_update(&sha2, reinterpret_cast<const uchar*>(&prog.p_memsz), sizeof(prog.p_memsz));
|
|
||||||
sha1_update(&sha2, reinterpret_cast<const uchar*>(&prog.p_filesz), sizeof(prog.p_filesz));
|
|
||||||
|
|
||||||
fmt::append(dump, "\n\tSegment: p_type=0x%x, p_vaddr=0x%llx, p_filesz=0x%llx, p_memsz=0x%llx, p_offset=0x%llx", prog.p_type, prog.p_vaddr, prog.p_filesz, prog.p_memsz, prog.p_offset);
|
|
||||||
|
|
||||||
if (prog.p_type == 0x1u /* LOAD */ && prog.p_filesz > 0u)
|
|
||||||
{
|
|
||||||
sha1_update(&sha2, (elf_header + prog.p_offset), prog.p_filesz);
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (prog.p_type == 0x4u /* NOTE */ && prog.p_filesz > 0u)
|
|
||||||
{
|
|
||||||
sha1_update(&sha2, (elf_header + prog.p_offset), prog.p_filesz);
|
|
||||||
|
|
||||||
// We assume that the string SPUNAME exists 0x14 bytes into the NOTE segment
|
|
||||||
const auto spu_name = reinterpret_cast<const char*>(elf_header + prog.p_offset + 0x14);
|
|
||||||
fmt::append(dump, "\n\tSPUNAME: '%s'", spu_name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sha1_finish(&sha2, sha1_hash);
|
|
||||||
|
|
||||||
// Format patch name
|
|
||||||
std::string hash("SPU-0000000000000000000000000000000000000000");
|
|
||||||
for (u32 i = 0; i < sizeof(sha1_hash); i++)
|
|
||||||
{
|
|
||||||
constexpr auto pal = "0123456789abcdef";
|
|
||||||
hash[4 + i * 2] = pal[sha1_hash[i] >> 4];
|
|
||||||
hash[5 + i * 2] = pal[sha1_hash[i] & 15];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try to patch each segment, will only succeed if the address exists in SPU local storage
|
|
||||||
for (const auto& prog : obj.progs)
|
|
||||||
{
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
if (!Emu.GetTitleID().empty())
|
|
||||||
{
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ppu_loader.success("SPU executable hash: %s (<- %u)%s", hash, applied, dump);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Static HLE patching
|
// Static HLE patching
|
||||||
|
@ -1699,6 +1743,12 @@ std::shared_ptr<lv2_overlay> ppu_load_overlay(const ppu_exec_object& elf, const
|
||||||
applied += g_fxo->get<patch_engine>()->apply(Emu.GetTitleID() + '-' + hash, vm::g_base_addr);
|
applied += g_fxo->get<patch_engine>()->apply(Emu.GetTitleID() + '-' + hash, vm::g_base_addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Embedded SPU elf patching
|
||||||
|
for (const auto& seg : ovlm->segs)
|
||||||
|
{
|
||||||
|
ppu_check_patch_spu_images(seg);
|
||||||
|
}
|
||||||
|
|
||||||
ppu_loader.success("OVL executable hash: %s (<- %u)", hash, applied);
|
ppu_loader.success("OVL executable hash: %s (<- %u)", hash, applied);
|
||||||
|
|
||||||
// Load other programs
|
// Load other programs
|
||||||
|
|
|
@ -298,6 +298,7 @@ void Emulator::Init()
|
||||||
|
|
||||||
make_path_verbose(fs::get_cache_dir() + "shaderlog/");
|
make_path_verbose(fs::get_cache_dir() + "shaderlog/");
|
||||||
make_path_verbose(fs::get_config_dir() + "captures/");
|
make_path_verbose(fs::get_config_dir() + "captures/");
|
||||||
|
make_path_verbose(fs::get_cache_dir() + "spu_progs/");
|
||||||
|
|
||||||
// Initialize patch engine
|
// Initialize patch engine
|
||||||
g_fxo->init<patch_engine>()->append_global_patches();
|
g_fxo->init<patch_engine>()->append_global_patches();
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../../Utilities/types.h"
|
#include "../../Utilities/types.h"
|
||||||
#include "../../Utilities/File.h"
|
#include "../../Utilities/File.h"
|
||||||
|
@ -293,7 +293,7 @@ public:
|
||||||
header.e_curver = 1;
|
header.e_curver = 1;
|
||||||
header.e_os_abi = OS != elf_os::none ? OS : this->header.e_os_abi;
|
header.e_os_abi = OS != elf_os::none ? OS : this->header.e_os_abi;
|
||||||
header.e_abi_ver = this->header.e_abi_ver;
|
header.e_abi_ver = this->header.e_abi_ver;
|
||||||
header.e_type = Type != elf_type::none ? Type : this->header.e_type;
|
header.e_type = Type != elf_type::none ? Type : static_cast<elf_type>(this->header.e_type);
|
||||||
header.e_machine = Machine;
|
header.e_machine = Machine;
|
||||||
header.e_version = 1;
|
header.e_version = 1;
|
||||||
header.e_entry = this->header.e_entry;
|
header.e_entry = this->header.e_entry;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue