PPU Analyzer: Utilize exported functions

This commit is contained in:
Elad Ashkenazi 2024-06-08 12:44:23 +03:00
parent 6d280c5f3c
commit 6fff22391c
5 changed files with 60 additions and 19 deletions

View File

@ -530,7 +530,7 @@ namespace ppu_patterns
};
}
bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::basic_string<u32>& applied, std::function<bool()> check_aborted)
bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::basic_string<u32>& applied, const std::vector<u32>& exported_funcs, std::function<bool()> check_aborted)
{
if (segs.empty())
{
@ -931,6 +931,32 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
bool used_fallback = false;
if (func_queue.empty())
{
for (u32 addr : exported_funcs)
{
const u32 faddr = get_ref<u32>(addr);
if (addr < start || addr >= start + segs[0].size)
{
// TODO: Reverse engineer how it works (maybe some flag in exports)
if (faddr < start || faddr >= start + segs[0].size)
{
ppu_log.notice("Export not usable at 0x%x / 0x%x (0x%x...0x%x)", addr, faddr, start, start + segs[0].size);
continue;
}
addr = faddr;
}
ppu_log.trace("Enqueued exported PPU function 0x%x for analysis", addr);
add_func(addr, 0, 0);
used_fallback = true;
}
}
if (func_queue.empty() && segs[0].size >= 4u)
{
// Fallback, identify functions using callers (no jumptable detection, tail calls etc)

View File

@ -109,7 +109,7 @@ struct ppu_module
addr_to_seg_index = info.addr_to_seg_index;
}
bool analyse(u32 lib_toc, u32 entry, u32 end, const std::basic_string<u32>& applied, std::function<bool()> check_aborted = {});
bool analyse(u32 lib_toc, u32 entry, u32 end, const std::basic_string<u32>& applied, const std::vector<u32>& exported_funcs = std::vector<u32>{}, std::function<bool()> check_aborted = {});
void validate(u32 reloc);
template <typename T>

View File

@ -682,7 +682,7 @@ extern bool ppu_register_library_lock(std::string_view libname, bool lock_lib)
}
// Load and register exports; return special exports found (nameless module)
static auto ppu_load_exports(const ppu_module& _module, ppu_linkage_info* link, u32 exports_start, u32 exports_end, bool for_observing_callbacks = false, std::basic_string<bool>* loaded_flags = nullptr)
static auto ppu_load_exports(const ppu_module& _module, ppu_linkage_info* link, u32 exports_start, u32 exports_end, bool for_observing_callbacks = false, std::vector<u32>* funcs = nullptr, std::basic_string<bool>* loaded_flags = nullptr)
{
std::unordered_map<u32, u32> result;
@ -714,6 +714,11 @@ static auto ppu_load_exports(const ppu_module& _module, ppu_linkage_info* link,
const u32 nid = _module.get_ref<u32>(lib.nids, i);
const u32 addr = _module.get_ref<u32>(lib.addrs, i);
if (funcs)
{
funcs->emplace_back(addr);
}
if (i < lib.num_func)
{
ppu_loader.notice("** Special: [%s] at 0x%x [0x%x, 0x%x]", ppu_get_function_name({}, nid), addr, _module.get_ref<u32>(addr), _module.get_ref<u32>(addr + 4));
@ -735,13 +740,6 @@ static auto ppu_load_exports(const ppu_module& _module, ppu_linkage_info* link,
continue;
}
const bool is_dummy_load = Emu.IsReady() && g_fxo->get<main_ppu_module>().segs.empty() && !Emu.DeserialManager();
if (!is_dummy_load && for_observing_callbacks)
{
continue;
}
const std::string module_name(&_module.get_ref<const char>(lib.name));
if (unload_exports)
@ -761,7 +759,7 @@ static auto ppu_load_exports(const ppu_module& _module, ppu_linkage_info* link,
ppu_loader.error("Unexpected num_tlsvar (%u)!", lib.num_tlsvar);
}
const bool should_load = is_dummy_load || ppu_register_library_lock(module_name, true);
const bool should_load = for_observing_callbacks || ppu_register_library_lock(module_name, true);
if (loaded_flags)
{
@ -790,6 +788,16 @@ static auto ppu_load_exports(const ppu_module& _module, ppu_linkage_info* link,
const u32 faddr = _module.get_ref<u32>(faddrs, i);
ppu_loader.notice("**** %s export: [%s] (0x%08x) at 0x%x [at:0x%x]", module_name, ppu_get_function_name(module_name, fnid), fnid, faddr, _module.get_ref<u32>(faddr));
if (funcs)
{
funcs->emplace_back(faddr);
}
if (for_observing_callbacks)
{
continue;
}
// Function linkage info
auto& flink = mlink.functions[fnid];
@ -850,6 +858,11 @@ static auto ppu_load_exports(const ppu_module& _module, ppu_linkage_info* link,
const u32 vaddr = _module.get_ref<u32>(vaddrs, i);
ppu_loader.notice("**** %s export: &[%s] at 0x%x", module_name, ppu_get_variable_name(module_name, vnid), vaddr);
if (for_observing_callbacks)
{
continue;
}
// Variable linkage info
auto& vlink = mlink.variables[vnid];
@ -979,7 +992,7 @@ void ppu_manual_load_imports_exports(u32 imports_start, u32 imports_size, u32 ex
vm_all_fake_module.segs.emplace_back(ppu_segment{0x10000, 0 - 0x10000u, 1 /*LOAD*/, 0, 0 - 0x1000u, vm::base(0x10000)});
vm_all_fake_module.addr_to_seg_index.emplace(0x10000, 0);
ppu_load_exports(vm_all_fake_module, &link, exports_start, exports_start + exports_size, false, &loaded_flags);
ppu_load_exports(vm_all_fake_module, &link, exports_start, exports_start + exports_size, false, nullptr, &loaded_flags);
if (!imports_size)
{
@ -1731,6 +1744,8 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, bool virtual_lo
}
}
std::vector<u32> exported_funcs;
if (!elf.progs.empty() && elf.progs[0].p_paddr)
{
struct ppu_prx_library_info
@ -1774,7 +1789,7 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, bool virtual_lo
ppu_linkage_info dummy{};
prx->specials = ppu_load_exports(*prx, virtual_load ? &dummy : &link, prx->exports_start, prx->exports_end, true);
prx->specials = ppu_load_exports(*prx, virtual_load ? &dummy : &link, prx->exports_start, prx->exports_end, true, &exported_funcs);
prx->imports = ppu_load_imports(*prx, prx->relocs, virtual_load ? &dummy : &link, lib_info->imports_start, lib_info->imports_end);
if (virtual_load)
@ -1883,7 +1898,7 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, bool virtual_lo
ppu_check_patch_spu_images(*prx, seg);
}
prx->analyse(toc, 0, end, applied);
prx->analyse(toc, 0, end, applied, exported_funcs);
if (!ar && !virtual_load)
{
@ -3014,7 +3029,7 @@ std::pair<std::shared_ptr<lv2_overlay>, CellError> ppu_load_overlay(const ppu_ex
const auto cpu = cpu_thread::get_current();
// Analyse executable (TODO)
if (!ovlm->analyse(0, ovlm->entry, end, ovlm->applied_patches, !cpu ? std::function<bool()>() : [cpu]()
if (!ovlm->analyse(0, ovlm->entry, end, ovlm->applied_patches, std::vector<u32>{}, !cpu ? std::function<bool()>() : [cpu]()
{
return !!(cpu->state & cpu_flag::exit);
}))

View File

@ -4148,7 +4148,7 @@ extern void ppu_precompile(std::vector<std::string>& dir_queue, std::vector<ppu_
}
// Participate in thread execution limitation (takes a long time)
if (std::lock_guard lock(g_fxo->get<jit_core_allocator>().sem); !ovlm->analyse(0, ovlm->entry, ovlm->seg0_code_end, ovlm->applied_patches, []()
if (std::lock_guard lock(g_fxo->get<jit_core_allocator>().sem); !ovlm->analyse(0, ovlm->entry, ovlm->seg0_code_end, ovlm->applied_patches, std::vector<u32>{}, []()
{
return Emu.IsStopped();
}))
@ -4256,7 +4256,7 @@ extern void ppu_precompile(std::vector<std::string>& dir_queue, std::vector<ppu_
break;
}
if (!_main.analyse(0, _main.elf_entry, _main.seg0_code_end, _main.applied_patches, [](){ return Emu.IsStopped(); }))
if (!_main.analyse(0, _main.elf_entry, _main.seg0_code_end, _main.applied_patches, std::vector<u32>{}, [](){ return Emu.IsStopped(); }))
{
g_fxo->get<spu_cache>() = std::move(current_cache);
break;
@ -4308,7 +4308,7 @@ extern void ppu_initialize()
std::optional<scoped_progress_dialog> progr(std::in_place, "Analyzing PPU Executable...");
// Analyse executable
if (!_main.analyse(0, _main.elf_entry, _main.seg0_code_end, _main.applied_patches, [](){ return Emu.IsStopped(); }))
if (!_main.analyse(0, _main.elf_entry, _main.seg0_code_end, _main.applied_patches, std::vector<u32>{}, [](){ return Emu.IsStopped(); }))
{
return;
}

View File

@ -1681,7 +1681,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
{
if (auto& _main = *ensure(g_fxo->try_get<main_ppu_module>()); !_main.path.empty())
{
if (!_main.analyse(0, _main.elf_entry, _main.seg0_code_end, _main.applied_patches, [](){ return Emu.IsStopped(); }))
if (!_main.analyse(0, _main.elf_entry, _main.seg0_code_end, _main.applied_patches, std::vector<u32>{}, [](){ return Emu.IsStopped(); }))
{
return;
}