diff --git a/rpcs3/Emu/Cell/PPUModule.cpp b/rpcs3/Emu/Cell/PPUModule.cpp index badedfebff..be0eb9c171 100644 --- a/rpcs3/Emu/Cell/PPUModule.cpp +++ b/rpcs3/Emu/Cell/PPUModule.cpp @@ -694,6 +694,112 @@ static auto ppu_load_imports(std::vector& 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(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(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 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(&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 == 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 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(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()->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); + } + } + + ppu_loader.success("SPU executable hash: %s (<- %u)%s", hash, applied, dump); + } +} + std::shared_ptr ppu_load_prx(const ppu_prx_object& elf, const std::string& path) { // Create new PRX object @@ -966,6 +1072,12 @@ std::shared_ptr ppu_load_prx(const ppu_prx_object& elf, const std::stri applied += g_fxo->get()->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()->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(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 == 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(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); - } - } - - 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 ppu_load_overlay(const ppu_exec_object& elf, const applied += g_fxo->get()->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 diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index da4757078b..592e5fd152 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -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()->append_global_patches(); diff --git a/rpcs3/Loader/ELF.h b/rpcs3/Loader/ELF.h index a3b4130b35..11a3c6197e 100644 --- a/rpcs3/Loader/ELF.h +++ b/rpcs3/Loader/ELF.h @@ -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(this->header.e_type); header.e_machine = Machine; header.e_version = 1; header.e_entry = this->header.e_entry;