PPU LLVM: Implement SELF precompilation

Do not use PS3 memory for precompilation.
This commit is contained in:
Eladash 2023-06-25 15:53:42 +03:00 committed by Ivan
parent 7062ead4fd
commit 554b27a82a
11 changed files with 459 additions and 214 deletions

View File

@ -805,7 +805,7 @@ void unmap_vm_area(std::shared_ptr<vm::block_t>& ptr)
}
// Returns old 'applied' size
static usz apply_modification(std::basic_string<u32>& applied, patch_engine::patch_info& patch, u8* dst, u32 filesz, u32 min_addr)
static usz apply_modification(std::basic_string<u32>& applied, patch_engine::patch_info& patch, std::function<u8*(u32)> mem_translate, u32 filesz, u32 min_addr)
{
const usz old_applied_size = applied.size();
@ -846,8 +846,8 @@ static usz apply_modification(std::basic_string<u32>& applied, patch_engine::pat
{
if (p.type != patch_type::alloc) continue;
// Do not allow null address or if dst is not a VM ptr
if (const u32 alloc_at = vm::try_get_addr(dst + (p.offset & -4096)).first; alloc_at >> 16)
// Do not allow null address or if resultant ptr is not a VM ptr
if (const u32 alloc_at = vm::try_get_addr(mem_translate(p.offset & -4096)).first; alloc_at >> 16)
{
const u32 alloc_size = utils::align(static_cast<u32>(p.value.long_value) + alloc_at % 4096, 4096);
@ -934,7 +934,13 @@ static usz apply_modification(std::basic_string<u32>& applied, patch_engine::pat
offset -= min_addr;
auto ptr = dst + offset;
auto ptr = mem_translate(offset);
if (!ptr)
{
// Memory translation failed
continue;
}
if (relocate_instructions_at)
{
@ -960,7 +966,7 @@ static usz apply_modification(std::basic_string<u32>& applied, patch_engine::pat
}
case patch_type::code_alloc:
{
const u32 out_branch = vm::try_get_addr(dst + (offset & -4)).first;
const u32 out_branch = vm::try_get_addr(mem_translate(offset & -4)).first;
// Allow only if points to a PPU executable instruction
if (out_branch < 0x10000 || out_branch >= 0x4000'0000 || !vm::check_addr<4>(out_branch, vm::page_executable))
@ -1044,7 +1050,7 @@ static usz apply_modification(std::basic_string<u32>& applied, patch_engine::pat
case patch_type::jump:
case patch_type::jump_link:
{
const u32 out_branch = vm::try_get_addr(dst + (offset & -4)).first;
const u32 out_branch = vm::try_get_addr(mem_translate(offset & -4)).first;
const u32 dest = static_cast<u32>(p.value.long_value);
// Allow only if points to a PPU executable instruction
@ -1060,7 +1066,7 @@ static usz apply_modification(std::basic_string<u32>& applied, patch_engine::pat
{
const std::string& str = p.original_value;
const u32 out_branch = vm::try_get_addr(dst + (offset & -4)).first;
const u32 out_branch = vm::try_get_addr(mem_translate(offset & -4)).first;
const usz sep_pos = str.find_first_of(':');
// Must contain only a single ':' or none
@ -1251,7 +1257,7 @@ static usz apply_modification(std::basic_string<u32>& applied, patch_engine::pat
return old_applied_size;
}
std::basic_string<u32> patch_engine::apply(const std::string& name, u8* dst, u32 filesz, u32 min_addr)
std::basic_string<u32> patch_engine::apply(const std::string& name, std::function<u8*(u32)> mem_translate, u32 filesz, u32 min_addr)
{
if (!m_map.contains(name))
{
@ -1392,7 +1398,7 @@ std::basic_string<u32> patch_engine::apply(const std::string& name, u8* dst, u32
{
if (patch)
{
const usz old_size = apply_modification(applied_total, *patch, dst, filesz, min_addr);
const usz old_size = apply_modification(applied_total, *patch, mem_translate, filesz, min_addr);
if (applied_total.size() != old_size)
{

View File

@ -3,6 +3,7 @@
#include <vector>
#include <string>
#include <unordered_map>
#include <functional>
#include "util/types.hpp"
#include "util/yaml.hpp"
@ -212,7 +213,7 @@ public:
void append_title_patches(const std::string& title_id);
// Apply patch (returns the number of entries applied)
std::basic_string<u32> apply(const std::string& name, u8* dst, u32 filesz = -1, u32 min_addr = 0);
std::basic_string<u32> apply(const std::string& name, std::function<u8*(u32)> mem_translate, u32 filesz = -1, u32 min_addr = 0);
// Deallocate memory used by patches
void unload(const std::string& name);

View File

@ -78,7 +78,7 @@ void ppu_module::validate(u32 reloc)
if (size && size != funcs[index].size)
{
if (size + 4 != funcs[index].size || vm::read32(addr + size) != ppu_instructions::NOP())
if (size + 4 != funcs[index].size || *ensure(get_ptr<u32>(addr + size)) != ppu_instructions::NOP())
{
ppu_validator.error("%s.yml : function size mismatch at 0x%x(size=0x%x) (0x%x, 0x%x)", path, found, funcs[index].size, addr, size);
}
@ -112,9 +112,9 @@ void ppu_module::validate(u32 reloc)
}
}
static u32 ppu_test(const vm::cptr<u32> ptr, vm::cptr<void> fend, ppu_pattern_array pat)
static u32 ppu_test(const be_t<u32>* ptr, const void* fend, ppu_pattern_array pat)
{
vm::cptr<u32> cur = ptr;
const be_t<u32>* cur = ptr;
for (auto& p : pat)
{
@ -141,10 +141,10 @@ static u32 ppu_test(const vm::cptr<u32> ptr, vm::cptr<void> fend, ppu_pattern_ar
cur++;
}
return cur.addr() - ptr.addr();
return (cur - ptr) * sizeof(*ptr);
}
static u32 ppu_test(vm::cptr<u32> ptr, vm::cptr<void> fend, ppu_pattern_matrix pats)
static u32 ppu_test(const be_t<u32>* ptr, const void* fend, ppu_pattern_matrix pats)
{
for (auto pat : pats)
{
@ -585,6 +585,14 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
return func;
};
static const auto advance = [](auto& _ptr, auto& ptr, u32 count)
{
const auto old_ptr = ptr;
_ptr += count;
ptr += count;
return old_ptr;
};
// Register new TOC and find basic set of functions
auto add_toc = [&](u32 toc)
{
@ -596,16 +604,24 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
// Grope for OPD section (TODO: optimization, better constraints)
for (const auto& seg : segs)
{
if (!seg.addr) continue;
if (seg.size < 8) continue;
for (vm::cptr<u32> ptr = vm::cast(seg.addr); ptr.addr() < seg.addr + seg.size; ptr++)
const vm::cptr<void> seg_end = vm::cast(seg.addr + seg.size - 8);
vm::cptr<u32> _ptr = vm::cast(seg.addr);
auto ptr = get_ptr<u32>(_ptr);
for (; _ptr <= seg_end;)
{
if (ptr[0] >= start && ptr[0] < end && ptr[0] % 4 == 0 && ptr[1] == toc)
if (ptr[1] == toc && ptr[0] >= start && ptr[0] < end && ptr[0] % 4 == 0)
{
// New function
ppu_log.trace("OPD*: [0x%x] 0x%x (TOC=0x%x)", ptr, ptr[0], ptr[1]);
add_func(*ptr, addr_heap.count(ptr.addr()) ? toc : 0, 0);
ptr++;
ppu_log.trace("OPD*: [0x%x] 0x%x (TOC=0x%x)", _ptr, ptr[0], ptr[1]);
add_func(*ptr, addr_heap.count(_ptr.addr()) ? toc : 0, 0);
advance(_ptr, ptr, 2);
}
else
{
advance(_ptr, ptr, 1);
}
}
}
@ -621,9 +637,13 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
// Find references indiscriminately
for (const auto& seg : segs)
{
if (!seg.addr) continue;
if (seg.size < 4) continue;
for (vm::cptr<u32> ptr = vm::cast(seg.addr); ptr.addr() < seg.addr + seg.size; ptr++)
vm::cptr<u32> _ptr = vm::cast(seg.addr);
const vm::cptr<void> seg_end = vm::cast(seg.addr + seg.size - 4);
auto ptr = get_ptr<u32>(_ptr);
for (vm::cptr<u32> _ptr = vm::cast(seg.addr); _ptr <= seg_end; advance(_ptr, ptr, 1))
{
const u32 value = *ptr;
@ -651,15 +671,17 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
vm::cptr<void> sec_end = vm::cast(sec.addr + sec.size);
// Probe
for (vm::cptr<u32> ptr = vm::cast(sec.addr); ptr < sec_end; ptr += 2)
for (vm::cptr<u32> _ptr = vm::cast(sec.addr); _ptr < sec_end; _ptr += 2)
{
if (ptr + 6 <= sec_end && !ptr[0] && !ptr[2] && ptr[1] == ptr[4] && ptr[3] == ptr[5])
auto ptr = get_ptr<u32>(_ptr);
if (_ptr + 6 <= sec_end && !ptr[0] && !ptr[2] && ptr[1] == ptr[4] && ptr[3] == ptr[5])
{
// Special OPD format case (some homebrews)
ptr += 4;
advance(_ptr, ptr, 4);
}
if (ptr + 2 > sec_end)
if (_ptr + 2 > sec_end)
{
sec_end.set(0);
break;
@ -673,7 +695,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
//const u32 _toc_end = _toc + 0x8000;
// TODO: improve TOC constraints
if (_toc % 4 || !vm::check_addr(_toc) || _toc >= 0x40000000 || (_toc >= start && _toc < end))
if (_toc % 4 || !get_ptr<u32>(_toc) || _toc >= 0x40000000 || (_toc >= start && _toc < end))
{
sec_end.set(0);
break;
@ -689,18 +711,20 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
if (sec_end) ppu_log.notice("Reading OPD section at 0x%x...", sec.addr);
// Mine
for (vm::cptr<u32> ptr = vm::cast(sec.addr); ptr < sec_end; ptr += 2)
for (vm::cptr<u32> _ptr = vm::cast(sec.addr); _ptr < sec_end; _ptr += 2)
{
auto ptr = get_ptr<u32>(_ptr);
// Special case: see "Probe"
if (!ptr[0]) ptr += 4;
if (!ptr[0]) advance(_ptr, ptr, 4);
// Add function and TOC
const u32 addr = ptr[0];
const u32 toc = ptr[1];
ppu_log.trace("OPD: [0x%x] 0x%x (TOC=0x%x)", ptr, addr, toc);
ppu_log.trace("OPD: [0x%x] 0x%x (TOC=0x%x)", _ptr, addr, toc);
TOCs.emplace(toc);
auto& func = add_func(addr, addr_heap.count(ptr.addr()) ? toc : 0, 0);
auto& func = add_func(addr, addr_heap.count(_ptr.addr()) ? toc : 0, 0);
func.attr += ppu_attr::known_addr;
known_functions.emplace(addr);
}
@ -709,7 +733,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
// Register TOC from entry point
if (entry && !lib_toc)
{
lib_toc = vm::read32(entry) ? vm::read32(entry + 4) : vm::read32(entry + 20);
lib_toc = *ensure(get_ptr<u32>(entry)) ? *ensure(get_ptr<u32>(entry + 4)) : *ensure(get_ptr<u32>(entry + 20));
}
// Secondary attempt
@ -733,23 +757,25 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
vm::cptr<void> sec_end = vm::cast(sec.addr + sec.size);
// Probe
for (vm::cptr<u32> ptr = vm::cast(sec.addr); ptr < sec_end;)
for (vm::cptr<u32> _ptr = vm::cast(sec.addr); _ptr < sec_end;)
{
if (!ptr.aligned() || ptr.addr() < sec.addr || ptr >= sec_end)
if (!_ptr.aligned() || _ptr.addr() < sec.addr || _ptr >= sec_end)
{
sec_end.set(0);
break;
}
const auto ptr = get_ptr<u32>(_ptr);
const u32 size = ptr[0] + 4;
if (size == 4 && ptr + 1 == sec_end)
if (size == 4 && _ptr + 1 == sec_end)
{
// Null terminator
break;
}
if (size % 4 || size < 0x10 || ptr + size / 4 > sec_end)
if (size % 4 || size < 0x10 || _ptr + size / 4 > sec_end)
{
sec_end.set(0);
break;
@ -757,7 +783,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
if (ptr[1])
{
const u32 cie_off = ptr.addr() - ptr[1] + 4;
const u32 cie_off = _ptr.addr() - ptr[1] + 4;
if (cie_off % 4 || cie_off < sec.addr || cie_off >= sec_end.addr())
{
@ -766,14 +792,16 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
}
}
ptr = vm::cast(ptr.addr() + size);
_ptr = vm::cast(_ptr.addr() + size);
}
if (sec_end && sec.size > 4) ppu_log.notice("Reading .eh_frame section at 0x%x...", sec.addr);
// Mine
for (vm::cptr<u32> ptr = vm::cast(sec.addr); ptr < sec_end; ptr = vm::cast(ptr.addr() + ptr[0] + 4))
for (vm::cptr<u32> _ptr = vm::cast(sec.addr); _ptr < sec_end; _ptr = vm::cast(_ptr.addr() + *get_ptr<u32>(_ptr) + 4))
{
const auto ptr = get_ptr<u32>(_ptr);
if (ptr[0] == 0u)
{
// Null terminator
@ -788,7 +816,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
else
{
// Get associated CIE (currently unused)
const vm::cptr<u32> cie = vm::cast(ptr.addr() - ptr[1] + 4);
const vm::cptr<u32> cie = vm::cast(_ptr.addr() - ptr[1] + 4);
u32 addr = 0;
u32 size = 0;
@ -817,7 +845,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
// TODO: absolute/relative offset (approximation)
if (addr > 0xc0000000)
{
addr += ptr.addr() + 8;
addr += _ptr.addr() + 8;
}
ppu_log.trace(".eh_frame: [0x%x] FDE 0x%x (cie=*0x%x, addr=0x%x, size=0x%x)", ptr, ptr[0], cie, addr, size);
@ -875,15 +903,16 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
if (func.blocks.empty())
{
// Special function analysis
const vm::cptr<u32> ptr = vm::cast(func.addr);
const vm::cptr<u32> _ptr = vm::cast(func.addr);
const vm::cptr<void> fend = vm::cast(end);
const auto ptr = get_ptr<u32>(_ptr);
using namespace ppu_instructions;
if (ptr + 1 <= fend && (ptr[0] & 0xfc000001) == B({}, {}))
if (_ptr + 1 <= fend && (ptr[0] & 0xfc000001) == B({}, {}))
{
// Simple trampoline
const u32 target = (ptr[0] & 0x2 ? 0 : ptr.addr()) + ppu_opcode_t{ptr[0]}.bt24;
const u32 target = (ptr[0] & 0x2 ? 0 : _ptr.addr()) + ppu_opcode_t{ptr[0]}.bt24;
if (target == func.addr)
{
@ -913,7 +942,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
}
}
if (ptr + 0x4 <= fend &&
if (_ptr + 0x4 <= fend &&
(ptr[0] & 0xffff0000) == LIS(r11, 0) &&
(ptr[1] & 0xffff0000) == ADDI(r11, r11, 0) &&
ptr[2] == MTCTR(r11) &&
@ -941,7 +970,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
}
}
if (ptr + 0x7 <= fend &&
if (_ptr + 0x7 <= fend &&
ptr[0] == STD(r2, r1, 0x28) &&
(ptr[1] & 0xffff0000) == ADDIS(r12, r2, {}) &&
(ptr[2] & 0xffff0000) == LWZ(r11, r12, {}) &&
@ -957,9 +986,10 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
func.attr += ppu_attr::known_size;
// Look for another imports to fill gaps (hack)
auto p2 = ptr + 7;
auto _p2 = _ptr + 7;
auto p2 = get_ptr<u32>(_p2);
while (p2 + 0x7 <= fend &&
while (_p2 + 0x7 <= fend &&
p2[0] == STD(r2, r1, 0x28) &&
(p2[1] & 0xffff0000) == ADDIS(r12, r2, {}) &&
(p2[2] & 0xffff0000) == LWZ(r11, r12, {}) &&
@ -968,18 +998,18 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
p2[5] == MTCTR(r11) &&
p2[6] == BCTR())
{
auto& next = add_func(p2.addr(), -1, func.addr);
auto& next = add_func(_p2.addr(), -1, func.addr);
next.size = 0x1C;
next.blocks.emplace(next.addr, next.size);
next.attr += ppu_attr::known_addr;
next.attr += ppu_attr::known_size;
p2 += 7;
advance(_p2, p2, 7);
}
continue;
}
if (ptr + 0x7 <= fend &&
if (_ptr + 0x7 <= fend &&
ptr[0] == STD(r2, r1, 0x28) &&
(ptr[1] & 0xffff0000) == ADDIS(r2, r2, {}) &&
(ptr[2] & 0xffff0000) == ADDI(r2, r2, {}) &&
@ -1029,7 +1059,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
}
}
if (ptr + 4 <= fend &&
if (_ptr + 4 <= fend &&
ptr[0] == STD(r2, r1, 0x28) &&
(ptr[1] & 0xffff0000) == ADDIS(r2, r2, {}) &&
(ptr[2] & 0xffff0000) == ADDI(r2, r2, {}) &&
@ -1037,7 +1067,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
{
// Trampoline with TOC
const u32 toc_add = (ptr[1] << 16) + s16(ptr[2]);
const u32 target = (ptr[3] & 0x2 ? 0 : (ptr + 3).addr()) + ppu_opcode_t{ptr[3]}.bt24;
const u32 target = (ptr[3] & 0x2 ? 0 : (_ptr + 3).addr()) + ppu_opcode_t{ptr[3]}.bt24;
if (target >= start && target < end)
{
@ -1076,7 +1106,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
}
}
if (ptr + 8 <= fend &&
if (_ptr + 8 <= fend &&
(ptr[0] & 0xffff0000) == LI(r12, 0) &&
(ptr[1] & 0xffff0000) == ORIS(r12, r12, 0) &&
(ptr[2] & 0xffff0000) == LWZ(r12, r12, 0) &&
@ -1095,9 +1125,10 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
func.attr += ppu_attr::known_size;
// Look for another imports to fill gaps (hack)
auto p2 = ptr + 8;
auto _p2 = _ptr + 8;
auto p2 = get_ptr<u32>(_p2);
while (p2 + 8 <= fend &&
while (_p2 + 8 <= fend &&
(p2[0] & 0xffff0000) == LI(r12, 0) &&
(p2[1] & 0xffff0000) == ORIS(r12, r12, 0) &&
(p2[2] & 0xffff0000) == LWZ(r12, r12, 0) &&
@ -1107,19 +1138,19 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
p2[6] == MTCTR(r0) &&
p2[7] == BCTR())
{
auto& next = add_func(p2.addr(), -1, func.addr);
auto& next = add_func(_p2.addr(), -1, func.addr);
next.size = 0x20;
next.blocks.emplace(next.addr, next.size);
next.attr += ppu_attr::known_addr;
next.attr += ppu_attr::known_size;
p2 += 8;
advance(_p2, p2, 8);
known_functions.emplace(next.addr);
}
continue;
}
if (ptr + 3 <= fend &&
if (_ptr + 3 <= fend &&
ptr[0] == 0x7c0004acu &&
ptr[1] == 0x00000000u &&
ptr[2] == BLR())
@ -1131,7 +1162,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
continue;
}
if (const u32 len = ppu_test(ptr, fend, ppu_patterns::abort))
if (const u32 len = ppu_test(ptr, get_ptr<void>(fend), ppu_patterns::abort))
{
// Function "abort"
ppu_log.notice("Function [0x%x]: 'abort'", func.addr);
@ -1199,10 +1230,13 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
{
auto& block = block_queue[j].get();
for (vm::cptr<u32> _ptr = vm::cast(block.first); _ptr.addr() < func_end;)
vm::cptr<u32> _ptr = vm::cast(block.first);
auto ptr = ensure(get_ptr<u32>(_ptr));
for (; _ptr.addr() < func_end;)
{
const u32 iaddr = _ptr.addr();
const ppu_opcode_t op{*_ptr++};
const ppu_opcode_t op{*advance(_ptr, ptr, 1)};
const ppu_itype::type type = s_ppu_itype.decode(op.opcode);
if (type == ppu_itype::UNK)
@ -1276,9 +1310,9 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
const u32 jt_addr = _ptr.addr();
const u32 jt_end = func_end;
for (; _ptr.addr() < jt_end; _ptr++)
for (; _ptr.addr() < jt_end; advance(_ptr, ptr, 1))
{
const u32 addr = jt_addr + *_ptr;
const u32 addr = jt_addr + *ptr;
if (addr == jt_addr)
{
@ -1331,7 +1365,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
block.second = _ptr.addr() - block.first;
break;
}
else if (type == ppu_itype::STDU && func.attr & ppu_attr::no_size && (op.opcode == *_ptr || *_ptr == ppu_instructions::BLR()))
else if (type == ppu_itype::STDU && func.attr & ppu_attr::no_size && (op.opcode == *ptr || *ptr == ppu_instructions::BLR()))
{
// Hack
ppu_log.success("[0x%x] Instruction repetition: 0x%08x", iaddr, op.opcode);
@ -1391,7 +1425,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
for (vm::cptr<u32> _ptr = vm::cast(block.first); _ptr.addr() < block.first + block.second;)
{
const u32 iaddr = _ptr.addr();
const ppu_opcode_t op{*_ptr++};
const ppu_opcode_t op{*ensure(get_ptr<u32>(_ptr++))};
const ppu_itype::type type = s_ppu_itype.decode(op.opcode);
if (type == ppu_itype::B || type == ppu_itype::BC)
@ -1465,7 +1499,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
for (vm::cptr<u32> _ptr = vm::cast(start); _ptr.addr() < next;)
{
const u32 addr = _ptr.addr();
const ppu_opcode_t op{*_ptr++};
const ppu_opcode_t op{*ensure(get_ptr<u32>(_ptr++))};
const ppu_itype::type type = s_ppu_itype.decode(op.opcode);
if (type == ppu_itype::UNK)
@ -1607,7 +1641,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
continue;
}
const u32 target = vm::_ref<u32>(rel.addr);
const u32 target = *ensure(get_ptr<u32>(rel.addr));
if (target % 4 || target < start || target >= end)
{
@ -1684,7 +1718,7 @@ bool ppu_module::analyse(u32 lib_toc, u32 entry, const u32 sec_end, const std::b
for (; i_pos < lim; i_pos += 4)
{
const u32 opc = vm::_ref<u32>(i_pos);
const u32 opc = *ensure(get_ptr<u32>(i_pos));
switch (auto type = s_ppu_itype.decode(opc))
{

View File

@ -3,8 +3,10 @@
#include <string>
#include <map>
#include <set>
#include <deque>
#include "util/types.hpp"
#include "util/endian.hpp"
#include "util/to_endian.hpp"
#include "Utilities/bit_set.h"
#include "PPUOpcodes.h"
@ -65,6 +67,7 @@ struct ppu_segment
u32 type;
u32 flags;
u32 filesz;
void* ptr{};
};
// PPU Module Information
@ -89,6 +92,8 @@ struct ppu_module
std::vector<ppu_segment> segs{};
std::vector<ppu_segment> secs{};
std::vector<ppu_function> funcs{};
std::deque<std::shared_ptr<void>> allocations;
std::map<u32, u32> addr_to_seg_index;
// Copy info without functions
void copy_part(const ppu_module& info)
@ -99,10 +104,44 @@ struct ppu_module
relocs = info.relocs;
segs = info.segs;
secs = info.secs;
allocations = info.allocations;
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 = {});
void validate(u32 reloc);
template <typename T>
to_be_t<T>* get_ptr(u32 addr) const
{
auto it = addr_to_seg_index.upper_bound(addr);
if (it == addr_to_seg_index.begin())
{
return nullptr;
}
it--;
const auto& seg = segs[it->second];
const u32 seg_size = seg.size;
const u32 seg_addr = seg.addr;
constexpr usz size_element = std::is_void_v<T> ? 0 : sizeof(std::conditional_t<std::is_void_v<T>, char, T>);
if (seg_size >= std::max<usz>(size_element, 1) && addr <= seg_addr + seg_size - size_element)
{
return reinterpret_cast<to_be_t<T>*>(static_cast<u8*>(seg.ptr) + (addr - seg_addr));
}
return nullptr;
}
template <typename T, typename U> requires requires (const U& obj) { +obj.addr() * 0; }
to_be_t<T>* get_ptr(U&& addr) const
{
return get_ptr<T>(addr.addr());
}
};
struct main_ppu_module : public ppu_module

View File

@ -754,7 +754,7 @@ static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 expo
if (lib.num_tlsvar)
{
ppu_loader.fatal("Unexpected num_tlsvar (%u)!", lib.num_tlsvar);
ppu_loader.error("Unexpected num_tlsvar (%u)!", lib.num_tlsvar);
}
const bool should_load = ppu_register_library_lock(module_name, true);
@ -889,7 +889,7 @@ static auto ppu_load_imports(std::vector<ppu_reloc>& relocs, ppu_linkage_info* l
if (lib.num_tlsvar)
{
ppu_loader.fatal("Unexpected num_tlsvar (%u)!", lib.num_tlsvar);
ppu_loader.error("Unexpected num_tlsvar (%u)!", lib.num_tlsvar);
}
// Static module
@ -1056,13 +1056,18 @@ void init_ppu_functions(utils::serial* ar, bool full = false)
}
}
static void ppu_check_patch_spu_images(const ppu_segment& seg)
static void ppu_check_patch_spu_images(const ppu_module& mod, const ppu_segment& seg)
{
const std::string_view seg_view{vm::get_super_ptr<char>(seg.addr), seg.size};
if (!seg.size)
{
return;
}
const std::string_view seg_view{ensure(mod.get_ptr<char>(seg.addr)), seg.size};
for (usz 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);
const auto elf_header = ensure(mod.get_ptr<u8>(seg.addr + i));
// Try to load SPU image
const spu_exec_object obj(fs::file(elf_header, seg.size - i));
@ -1103,7 +1108,7 @@ static void ppu_check_patch_spu_images(const ppu_segment& seg)
sha1_update(&sha2, (elf_header + prog.p_offset), prog.p_filesz);
// We assume that the string SPUNAME exists 0x14 bytes into the NOTE segment
name = reinterpret_cast<char*>(elf_header + prog.p_offset + 0x14);
name = ensure(mod.get_ptr<const char>(seg.addr + i + prog.p_offset + 0x14));
if (!name.empty())
{
@ -1137,12 +1142,12 @@ static void ppu_check_patch_spu_images(const ppu_segment& seg)
for (const auto& prog : obj.progs)
{
// Apply the patch
applied += g_fxo->get<patch_engine>().apply(hash, (elf_header + prog.p_offset), prog.p_filesz, prog.p_vaddr);
applied += g_fxo->get<patch_engine>().apply(hash, [&](u32 addr) { return addr + elf_header + prog.p_offset; }, prog.p_filesz, prog.p_vaddr);
if (!Emu.GetTitleID().empty())
{
// Alternative patch
applied += g_fxo->get<patch_engine>().apply(Emu.GetTitleID() + '-' + hash, (elf_header + prog.p_offset), prog.p_filesz, prog.p_vaddr);
applied += g_fxo->get<patch_engine>().apply(Emu.GetTitleID() + '-' + hash, [&](u32 addr) { return addr + elf_header + prog.p_offset; }, prog.p_filesz, prog.p_vaddr);
}
}
@ -1248,7 +1253,7 @@ const char* get_prx_name_by_cia(u32 addr)
return nullptr;
}
std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::string& path, s64 file_offset, utils::serial* ar)
std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, bool virtual_load, const std::string& path, s64 file_offset, utils::serial* ar)
{
if (elf != elf_error::ok)
{
@ -1256,7 +1261,7 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
}
// Create new PRX object
const auto prx = !ar ? idm::make_ptr<lv2_obj, lv2_prx>() : std::make_shared<lv2_prx>();
const auto prx = !ar && !virtual_load ? idm::make_ptr<lv2_obj, lv2_prx>() : std::make_shared<lv2_prx>();
// Access linkage information object
auto& link = g_fxo->get<ppu_linkage_info>();
@ -1271,6 +1276,9 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
u32 end = 0;
u32 toc = 0;
// 0x100000: Workaround for analyser glitches
u32 allocating_address = 0x100000;
for (const auto& prog : elf.progs)
{
ppu_loader.notice("** Segment: p_type=0x%x, p_vaddr=0x%llx, p_filesz=0x%llx, p_memsz=0x%llx, flags=0x%x", prog.p_type, prog.p_vaddr, prog.p_filesz, prog.p_memsz, prog.p_flags);
@ -1295,15 +1303,42 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
// Alloc segment memory
// Or use saved address
const u32 addr = !ar ? vm::alloc(mem_size, vm::main) : ar->operator u32();
u32 addr = 0;
if (!vm::check_addr(addr))
if (virtual_load)
{
addr = std::exchange(allocating_address, allocating_address + utils::align<u32>(mem_size, 0x10000));
}
else
{
addr = (!ar ? vm::alloc(mem_size, vm::main) : ar->operator u32());
}
_seg.ptr = vm::base(addr);
if (virtual_load)
{
// Leave additional room for the analyser so it can safely access beyond limit a bit
// Because with VM the address sapce is not really a limit so any u32 address is valid there, here it is UB to create pointer that goes beyond the boundaries
// TODO: Use make_shared_for_overwrite when all compilers support it
const usz alloc_size = utils::align<usz>(mem_size, 0x10000) + 4096;
prx->allocations.push_back(std::shared_ptr<u8[]>(new u8[alloc_size]));
_seg.ptr = prx->allocations.back().get();
std::memset(static_cast<u8*>(_seg.ptr) + prog.bin.size(), 0, alloc_size - 4096 - prog.bin.size());
}
else if (!vm::check_addr(addr))
{
fmt::throw_exception("vm::alloc() failed (size=0x%x)", mem_size);
}
_seg.addr = addr;
_seg.size = mem_size;
_seg.filesz = file_size;
prx->addr_to_seg_index.emplace(addr, prx->segs.size() - 1);
// Copy segment data
if (!ar) std::memcpy(vm::base(addr), prog.bin.data(), file_size);
if (!ar) std::memcpy(ensure(prx->get_ptr<void>(addr)), prog.bin.data(), file_size);
ppu_loader.warning("**** Loaded to 0x%x...0x%x (size=0x%x)", addr, addr + mem_size - 1, mem_size);
// Hash segment
@ -1312,7 +1347,7 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
sha1_update(&sha, prog.bin.data(), prog.bin.size());
// Initialize executable code if necessary
if (prog.p_flags & 0x1)
if (prog.p_flags & 0x1 && !virtual_load)
{
if (ar)
{
@ -1322,10 +1357,6 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
ppu_register_range(addr, mem_size);
}
_seg.addr = addr;
_seg.size = mem_size;
_seg.filesz = file_size;
}
break;
@ -1421,63 +1452,63 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
{
case 1: // R_PPC64_ADDR32
{
const u32 value = vm::_ref<u32>(raddr) = static_cast<u32>(rdata);
const u32 value = *ensure(prx->get_ptr<u32>(raddr)) = static_cast<u32>(rdata);
ppu_loader.trace("**** RELOCATION(1): 0x%x <- 0x%08x (0x%llx)", raddr, value, rdata);
break;
}
case 4: //R_PPC64_ADDR16_LO
{
const u16 value = vm::_ref<u16>(raddr) = static_cast<u16>(rdata);
const u16 value = *ensure(prx->get_ptr<u16>(raddr)) = static_cast<u16>(rdata);
ppu_loader.trace("**** RELOCATION(4): 0x%x <- 0x%04x (0x%llx)", raddr, value, rdata);
break;
}
case 5: //R_PPC64_ADDR16_HI
{
const u16 value = vm::_ref<u16>(raddr) = static_cast<u16>(rdata >> 16);
const u16 value = *ensure(prx->get_ptr<u16>(raddr)) = static_cast<u16>(rdata >> 16);
ppu_loader.trace("**** RELOCATION(5): 0x%x <- 0x%04x (0x%llx)", raddr, value, rdata);
break;
}
case 6: //R_PPC64_ADDR16_HA
{
const u16 value = vm::_ref<u16>(raddr) = static_cast<u16>(rdata >> 16) + (rdata & 0x8000 ? 1 : 0);
const u16 value = *ensure(prx->get_ptr<u16>(raddr)) = static_cast<u16>(rdata >> 16) + (rdata & 0x8000 ? 1 : 0);
ppu_loader.trace("**** RELOCATION(6): 0x%x <- 0x%04x (0x%llx)", raddr, value, rdata);
break;
}
case 10: //R_PPC64_REL24
{
const u32 value = vm::_ref<ppu_bf_t<be_t<u32>, 6, 24>>(raddr) = static_cast<u32>(rdata - raddr) >> 2;
const u32 value = *ensure(prx->get_ptr<ppu_bf_t<be_t<u32>, 6, 24>>(raddr)) = static_cast<u32>(rdata - raddr) >> 2;
ppu_loader.warning("**** RELOCATION(10): 0x%x <- 0x%06x (0x%llx)", raddr, value, rdata);
break;
}
case 11: //R_PPC64_REL14
{
const u32 value = vm::_ref<ppu_bf_t<be_t<u32>, 16, 14>>(raddr) = static_cast<u32>(rdata - raddr) >> 2;
const u32 value = *ensure(prx->get_ptr<ppu_bf_t<be_t<u32>, 16, 14>>(raddr)) = static_cast<u32>(rdata - raddr) >> 2;
ppu_loader.warning("**** RELOCATION(11): 0x%x <- 0x%06x (0x%llx)", raddr, value, rdata);
break;
}
case 38: //R_PPC64_ADDR64
{
const u64 value = vm::_ref<u64>(raddr) = rdata;
const u64 value = *ensure(prx->get_ptr<u64>(raddr)) = rdata;
ppu_loader.trace("**** RELOCATION(38): 0x%x <- 0x%016llx (0x%llx)", raddr, value, rdata);
break;
}
case 44: //R_PPC64_REL64
{
const u64 value = vm::_ref<u64>(raddr) = rdata - raddr;
const u64 value = *ensure(prx->get_ptr<u64>(raddr)) = rdata - raddr;
ppu_loader.trace("**** RELOCATION(44): 0x%x <- 0x%016llx (0x%llx)", raddr, value, rdata);
break;
}
case 57: //R_PPC64_ADDR16_LO_DS
{
const u16 value = vm::_ref<ppu_bf_t<be_t<u16>, 0, 14>>(raddr) = static_cast<u16>(rdata) >> 2;
const u16 value = *ensure(prx->get_ptr<ppu_bf_t<be_t<u16>, 0, 14>>(raddr)) = static_cast<u16>(rdata) >> 2;
ppu_loader.trace("**** RELOCATION(57): 0x%x <- 0x%04x (0x%llx)", raddr, value, rdata);
break;
}
@ -1512,7 +1543,7 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
};
// Access library information (TODO)
const vm::cptr<ppu_prx_library_info> lib_info = vm::cast(prx->segs[0].addr + elf.progs[0].p_paddr - elf.progs[0].p_offset);
const auto lib_info = ensure(prx->get_ptr<const ppu_prx_library_info>(prx->segs[0].addr + elf.progs[0].p_paddr - elf.progs[0].p_offset));
const std::string lib_name = lib_info->name;
strcpy_trunc(prx->module_info_name, lib_name);
@ -1523,7 +1554,7 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
prx->exports_start = lib_info->exports_start;
prx->exports_end = lib_info->exports_end;
for (usz start = prx->exports_start, size = 0;; size++, start += vm::read8(start) ? vm::read8(start) : sizeof(ppu_prx_module_info))
for (usz start = prx->exports_start, size = 0;; size++)
{
if (start >= prx->exports_end)
{
@ -1531,18 +1562,25 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
prx->m_external_loaded_flags.resize(size);
break;
}
const u8 increment = *ensure(prx->get_ptr<u8>(start));
start += increment ? increment : sizeof(ppu_prx_module_info);
}
ppu_loader.warning("Library %s (rtoc=0x%x):", lib_name, lib_info->toc);
prx->specials = ppu_load_exports(&link, prx->exports_start, prx->exports_end, true);
prx->imports = ppu_load_imports(prx->relocs, &link, lib_info->imports_start, lib_info->imports_end);
if (!virtual_load)
{
prx->specials = ppu_load_exports(&link, prx->exports_start, prx->exports_end, true);
prx->imports = ppu_load_imports(prx->relocs, &link, lib_info->imports_start, lib_info->imports_end);
}
std::stable_sort(prx->relocs.begin(), prx->relocs.end());
toc = lib_info->toc;
}
else
{
ppu_loader.fatal("Library %s: PRX library info not found");
ppu_loader.error("Library %s: PRX library info not found");
}
prx->start.set(prx->specials[0xbc9a0086]);
@ -1579,12 +1617,12 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
const std::string hash_seg = fmt::format("%s-%u", hash, i);
// Apply the patch
auto _applied = g_fxo->get<patch_engine>().apply(hash_seg, vm::get_super_ptr(seg.addr), seg.size);
auto _applied = g_fxo->get<patch_engine>().apply(hash_seg, [&](u32 addr) { return prx->get_ptr<u8>(addr + seg.addr); }, seg.size);
if (!Emu.GetTitleID().empty())
{
// Alternative patch
_applied += g_fxo->get<patch_engine>().apply(Emu.GetTitleID() + '-' + hash_seg, vm::get_super_ptr(seg.addr), seg.size);
_applied += g_fxo->get<patch_engine>().apply(Emu.GetTitleID() + '-' + hash_seg, [&](u32 addr) { return prx->get_ptr<u8>(addr + seg.addr); }, seg.size);
}
// Rebase patch offsets
@ -1605,12 +1643,15 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
// Embedded SPU elf patching
for (const auto& seg : prx->segs)
{
ppu_check_patch_spu_images(seg);
ppu_check_patch_spu_images(*prx, seg);
}
prx->analyse(toc, 0, end, applied);
try_spawn_ppu_if_exclusive_program(*prx);
if (!ar && !virtual_load)
{
try_spawn_ppu_if_exclusive_program(*prx);
}
return prx;
}
@ -1667,7 +1708,10 @@ void ppu_unload_prx(const lv2_prx& prx)
{
if (!seg.size) continue;
vm::dealloc(seg.addr, vm::main);
if (seg.ptr == vm::base(seg.addr))
{
vm::dealloc(seg.addr, vm::main);
}
const std::string hash_seg = fmt::format("%s-%u", hash, &seg - prx.segs.data());
@ -1682,7 +1726,7 @@ void ppu_unload_prx(const lv2_prx& prx)
}
}
bool ppu_load_exec(const ppu_exec_object& elf, const std::string& elf_path, utils::serial* ar)
bool ppu_load_exec(const ppu_exec_object& elf, bool virtual_load, const std::string& elf_path, utils::serial* ar)
{
if (elf != elf_error::ok)
{
@ -1753,6 +1797,12 @@ bool ppu_load_exec(const ppu_exec_object& elf, const std::string& elf_path, util
} error_handler{_main};
if (virtual_load)
{
// No need for cleanup
error_handler.errored = false;
}
// Allocate memory at fixed positions
for (const auto& prog : elf.progs)
{
@ -1774,13 +1824,25 @@ bool ppu_load_exec(const ppu_exec_object& elf, const std::string& elf_path, util
{
if (prog.bin.size() > size || prog.bin.size() != prog.p_filesz)
{
ppu_loader.fatal("ppu_load_exec(): Invalid binary size (0x%llx, memsz=0x%x)", prog.bin.size(), size);
ppu_loader.error("ppu_load_exec(): Invalid binary size (0x%llx, memsz=0x%x)", prog.bin.size(), size);
return false;
}
const bool already_loaded = ar && vm::check_addr(addr, vm::page_readable, size);
if (already_loaded)
_seg.ptr = vm::base(addr);
if (virtual_load)
{
// Leave additional room for the analyser so it can safely access beyond limit a bit
// Because with VM the address sapce is not really a limit so any u32 address is valid there, here it is UB to create pointer that goes beyond the boundaries
// TODO: Use make_shared_for_overwrite when all compilers support it
const usz alloc_size = utils::align<usz>(size, 0x10000) + 4096;
_main.allocations.push_back(std::shared_ptr<u8[]>(new u8[alloc_size]));
_seg.ptr = _main.allocations.back().get();
std::memset(static_cast<u8*>(_seg.ptr) + prog.bin.size(), 0, alloc_size - 4096 - prog.bin.size());
}
else if (already_loaded)
{
}
else if (!vm::falloc(addr, size, vm::main))
@ -1789,15 +1851,19 @@ bool ppu_load_exec(const ppu_exec_object& elf, const std::string& elf_path, util
if (!vm::falloc(addr, size))
{
ppu_loader.fatal("ppu_load_exec(): vm::falloc() failed (addr=0x%x, memsz=0x%x)", addr, size);
ppu_loader.error("ppu_load_exec(): vm::falloc() failed (addr=0x%x, memsz=0x%x)", addr, size);
return false;
}
}
// Store only LOAD segments (TODO)
_main.segs.emplace_back(_seg);
_main.addr_to_seg_index.emplace(addr, _main.segs.size() - 1);
// Copy segment data, hash it
if (!already_loaded)
{
std::memcpy(vm::base(addr), prog.bin.data(), prog.bin.size());
std::memcpy(_main.get_ptr<void>(addr), prog.bin.data(), prog.bin.size());
}
else
{
@ -1812,7 +1878,7 @@ bool ppu_load_exec(const ppu_exec_object& elf, const std::string& elf_path, util
sha1_update(&sha, prog.bin.data(), prog.bin.size());
// Initialize executable code if necessary
if (prog.p_flags & 0x1)
if (prog.p_flags & 0x1 && !virtual_load)
{
if (already_loaded && ar)
{
@ -1822,9 +1888,6 @@ bool ppu_load_exec(const ppu_exec_object& elf, const std::string& elf_path, util
ppu_register_range(addr, size);
}
// Store only LOAD segments (TODO)
_main.segs.emplace_back(_seg);
}
}
@ -1868,12 +1931,12 @@ bool ppu_load_exec(const ppu_exec_object& elf, const std::string& elf_path, util
Emu.SetExecutableHash(hash);
// Apply the patch
auto applied = g_fxo->get<patch_engine>().apply(!ar ? hash : std::string{}, vm::g_base_addr);
auto applied = g_fxo->get<patch_engine>().apply(!ar ? hash : std::string{}, [&](u32 addr) { return _main.get_ptr<u8>(addr); });
if (!ar && !Emu.GetTitleID().empty())
{
// Alternative patch
applied += g_fxo->get<patch_engine>().apply(Emu.GetTitleID() + '-' + hash, vm::g_base_addr);
applied += g_fxo->get<patch_engine>().apply(Emu.GetTitleID() + '-' + hash, [&](u32 addr) { return _main.get_ptr<u8>(addr); });
}
if (applied.empty())
@ -1891,11 +1954,11 @@ bool ppu_load_exec(const ppu_exec_object& elf, const std::string& elf_path, util
// Embedded SPU elf patching
for (const auto& seg : _main.segs)
{
ppu_check_patch_spu_images(seg);
ppu_check_patch_spu_images(_main, seg);
}
// Static HLE patching
if (g_cfg.core.hook_functions)
if (g_cfg.core.hook_functions && !virtual_load)
{
auto shle = g_fxo->init<statichle_handler>(0);
@ -1943,7 +2006,7 @@ bool ppu_load_exec(const ppu_exec_object& elf, const std::string& elf_path, util
if ((prog.p_vaddr | prog.p_filesz | prog.p_memsz) > u32{umax})
{
ppu_loader.fatal("ppu_load_exec(): TLS segment is invalid!");
ppu_loader.error("ppu_load_exec(): TLS segment is invalid!");
return false;
}
@ -1970,7 +2033,7 @@ bool ppu_load_exec(const ppu_exec_object& elf, const std::string& elf_path, util
//be_t<u32> crash_dump_param_addr;
};
const auto& info = vm::_ref<process_param_t>(vm::cast(prog.p_vaddr));
const auto& info = *ensure(_main.get_ptr<process_param_t>(vm::cast(prog.p_vaddr)));
if (info.size < sizeof(process_param_t))
{
@ -2025,7 +2088,7 @@ bool ppu_load_exec(const ppu_exec_object& elf, const std::string& elf_path, util
be_t<u32> unk2;
};
const auto& proc_prx_param = vm::_ref<const ppu_proc_prx_param_t>(vm::cast(prog.p_vaddr));
const auto& proc_prx_param = *ensure(_main.get_ptr<const ppu_proc_prx_param_t>(vm::cast(prog.p_vaddr)));
ppu_loader.notice("* libent_start = *0x%x", proc_prx_param.libent_start);
ppu_loader.notice("* libstub_start = *0x%x", proc_prx_param.libstub_start);
@ -2034,12 +2097,16 @@ bool ppu_load_exec(const ppu_exec_object& elf, const std::string& elf_path, util
if (proc_prx_param.magic != 0x1b434cecu)
{
ppu_loader.fatal("ppu_load_exec(): Bad magic! (0x%x)", proc_prx_param.magic);
ppu_loader.error("ppu_load_exec(): Bad magic! (0x%x)", proc_prx_param.magic);
return false;
}
ppu_load_exports(&link, proc_prx_param.libent_start, proc_prx_param.libent_end);
ppu_load_imports(_main.relocs, &link, proc_prx_param.libstub_start, proc_prx_param.libstub_end);
if (!virtual_load)
{
ppu_load_exports(&link, proc_prx_param.libent_start, proc_prx_param.libent_end);
ppu_load_imports(_main.relocs, &link, proc_prx_param.libstub_start, proc_prx_param.libstub_end);
}
std::stable_sort(_main.relocs.begin(), _main.relocs.end());
}
break;
@ -2106,7 +2173,7 @@ bool ppu_load_exec(const ppu_exec_object& elf, const std::string& elf_path, util
load_libs.emplace("libsysmodule.sprx");
}
if (ar || Emu.IsVsh())
if (ar || Emu.IsVsh() || virtual_load)
{
// Cannot be used with vsh.self or savestates (they self-manage itself)
load_libs.clear();
@ -2149,8 +2216,15 @@ bool ppu_load_exec(const ppu_exec_object& elf, const std::string& elf_path, util
g_fxo->init<lv2_memory_container>(mem_size);
}
void init_fxo_for_exec(utils::serial* ar, bool full);
init_fxo_for_exec(ar, false);
if (!virtual_load)
{
void init_fxo_for_exec(utils::serial* ar, bool full);
init_fxo_for_exec(ar, false);
}
else
{
Emu.ConfigurePPUCache();
}
liblv2_begin = 0;
liblv2_end = 0;
@ -2165,13 +2239,13 @@ bool ppu_load_exec(const ppu_exec_object& elf, const std::string& elf_path, util
{
ppu_loader.warning("Loading library: %s", name);
auto prx = ppu_load_prx(obj, lle_dir + name, 0, nullptr);
auto prx = ppu_load_prx(obj, false, lle_dir + name, 0, nullptr);
prx->state = PRX_STATE_STARTED;
prx->load_exports();
if (prx->funcs.empty())
{
ppu_loader.fatal("Module %s has no functions!", name);
ppu_loader.error("Module %s has no functions!", name);
}
else
{
@ -2196,7 +2270,7 @@ bool ppu_load_exec(const ppu_exec_object& elf, const std::string& elf_path, util
}
}
if (ar)
if (ar || virtual_load)
{
error_handler.errored = false;
return true;
@ -2332,7 +2406,7 @@ bool ppu_load_exec(const ppu_exec_object& elf, const std::string& elf_path, util
return true;
}
std::pair<std::shared_ptr<lv2_overlay>, CellError> ppu_load_overlay(const ppu_exec_object& elf, const std::string& path, s64 file_offset, utils::serial* ar)
std::pair<std::shared_ptr<lv2_overlay>, CellError> ppu_load_overlay(const ppu_exec_object& elf, bool virtual_load, const std::string& path, s64 file_offset, utils::serial* ar)
{
if (elf != elf_error::ok)
{
@ -2396,11 +2470,23 @@ std::pair<std::shared_ptr<lv2_overlay>, CellError> ppu_load_overlay(const ppu_ex
const bool already_loaded = !!ar; // Unimplemented optimization for savestates
if (already_loaded)
_seg.ptr = vm::base(addr);
if (virtual_load)
{
// Leave additional room for the analyser so it can safely access beyond limit a bit
// Because with VM the address sapce is not really a limit so any u32 address is valid there, here it is UB to create pointer that goes beyond the boundaries
// TODO: Use make_shared_for_overwrite when all compilers support it
const usz alloc_size = utils::align<usz>(size, 0x10000) + 4096;
ovlm->allocations.push_back(std::shared_ptr<u8[]>(new u8[alloc_size]));
_seg.ptr = ovlm->allocations.back().get();
std::memset(static_cast<u8*>(_seg.ptr) + prog.bin.size(), 0, alloc_size - 4096 - prog.bin.size());
}
else if (already_loaded)
{
if (!vm::check_addr(addr, vm::page_readable, size))
{
ppu_loader.fatal("ppu_load_overlay(): Archived PPU overlay memory has not been found! (addr=0x%x, memsz=0x%x)", addr, size);
ppu_loader.error("ppu_load_overlay(): Archived PPU overlay memory has not been found! (addr=0x%x, memsz=0x%x)", addr, size);
return {nullptr, CELL_EABORT};
}
}
@ -2418,14 +2504,18 @@ std::pair<std::shared_ptr<lv2_overlay>, CellError> ppu_load_overlay(const ppu_ex
return {nullptr, CELL_EBUSY};
}
// Store only LOAD segments (TODO)
ovlm->segs.emplace_back(_seg);
ovlm->addr_to_seg_index.emplace(addr, ovlm->segs.size() - 1);
// Copy segment data, hash it
if (!already_loaded) std::memcpy(vm::base(addr), prog.bin.data(), prog.bin.size());
if (!already_loaded) std::memcpy(ensure(ovlm->get_ptr<void>(addr)), prog.bin.data(), prog.bin.size());
sha1_update(&sha, reinterpret_cast<const uchar*>(&prog.p_vaddr), sizeof(prog.p_vaddr));
sha1_update(&sha, reinterpret_cast<const uchar*>(&prog.p_memsz), sizeof(prog.p_memsz));
sha1_update(&sha, prog.bin.data(), prog.bin.size());
// Initialize executable code if necessary
if (prog.p_flags & 0x1)
if (prog.p_flags & 0x1 && !virtual_load)
{
if (ar)
{
@ -2435,9 +2525,6 @@ std::pair<std::shared_ptr<lv2_overlay>, CellError> ppu_load_overlay(const ppu_ex
ppu_register_range(addr, size);
}
// Store only LOAD segments (TODO)
ovlm->segs.emplace_back(_seg);
}
}
@ -2479,18 +2566,18 @@ std::pair<std::shared_ptr<lv2_overlay>, CellError> ppu_load_overlay(const ppu_ex
}
// Apply the patch
auto applied = g_fxo->get<patch_engine>().apply(hash, vm::g_base_addr);
auto applied = g_fxo->get<patch_engine>().apply(hash, [ovlm](u32 addr) { return ovlm->get_ptr<u8>(addr); });
if (!Emu.GetTitleID().empty())
{
// Alternative patch
applied += g_fxo->get<patch_engine>().apply(Emu.GetTitleID() + '-' + hash, vm::g_base_addr);
applied += g_fxo->get<patch_engine>().apply(Emu.GetTitleID() + '-' + hash, [ovlm](u32 addr) { return ovlm->get_ptr<u8>(addr); });
}
// Embedded SPU elf patching
for (const auto& seg : ovlm->segs)
{
ppu_check_patch_spu_images(seg);
ppu_check_patch_spu_images(*ovlm, seg);
}
if (applied.empty())
@ -2523,7 +2610,7 @@ std::pair<std::shared_ptr<lv2_overlay>, CellError> ppu_load_overlay(const ppu_ex
//and a lot of zeros.
};
const auto& info = vm::_ref<process_param_t>(vm::cast(prog.p_vaddr));
const auto& info = *ensure(ovlm->get_ptr<process_param_t>(vm::cast(prog.p_vaddr)));
if (info.size < sizeof(process_param_t))
{
@ -2561,7 +2648,7 @@ std::pair<std::shared_ptr<lv2_overlay>, CellError> ppu_load_overlay(const ppu_ex
be_t<u32> unk2;
};
const auto& proc_prx_param = vm::_ref<const ppu_proc_prx_param_t>(vm::cast(prog.p_vaddr));
const auto& proc_prx_param = *ensure(ovlm->get_ptr<const ppu_proc_prx_param_t>(vm::cast(prog.p_vaddr)));
ppu_loader.notice("* libent_start = *0x%x", proc_prx_param.libent_start);
ppu_loader.notice("* libstub_start = *0x%x", proc_prx_param.libstub_start);
@ -2573,8 +2660,11 @@ std::pair<std::shared_ptr<lv2_overlay>, CellError> ppu_load_overlay(const ppu_ex
fmt::throw_exception("Bad magic! (0x%x)", proc_prx_param.magic);
}
ppu_load_exports(&link, proc_prx_param.libent_start, proc_prx_param.libent_end);
ppu_load_imports(ovlm->relocs, &link, proc_prx_param.libstub_start, proc_prx_param.libstub_end);
if (!virtual_load)
{
ppu_load_exports(&link, proc_prx_param.libent_start, proc_prx_param.libent_end);
ppu_load_imports(ovlm->relocs, &link, proc_prx_param.libstub_start, proc_prx_param.libstub_end);
}
}
break;
}
@ -2593,7 +2683,7 @@ std::pair<std::shared_ptr<lv2_overlay>, CellError> ppu_load_overlay(const ppu_ex
// Validate analyser results (not required)
ovlm->validate(0);
if (!ar)
if (!ar && !virtual_load)
{
idm::import_existing<lv2_obj, lv2_overlay>(ovlm);
try_spawn_ppu_if_exclusive_program(*ovlm);
@ -2641,7 +2731,7 @@ bool ppu_load_rel_exec(const ppu_rel_object& elf)
if (!addr)
{
ppu_loader.fatal("ppu_load_rel_exec(): vm::alloc() failed (memsz=0x%x)", memsize);
ppu_loader.error("ppu_load_rel_exec(): vm::alloc() failed (memsz=0x%x)", memsize);
return false;
}

View File

@ -161,9 +161,10 @@ extern void ppu_initialize();
extern void ppu_finalize(const ppu_module& info);
extern bool ppu_initialize(const ppu_module& info, bool = false);
static void ppu_initialize2(class jit_compiler& jit, const ppu_module& module_part, const std::string& cache_path, const std::string& obj_name);
extern std::pair<std::shared_ptr<lv2_overlay>, CellError> ppu_load_overlay(const ppu_exec_object&, const std::string& path, s64 file_offset, utils::serial* = nullptr);
extern bool ppu_load_exec(const ppu_exec_object&, bool virtual_load, const std::string&, utils::serial* = nullptr);
extern std::pair<std::shared_ptr<lv2_overlay>, CellError> ppu_load_overlay(const ppu_exec_object&, bool virtual_load, const std::string& path, s64 file_offset, utils::serial* = nullptr);
extern void ppu_unload_prx(const lv2_prx&);
extern std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object&, const std::string&, s64 file_offset, utils::serial* = nullptr);
extern std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object&, bool virtual_load, const std::string&, s64 file_offset, utils::serial* = nullptr);
extern void ppu_execute_syscall(ppu_thread& ppu, u64 code);
static void ppu_break(ppu_thread&, ppu_opcode_t, be_t<u32>*, ppu_intrp_func*);
@ -3140,7 +3141,8 @@ extern void ppu_precompile(std::vector<std::string>& dir_queue, std::vector<ppu_
atomic_t<usz> fnext = 0;
shared_mutex sprx_mtx, ovl_mtx;
lf_queue<std::string> possible_exec_file_paths;
shared_mutex ovl_mtx;
named_thread_group workers("SPRX Worker ", std::min<u32>(utils::get_thread_count(), ::size32(file_queue)), [&]
{
@ -3192,17 +3194,11 @@ extern void ppu_precompile(std::vector<std::string>& dir_queue, std::vector<ppu_
if (ppu_prx_object obj = src; (prx_err = obj, obj == elf_error::ok))
{
std::unique_lock lock(sprx_mtx);
if (auto prx = ppu_load_prx(obj, path, offset))
if (auto prx = ppu_load_prx(obj, true, path, offset))
{
lock.unlock();
obj.clear(), src.close(); // Clear decrypted file and elf object memory
ppu_initialize(*prx);
idm::remove<lv2_obj, lv2_prx>(idm::last_id());
lock.lock();
ppu_unload_prx(*prx);
lock.unlock();
ppu_finalize(*prx);
continue;
}
@ -3215,10 +3211,7 @@ extern void ppu_precompile(std::vector<std::string>& dir_queue, std::vector<ppu_
{
while (ovl_err == elf_error::ok)
{
// Only one thread compiles OVL atm, other can compile PRX cuncurrently
std::unique_lock lock(ovl_mtx);
auto [ovlm, error] = ppu_load_overlay(obj, path, offset);
auto [ovlm, error] = ppu_load_overlay(obj, true, path, offset);
if (error)
{
@ -3229,15 +3222,13 @@ extern void ppu_precompile(std::vector<std::string>& dir_queue, std::vector<ppu_
obj.clear(), src.close(); // Clear decrypted file and elf object memory
ppu_initialize(*ovlm);
for (auto& seg : ovlm->segs)
{
vm::dealloc(seg.addr);
// Does not really require this lock, this is done for performance reasons.
// Seems like too many created threads is hard for Windows to manage efficiently with many CPU threads.
std::lock_guard lock(ovl_mtx);
ppu_initialize(*ovlm);
}
lock.unlock();
idm::remove<lv2_obj, lv2_overlay>(idm::last_id());
ppu_finalize(*ovlm);
break;
}
@ -3248,7 +3239,8 @@ extern void ppu_precompile(std::vector<std::string>& dir_queue, std::vector<ppu_
}
}
ppu_log.notice("Failed to precompile '%s' (prx: %s, ovl: %s)", path, prx_err, ovl_err);
ppu_log.notice("Failed to precompile '%s' (prx: %s, ovl: %s): Attempting tratment as executable file", path, prx_err, ovl_err);
possible_exec_file_paths.push(path);
continue;
}
});
@ -3256,6 +3248,97 @@ extern void ppu_precompile(std::vector<std::string>& dir_queue, std::vector<ppu_
// Join every thread
workers.join();
named_thread exec_worker("PPU Exec Worker", [&]
{
if (!possible_exec_file_paths)
{
return;
}
#ifdef __APPLE__
pthread_jit_write_protect_np(false);
#endif
// Set low priority
thread_ctrl::scoped_priority low_prio(-1);
auto slice = possible_exec_file_paths.pop_all();
auto main_module = std::move(g_fxo->get<main_ppu_module>());
for (; slice; slice.pop_front(), g_progr_fdone++)
{
g_progr_ftotal++;
if (Emu.IsStopped())
{
continue;
}
const std::string path = *slice;
ppu_log.notice("Trying to load as executable: %s", path);
// Load MSELF, SPRX or SELF
fs::file src{path};
if (!src)
{
ppu_log.error("Failed to open '%s' (%s)", path, fs::g_tls_error);
continue;
}
// Some files may fail to decrypt due to the lack of klic
src = decrypt_self(std::move(src));
if (!src)
{
ppu_log.notice("Failed to decrypt '%s'", path);
continue;
}
elf_error exec_err{};
if (ppu_exec_object obj = src; (exec_err = obj, obj == elf_error::ok))
{
while (exec_err == elf_error::ok)
{
if (!ppu_load_exec(obj, true, path))
{
// Abort
exec_err = elf_error::header_type;
break;
}
main_ppu_module& _main = g_fxo->get<main_ppu_module>();
if (!_main.analyse(0, _main.elf_entry, _main.seg0_code_end, _main.applied_pathes, [](){ return Emu.IsStopped(); }))
{
break;
}
obj.clear(), src.close(); // Clear decrypted file and elf object memory
ppu_initialize(_main);
ppu_finalize(_main);
_main = {};
break;
}
if (exec_err == elf_error::ok)
{
continue;
}
}
ppu_log.notice("Failed to precompile '%s' as executable (%s)", path, exec_err);
continue;
}
g_fxo->get<main_ppu_module>() = std::move(main_module);
});
exec_worker();
// Revert changes
if (!had_ovl)
@ -3360,7 +3443,7 @@ extern void ppu_initialize()
{
const std::string eseibrd = mount_point + "/vsh/module/eseibrd.sprx";
if (auto prx = ppu_load_prx(ppu_prx_object{decrypt_self(fs::file{eseibrd})}, eseibrd, 0))
if (auto prx = ppu_load_prx(ppu_prx_object{decrypt_self(fs::file{eseibrd})}, true, eseibrd, 0))
{
// Check if cache exists for this infinitesimally small prx
dev_flash_located = ppu_initialize(*prx, true);
@ -3563,9 +3646,11 @@ bool ppu_initialize(const ppu_module& info, bool check_only)
continue;
}
for (u32 i = addr; i < addr + size; i += 4)
auto i_ptr = ensure(info.get_ptr<u32>(addr));
for (u32 i = addr; i < addr + size; i += 4, i_ptr++)
{
if (g_ppu_itype.decode(vm::read32(i)) == ppu_itype::MFVSCR)
if (g_ppu_itype.decode(*i_ptr) == ppu_itype::MFVSCR)
{
ppu_log.warning("MFVSCR found");
has_mfvscr = true;
@ -3708,7 +3793,7 @@ bool ppu_initialize(const ppu_module& info, bool check_only)
if (roff > addr)
{
// Hash from addr to the beginning of the relocation
sha1_update(&ctx, vm::_ptr<const u8>(addr), roff - addr);
sha1_update(&ctx, ensure(info.get_ptr<const u8>(addr)), roff - addr);
}
// Hash relocation type instead
@ -3721,9 +3806,11 @@ bool ppu_initialize(const ppu_module& info, bool check_only)
if (has_dcbz == 1)
{
for (u32 i = addr, end = block.second + block.first - 1; i <= end; i += 4)
auto i_ptr = ensure(info.get_ptr<u32>(addr));
for (u32 i = addr, end = block.second + block.first - 1; i <= end; i += 4, i_ptr++)
{
if (g_ppu_itype.decode(vm::read32(i)) == ppu_itype::DCBZ)
if (g_ppu_itype.decode(*i_ptr) == ppu_itype::DCBZ)
{
has_dcbz = 2;
break;
@ -3732,7 +3819,7 @@ bool ppu_initialize(const ppu_module& info, bool check_only)
}
// Hash from addr to the end of the block
sha1_update(&ctx, vm::_ptr<const u8>(addr), block.second - (addr - block.first));
sha1_update(&ctx, ensure(info.get_ptr<const u8>(addr)), block.second - (addr - block.first));
}
if (reloc)
@ -3742,9 +3829,11 @@ bool ppu_initialize(const ppu_module& info, bool check_only)
if (has_dcbz == 1)
{
for (u32 i = func.addr, end = func.addr + func.size - 1; i <= end; i += 4)
auto i_ptr = ensure(info.get_ptr<u32>(func.addr));
for (u32 i = func.addr, end = func.addr + func.size - 1; i <= end; i += 4, i_ptr++)
{
if (g_ppu_itype.decode(vm::read32(i)) == ppu_itype::DCBZ)
if (g_ppu_itype.decode(*i_ptr) == ppu_itype::DCBZ)
{
has_dcbz = 2;
break;
@ -3752,7 +3841,7 @@ bool ppu_initialize(const ppu_module& info, bool check_only)
}
}
sha1_update(&ctx, vm::_ptr<const u8>(func.addr), func.size);
sha1_update(&ctx, ensure(info.get_ptr<const u8>(func.addr)), func.size);
}
if (false)

View File

@ -150,7 +150,7 @@ Function* PPUTranslator::Translate(const ppu_function& info)
for (u32 addr = m_addr; addr < m_addr + info.size; addr += 4)
{
const u32 op = vm::read32(vm::cast(addr + base));
const u32 op = *ensure(m_info.get_ptr<u32>(addr + base));
switch (g_ppu_itype.decode(op))
{
@ -214,7 +214,7 @@ Function* PPUTranslator::Translate(const ppu_function& info)
const auto block = std::make_pair(info.addr, info.size);
{
// Optimize BLR (prefetch LR)
if (vm::read32(vm::cast(block.first + block.second - 4)) == ppu_instructions::BLR())
if (*ensure(m_info.get_ptr<u32>(block.first + block.second - 4)) == ppu_instructions::BLR())
{
RegLoad(m_lr);
}
@ -239,7 +239,7 @@ Function* PPUTranslator::Translate(const ppu_function& info)
m_rel = nullptr;
}
const u32 op = vm::read32(vm::cast(m_addr + base));
const u32 op = *ensure(m_info.get_ptr<u32>(m_addr + base));
(this->*(s_ppu_decoder.decode(op)))({op});

View File

@ -13,7 +13,7 @@
#include "sys_overlay.h"
#include "sys_fs.h"
extern std::pair<std::shared_ptr<lv2_overlay>, CellError> ppu_load_overlay(const ppu_exec_object&, const std::string& path, s64 file_offset, utils::serial* ar = nullptr);
extern std::pair<std::shared_ptr<lv2_overlay>, CellError> ppu_load_overlay(const ppu_exec_object&, bool virtual_load, const std::string& path, s64 file_offset, utils::serial* ar = nullptr);
extern bool ppu_initialize(const ppu_module&, bool = false);
extern void ppu_finalize(const ppu_module&);
@ -43,7 +43,7 @@ static error_code overlay_load_module(vm::ptr<u32> ovlmid, const std::string& vp
return {CELL_ENOEXEC, obj.operator elf_error()};
}
const auto [ovlm, error] = ppu_load_overlay(obj, vfs::get(vpath), file_offset);
const auto [ovlm, error] = ppu_load_overlay(obj, false, vfs::get(vpath), file_offset);
obj.clear();
@ -77,7 +77,7 @@ std::shared_ptr<void> lv2_overlay::load(utils::serial& ar)
{
u128 klic = g_fxo->get<loaded_npdrm_keys>().last_key();
file = make_file_view(std::move(file), offset);
ovlm = ppu_load_overlay(ppu_exec_object{ decrypt_self(std::move(file), reinterpret_cast<u8*>(&klic)) }, path, 0, &ar).first;
ovlm = ppu_load_overlay(ppu_exec_object{ decrypt_self(std::move(file), reinterpret_cast<u8*>(&klic)) }, false, path, 0, &ar).first;
ensure(ovlm);
}
else

View File

@ -17,7 +17,7 @@
#include "sys_memory.h"
#include <span>
extern std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object&, const std::string&, s64, utils::serial* = nullptr);
extern std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object&, bool virtual_load, const std::string&, s64, utils::serial* = nullptr);
extern void ppu_unload_prx(const lv2_prx& prx);
extern bool ppu_initialize(const ppu_module&, bool = false);
extern void ppu_finalize(const ppu_module&);
@ -270,7 +270,7 @@ static error_code prx_load_module(const std::string& vpath, u64 flags, vm::ptr<s
return CELL_PRX_ERROR_ILLEGAL_LIBRARY;
}
const auto prx = ppu_load_prx(obj, path, file_offset);
const auto prx = ppu_load_prx(obj, false, path, file_offset);
obj.clear();
@ -324,7 +324,7 @@ std::shared_ptr<void> lv2_prx::load(utils::serial& ar)
{
u128 klic = g_fxo->get<loaded_npdrm_keys>().last_key();
file = make_file_view(std::move(file), offset);
prx = ppu_load_prx(ppu_prx_object{ decrypt_self(std::move(file), reinterpret_cast<u8*>(&klic)) }, path, 0, &ar);
prx = ppu_load_prx(ppu_prx_object{ decrypt_self(std::move(file), reinterpret_cast<u8*>(&klic)) }, false, path, 0, &ar);
prx->m_loaded_flags = std::move(loaded_flags);
prx->m_external_loaded_flags = std::move(external_flags);

View File

@ -185,12 +185,12 @@ void sys_spu_image::deploy(u8* loc, std::span<const sys_spu_segment> segs, bool
}
// Apply the patch
auto applied = g_fxo->get<patch_engine>().apply(hash, loc);
auto applied = g_fxo->get<patch_engine>().apply(hash, [loc](u32 addr) { return loc + addr; });
if (!Emu.GetTitleID().empty())
{
// Alternative patch
applied += g_fxo->get<patch_engine>().apply(Emu.GetTitleID() + '-' + hash, loc);
applied += g_fxo->get<patch_engine>().apply(Emu.GetTitleID() + '-' + hash, [loc](u32 addr) { return loc + addr; });
}
(is_verbose ? spu_log.notice : sys_spu.trace)("Loaded SPU image: %s (<- %u)%s", hash, applied.size(), dump);

View File

@ -70,15 +70,15 @@ std::string g_cfg_defaults;
atomic_t<u64> g_watchdog_hold_ctr{0};
extern bool ppu_load_exec(const ppu_exec_object&, const std::string&, utils::serial* = nullptr);
extern bool ppu_load_exec(const ppu_exec_object&, bool virtual_load, const std::string&, utils::serial* = nullptr);
extern void spu_load_exec(const spu_exec_object&);
extern void spu_load_rel_exec(const spu_rel_object&);
extern void ppu_precompile(std::vector<std::string>& dir_queue, std::vector<ppu_module*>* loaded_prx);
extern bool ppu_initialize(const ppu_module&, bool = false);
extern void ppu_finalize(const ppu_module&);
extern void ppu_unload_prx(const lv2_prx&);
extern std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object&, const std::string&, s64 = 0, utils::serial* = nullptr);
extern std::pair<std::shared_ptr<lv2_overlay>, CellError> ppu_load_overlay(const ppu_exec_object&, const std::string& path, s64 = 0, utils::serial* = nullptr);
extern std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object&, bool virtual_load, const std::string&, s64 = 0, utils::serial* = nullptr);
extern std::pair<std::shared_ptr<lv2_overlay>, CellError> ppu_load_overlay(const ppu_exec_object&, bool virtual_load, const std::string& path, s64 = 0, utils::serial* = nullptr);
extern bool ppu_load_rel_exec(const ppu_rel_object&);
extern bool is_savestate_version_compatible(const std::vector<std::pair<u16, u16>>& data, bool is_boot_check);
extern std::vector<std::pair<u16, u16>> read_used_savestate_versions();
@ -1338,6 +1338,8 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
std::vector<std::string> dir_queue;
dir_queue.emplace_back(m_path + '/');
init_fxo_for_exec(nullptr, true);
{
if (m_title_id.empty())
{
@ -1384,7 +1386,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
const ppu_exec_object obj = src;
if (obj == elf_error::ok && ppu_load_exec(obj, path))
if (obj == elf_error::ok && ppu_load_exec(obj, true, path))
{
g_fxo->get<main_ppu_module>().path = path;
}
@ -1393,22 +1395,6 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
sys_log.error("Failed to load binary '%s' (%s)", path, obj.get_error());
}
}
else
{
// Workaround for analyser glitches
ensure(vm::falloc(0x10000, 0xf0000, vm::main));
}
}
if (auto& _main = g_fxo->get<main_ppu_module>(); _main.path.empty())
{
init_fxo_for_exec(nullptr, true);
}
if (auto main_ppu = idm::get<named_thread<ppu_thread>>(ppu_thread::id_base))
{
// Created by ppu_load_exec, unwanted
main_ppu->state += cpu_flag::exit;
}
g_fxo->init<named_thread>("SPRX Loader"sv, [this, dir_queue]() mutable
@ -1961,11 +1947,11 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
g_fxo->init<main_ppu_module>();
if (ppu_load_exec(ppu_exec, m_path, DeserialManager()))
if (ppu_load_exec(ppu_exec, false, m_path, DeserialManager()))
{
}
// Overlay (OVL) executable (only load it)
else if (vm::map(0x3000'0000, 0x1000'0000, 0x200); !ppu_load_overlay(ppu_exec, m_path).first)
else if (vm::map(0x3000'0000, 0x1000'0000, 0x200); !ppu_load_overlay(ppu_exec, false, m_path).first)
{
ppu_exec.set_error(elf_error::header_type);
}
@ -1989,7 +1975,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool is_disc_patch,
// PPU PRX
GetCallbacks().on_ready();
g_fxo->init(false);
ppu_load_prx(ppu_prx, m_path);
ppu_load_prx(ppu_prx, false, m_path);
Pause(true);
}
else if (spu_exec.open(elf_file) == elf_error::ok)