mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-04-20 11:36:13 +00: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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
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);
|
||||
|
||||
// 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);
|
||||
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);
|
||||
|
||||
ppu_check_patch_spu_images(seg);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
// Load other programs
|
||||
|
|
|
@ -298,6 +298,7 @@ void Emulator::Init()
|
|||
|
||||
make_path_verbose(fs::get_cache_dir() + "shaderlog/");
|
||||
make_path_verbose(fs::get_config_dir() + "captures/");
|
||||
make_path_verbose(fs::get_cache_dir() + "spu_progs/");
|
||||
|
||||
// Initialize patch engine
|
||||
g_fxo->init<patch_engine>()->append_global_patches();
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#pragma once
|
||||
#pragma once
|
||||
|
||||
#include "../../Utilities/types.h"
|
||||
#include "../../Utilities/File.h"
|
||||
|
@ -293,7 +293,7 @@ public:
|
|||
header.e_curver = 1;
|
||||
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_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_version = 1;
|
||||
header.e_entry = this->header.e_entry;
|
||||
|
|
Loading…
Add table
Reference in a new issue