diff --git a/Utilities/bin_patch.cpp b/Utilities/bin_patch.cpp index b578f03aad..0e339fa748 100644 --- a/Utilities/bin_patch.cpp +++ b/Utilities/bin_patch.cpp @@ -173,3 +173,78 @@ std::size_t patch_engine::apply(const std::string& name, u8* dst) const return found->second.size(); } + +std::size_t patch_engine::apply_with_ls_check(const std::string& name, u8* dst, u32 filesz, u32 ls_addr) const +{ + u32 rejected = 0; + + const auto found = m_map.find(name); + + if (found == m_map.cend()) + { + return 0; + } + + // Apply modifications sequentially + for (const auto& p : found->second) + { + auto ptr = dst + (p.offset - ls_addr); + + if(p.offset < ls_addr || p.offset >= (ls_addr + filesz)) + { + // This patch is out of range for this segment + rejected++; + continue; + } + + switch (p.type) + { + case patch_type::load: + { + // Invalid in this context + break; + } + case patch_type::byte: + { + *ptr = static_cast(p.value); + break; + } + case patch_type::le16: + { + *reinterpret_cast*>(ptr) = static_cast(p.value); + break; + } + case patch_type::le32: + case patch_type::lef32: + { + *reinterpret_cast*>(ptr) = static_cast(p.value); + break; + } + case patch_type::le64: + case patch_type::lef64: + { + *reinterpret_cast*>(ptr) = static_cast(p.value); + break; + } + case patch_type::be16: + { + *reinterpret_cast*>(ptr) = static_cast(p.value); + break; + } + case patch_type::be32: + case patch_type::bef32: + { + *reinterpret_cast*>(ptr) = static_cast(p.value); + break; + } + case patch_type::be64: + case patch_type::bef64: + { + *reinterpret_cast*>(ptr) = static_cast(p.value); + break; + } + } + } + + return (found->second.size() - rejected); +} diff --git a/Utilities/bin_patch.h b/Utilities/bin_patch.h index 562db18ba0..8bff69ab5f 100644 --- a/Utilities/bin_patch.h +++ b/Utilities/bin_patch.h @@ -39,4 +39,6 @@ public: // Apply patch (returns the number of entries applied) std::size_t apply(const std::string& name, u8* dst) const; + // Apply patch with a check that the address exists in SPU local storage + std::size_t apply_with_ls_check(const std::string&name, u8*dst, u32 filesz, u32 ls_addr) const; }; diff --git a/rpcs3/Emu/Cell/PPUModule.cpp b/rpcs3/Emu/Cell/PPUModule.cpp index 6cb6850a19..32d8ed84d4 100644 --- a/rpcs3/Emu/Cell/PPUModule.cpp +++ b/rpcs3/Emu/Cell/PPUModule.cpp @@ -1148,6 +1148,80 @@ void ppu_load_exec(const ppu_exec_object& elf) // Initialize HLE modules 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 += 4) + { + uchar* elf_header = vm::_ptr(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(&prog.p_vaddr), sizeof(prog.p_vaddr)); + sha1_update(&sha2, reinterpret_cast(&prog.p_memsz), sizeof(prog.p_memsz)); + sha1_update(&sha2, reinterpret_cast(&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 == 0x1 /* LOAD */ && prog.p_filesz > 0) + { + sha1_update(&sha2, (elf_header + prog.p_offset), prog.p_filesz); + } + + else if (prog.p_type == 0x4 /* NOTE */ && prog.p_filesz > 0) + { + 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(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()->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()->apply_with_ls_check(Emu.GetTitleID() + '-' + hash, (elf_header + prog.p_offset), prog.p_filesz, prog.p_vaddr); + } + } + + LOG_NOTICE(LOADER, "SPU executable hash: %s (<- %u)%s", hash, applied, dump); + + } + // Static HLE patching if (g_cfg.core.hook_functions) {