diff --git a/rpcs3/Emu/Cell/PPUAnalyser.cpp b/rpcs3/Emu/Cell/PPUAnalyser.cpp index b5d7ad6520..0cff30ec89 100644 --- a/rpcs3/Emu/Cell/PPUAnalyser.cpp +++ b/rpcs3/Emu/Cell/PPUAnalyser.cpp @@ -10,7 +10,6 @@ namespace vm { using namespace ps3; } const ppu_decoder s_ppu_itype; -const ppu_decoder s_ppu_iname; template<> void fmt_class_string::format(std::string& out, u64 arg) @@ -36,10 +35,10 @@ void fmt_class_string>::format(std::string& out, u64 arg) format_bitset(out, arg, "[", ",", "]", &fmt_class_string::format); } -void ppu_validate(const std::string& fname, const std::vector& funcs, u32 reloc) +void ppu_module::validate(u32 reloc) { // Load custom PRX configuration if available - if (fs::file yml{fname + ".yml"}) + if (fs::file yml{path + ".yml"}) { const auto cfg = YAML::Load(yml.to_string()); @@ -57,14 +56,14 @@ void ppu_validate(const std::string& fname, const std::vector& fun while (addr > found && index + 1 < funcs.size()) { - LOG_WARNING(LOADER, "%s.yml : unexpected function at 0x%x (0x%x, 0x%x)", fname, found, addr, size); + LOG_WARNING(LOADER, "%s.yml : unexpected function at 0x%x (0x%x, 0x%x)", path, found, addr, size); index++; found = funcs[index].addr - reloc; } if (addr < found) { - LOG_ERROR(LOADER, "%s.yml : function not found (0x%x, 0x%x)", fname, addr, size); + LOG_ERROR(LOADER, "%s.yml : function not found (0x%x, 0x%x)", path, addr, size); continue; } @@ -72,7 +71,7 @@ void ppu_validate(const std::string& fname, const std::vector& fun { if (size + 4 != funcs[index].size || vm::read32(addr + size) != ppu_instructions::NOP()) { - LOG_ERROR(LOADER, "%s.yml : function size mismatch at 0x%x(size=0x%x) (0x%x, 0x%x)", fname, found, funcs[index].size, addr, size); + LOG_ERROR(LOADER, "%s.yml : function size mismatch at 0x%x(size=0x%x) (0x%x, 0x%x)", path, found, funcs[index].size, addr, size); } } @@ -80,7 +79,7 @@ void ppu_validate(const std::string& fname, const std::vector& fun } else { - LOG_ERROR(LOADER, "%s.yml : function not found at the end (0x%x, 0x%x)", fname, addr, size); + LOG_ERROR(LOADER, "%s.yml : function not found at the end (0x%x, 0x%x)", path, addr, size); break; } } @@ -94,13 +93,13 @@ void ppu_validate(const std::string& fname, const std::vector& fun { if (funcs[index].size) { - LOG_ERROR(LOADER, "%s.yml : function not covered at 0x%x (size=0x%x)", fname, funcs[index].addr, funcs[index].size); + LOG_ERROR(LOADER, "%s.yml : function not covered at 0x%x (size=0x%x)", path, funcs[index].addr, funcs[index].size); } index++; } - LOG_SUCCESS(LOADER, "%s.yml : validation completed", fname); + LOG_SUCCESS(LOADER, "%s.yml : validation completed", path); } } @@ -522,17 +521,17 @@ namespace ppu_patterns }; } -std::vector ppu_analyse(const std::vector>& segs, const std::vector>& secs, u32 lib_toc, u32 entry) +void ppu_module::analyse(u32 lib_toc, u32 entry) { // Assume first segment is executable - const u32 start = segs[0].first; - const u32 end = segs[0].first + segs[0].second; + const u32 start = segs[0].addr; + const u32 end = segs[0].addr + segs[0].size; // Known TOCs (usually only 1) std::unordered_set TOCs; // Known functions - std::map funcs; + std::map fmap; // Function analysis workload std::vector> func_queue; @@ -543,7 +542,7 @@ std::vector ppu_analyse(const std::vector>& se // Register new function auto add_func = [&](u32 addr, u32 toc, u32 caller) -> ppu_function& { - ppu_function& func = funcs[addr]; + ppu_function& func = fmap[addr]; if (caller) { @@ -586,7 +585,7 @@ std::vector ppu_analyse(const std::vector>& se // Grope for OPD section (TODO: optimization, better constraints) for (const auto& seg : segs) { - for (vm::cptr ptr = vm::cast(seg.first); ptr.addr() < seg.first + seg.second; ptr++) + for (vm::cptr ptr = vm::cast(seg.addr); ptr.addr() < seg.addr + seg.size; ptr++) { if (ptr[0] >= start && ptr[0] < end && ptr[0] % 4 == 0 && ptr[1] == toc) { @@ -602,7 +601,7 @@ std::vector ppu_analyse(const std::vector>& se // Get next reliable function address auto get_limit = [&](u32 addr) -> u32 { - for (auto it = funcs.lower_bound(addr), end = funcs.end(); it != end; it++) + for (auto it = fmap.lower_bound(addr), end = fmap.end(); it != end; it++) { if (test(it->second.attr, ppu_attr::known_addr)) { @@ -616,7 +615,7 @@ std::vector ppu_analyse(const std::vector>& se // Find references indiscriminately for (const auto& seg : segs) { - for (vm::cptr ptr = vm::cast(seg.first); ptr.addr() < seg.first + seg.second; ptr++) + for (vm::cptr ptr = vm::cast(seg.addr); ptr.addr() < seg.addr + seg.size; ptr++) { const u32 value = *ptr; @@ -627,7 +626,7 @@ std::vector ppu_analyse(const std::vector>& se for (const auto& _seg : segs) { - if (value >= _seg.first && value < _seg.first + _seg.second) + if (value >= _seg.addr && value < _seg.addr + _seg.size) { addr_heap.emplace(value); break; @@ -639,10 +638,10 @@ std::vector ppu_analyse(const std::vector>& se // Find OPD section for (const auto& sec : secs) { - vm::cptr sec_end = vm::cast(sec.first + sec.second); + vm::cptr sec_end = vm::cast(sec.addr + sec.size); // Probe - for (vm::cptr ptr = vm::cast(sec.first); ptr < sec_end; ptr += 2) + for (vm::cptr 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]) { @@ -677,10 +676,10 @@ std::vector ppu_analyse(const std::vector>& se } } - if (sec_end) LOG_NOTICE(PPU, "Reading OPD section at 0x%x...", sec.first); + if (sec_end) LOG_NOTICE(PPU, "Reading OPD section at 0x%x...", sec.addr); // Mine - for (vm::cptr ptr = vm::cast(sec.first); ptr < sec_end; ptr += 2) + for (vm::cptr ptr = vm::cast(sec.addr); ptr < sec_end; ptr += 2) { // Special case: see "Probe" if (!ptr[0]) ptr += 4; @@ -709,7 +708,7 @@ std::vector ppu_analyse(const std::vector>& se } // Clean TOCs - for (auto&& pair : funcs) + for (auto&& pair : fmap) { if (pair.second.toc == -1) { @@ -720,12 +719,12 @@ std::vector ppu_analyse(const std::vector>& se // Find .eh_frame section for (const auto& sec : secs) { - vm::cptr sec_end = vm::cast(sec.first + sec.second); + vm::cptr sec_end = vm::cast(sec.addr + sec.size); // Probe - for (vm::cptr ptr = vm::cast(sec.first); ptr < sec_end;) + for (vm::cptr ptr = vm::cast(sec.addr); ptr < sec_end;) { - if (ptr % 4 || ptr.addr() < sec.first || ptr >= sec_end) + if (ptr % 4 || ptr.addr() < sec.addr || ptr >= sec_end) { sec_end.set(0); break; @@ -749,7 +748,7 @@ std::vector ppu_analyse(const std::vector>& se { const u32 cie_off = ptr.addr() - ptr[1] + 4; - if (cie_off % 4 || cie_off < sec.first || cie_off >= sec_end.addr()) + if (cie_off % 4 || cie_off < sec.addr || cie_off >= sec_end.addr()) { sec_end.set(0); break; @@ -759,10 +758,10 @@ std::vector ppu_analyse(const std::vector>& se ptr = vm::cast(ptr.addr() + size); } - if (sec_end && sec.second > 4) LOG_NOTICE(PPU, "Reading .eh_frame section at 0x%x...", sec.first); + if (sec_end && sec.size > 4) LOG_NOTICE(PPU, "Reading .eh_frame section at 0x%x...", sec.addr); // Mine - for (vm::cptr ptr = vm::cast(sec.first); ptr < sec_end; ptr = vm::cast(ptr.addr() + ptr[0] + 4)) + for (vm::cptr ptr = vm::cast(sec.addr); ptr < sec_end; ptr = vm::cast(ptr.addr() + ptr[0] + 4)) { if (ptr[0] == 0) { @@ -837,7 +836,7 @@ std::vector ppu_analyse(const std::vector>& se { for (u32 addr : func.callers) { - ppu_function& caller = funcs[addr]; + ppu_function& caller = fmap[addr]; if (!caller.toc) { @@ -847,7 +846,7 @@ std::vector ppu_analyse(const std::vector>& se for (u32 addr : func.calls) { - ppu_function& callee = funcs[addr]; + ppu_function& callee = fmap[addr]; if (!callee.toc) { @@ -1077,10 +1076,10 @@ std::vector ppu_analyse(const std::vector>& se if (test(func.attr, ppu_attr::no_size)) { // Get next function - const auto _next = funcs.lower_bound(func.blocks.crbegin()->first + 1); + const auto _next = fmap.lower_bound(func.blocks.crbegin()->first + 1); // Get limit - const u32 func_end2 = _next == funcs.end() ? func_end : std::min(_next->first, func_end); + const u32 func_end2 = _next == fmap.end() ? func_end : std::min(_next->first, func_end); // Set more block entries std::for_each(addr_heap.lower_bound(func.addr), addr_heap.lower_bound(func_end2), add_block); @@ -1322,14 +1321,14 @@ std::vector ppu_analyse(const std::vector>& se } // Function shrinkage, disabled (TODO: it's potentially dangerous but improvable) - for (auto& _pair : funcs) + for (auto& _pair : fmap) { auto& func = _pair.second; // Get next function addr - const auto _next = funcs.lower_bound(_pair.first + 1); + const auto _next = fmap.lower_bound(_pair.first + 1); - const u32 next = _next == funcs.end() ? end : _next->first; + const u32 next = _next == fmap.end() ? end : _next->first; // Just ensure that functions don't overlap if (func.addr + func.size > next) @@ -1415,7 +1414,7 @@ std::vector ppu_analyse(const std::vector>& se { lib_toc = *TOCs.begin(); - for (auto&& pair : funcs) + for (auto&& pair : fmap) { if (pair.second.toc == 0) { @@ -1425,16 +1424,12 @@ std::vector ppu_analyse(const std::vector>& se } // Convert map to vector (destructive) - std::vector result; - - for (auto&& pair : funcs) + for (auto&& pair : fmap) { auto& func = pair.second; LOG_TRACE(PPU, "Function %s (size=0x%x, toc=0x%x, attr %#x)", func.name, func.size, func.toc, func.attr); - result.emplace_back(std::move(func)); + funcs.emplace_back(std::move(func)); } - LOG_NOTICE(PPU, "Function analysis: %zu functions (%zu enqueued)", result.size(), func_queue.size()); - - return result; + LOG_NOTICE(PPU, "Function analysis: %zu functions (%zu enqueued)", funcs.size(), func_queue.size()); } diff --git a/rpcs3/Emu/Cell/PPUAnalyser.h b/rpcs3/Emu/Cell/PPUAnalyser.h index 1e2a1e9513..9d6fb2e700 100644 --- a/rpcs3/Emu/Cell/PPUAnalyser.h +++ b/rpcs3/Emu/Cell/PPUAnalyser.h @@ -38,11 +38,9 @@ struct ppu_function // PPU Relocation Information struct ppu_reloc { + u32 addr; u32 type; - u32 off; - u32 ptr; - u8 index_value; - u8 index_addr; + u64 data; }; // PPU Segment Information @@ -58,10 +56,14 @@ struct ppu_segment struct ppu_module { std::string name; - std::vector rels; + std::string path; + std::vector relocs; std::vector segs; + std::vector secs; std::vector funcs; - std::vector sections; + + void analyse(u32 lib_toc, u32 entry); + void validate(u32 reloc); }; // Aux @@ -131,10 +133,6 @@ struct ppu_pattern_matrix } }; -extern void ppu_validate(const std::string& fname, const std::vector& funcs, u32 reloc); - -extern std::vector ppu_analyse(const std::vector>& segs, const std::vector>& secs, u32 lib_toc, u32 entry); - // PPU Instruction Type struct ppu_itype { diff --git a/rpcs3/Emu/Cell/PPUModule.cpp b/rpcs3/Emu/Cell/PPUModule.cpp index b8990ab7ed..53b2b90f3c 100644 --- a/rpcs3/Emu/Cell/PPUModule.cpp +++ b/rpcs3/Emu/Cell/PPUModule.cpp @@ -112,7 +112,7 @@ struct ppu_linkage_info bool hle = false; u32 export_addr = 0; std::set imports; - std::set weak_imports; + std::set frefss; }; // FNID -> (export; [imports...]) @@ -342,9 +342,8 @@ static void ppu_initialize_modules(const std::shared_ptr& link } } -// Link variables/weak imports, looks to be literally the same as vrefs -// Don't use bool flag in VREFs, originally it didn't have it -static void ppu_patch_refs(u32 fref, u32 faddr) +// Resolve relocations for variable/function linkage. +static void ppu_patch_refs(std::vector* out_relocs, u32 fref, u32 faddr) { struct ref_t { @@ -357,42 +356,52 @@ static void ppu_patch_refs(u32 fref, u32 faddr) { if (ref->addend) LOG_WARNING(LOADER, "**** REF(%u): Addend value(0x%x, 0x%x)", ref->type, ref->addr, ref->addend); - const auto ref_import = vm::ptr::make(faddr + ref->addend); + const u32 raddr = ref->addr; + const u32 rtype = ref->type; + const u32 rdata = faddr + ref->addend; - const auto addr = faddr + ref->addend; + if (out_relocs) + { + // Register relocation with unpredictable target (data=0) + ppu_reloc _rel; + _rel.addr = raddr; + _rel.type = rtype; + _rel.data = 0; + out_relocs->emplace_back(_rel); + } - // OPs are probably similar to relocations - switch (u32 type = ref->type) + // OPs must be similar to relocations + switch (rtype) { - case 0x1: + case 1: { - const u32 value = vm::_ref(ref->addr) = addr; + const u32 value = vm::_ref(ref->addr) = rdata; LOG_TRACE(LOADER, "**** REF(1): 0x%x <- 0x%x", ref->addr, value); break; } - case 0x4: + case 4: { - const u16 value = vm::_ref(ref->addr) = static_cast(addr); + const u16 value = vm::_ref(ref->addr) = static_cast(rdata); LOG_TRACE(LOADER, "**** REF(4): 0x%x <- 0x%04x (0x%llx)", ref->addr, value, faddr); break; } - case 0x6: + case 6: { - const u16 value = vm::_ref(ref->addr) = static_cast(addr >> 16) + (addr & 0x8000 ? 1 : 0); + const u16 value = vm::_ref(ref->addr) = static_cast(rdata >> 16) + (rdata & 0x8000 ? 1 : 0); LOG_TRACE(LOADER, "**** REF(6): 0x%x <- 0x%04x (0x%llx)", ref->addr, value, faddr); break; } case 57: { - const u16 value = vm::_ref, 0, 14>>(ref->addr) = static_cast(addr) >> 2; + const u16 value = vm::_ref, 0, 14>>(ref->addr) = static_cast(rdata) >> 2; LOG_TRACE(LOADER, "**** REF(57): 0x%x <- 0x%04x (0x%llx)", ref->addr, value, faddr); break; } - default: LOG_ERROR(LOADER, "**** REF(%u): Unknown/Illegal type (0x%x, 0x%x)", ref->type, ref->addr, ref->addend); + default: LOG_ERROR(LOADER, "**** REF(%u): Unknown/Illegal type (0x%x, 0x%x)", rtype, raddr, ref->addend); } } } @@ -523,9 +532,9 @@ static auto ppu_load_exports(const std::shared_ptr& link, u32 //LOG_WARNING(LOADER, "Exported function '%s' in module '%s'", ppu_get_function_name(module_name, fnid), module_name); } - for (const u32 weak_import_addr : flink.weak_imports) + for (const u32 fref : flink.frefss) { - ppu_patch_refs(weak_import_addr, faddr); + ppu_patch_refs(nullptr, fref, faddr); } } } @@ -557,7 +566,7 @@ static auto ppu_load_exports(const std::shared_ptr& link, u32 // Fix imports for (const auto vref : vlink.imports) { - ppu_patch_refs(vref, vaddr); + ppu_patch_refs(nullptr, vref, vaddr); //LOG_WARNING(LOADER, "Exported variable '%s' in module '%s'", ppu_get_variable_name(module_name, vnid), module_name); } } @@ -569,7 +578,7 @@ static auto ppu_load_exports(const std::shared_ptr& link, u32 return result; } -static void ppu_load_imports(const std::shared_ptr& link, u32 imports_start, u32 imports_end) +static void ppu_load_imports(std::vector& relocs, const std::shared_ptr& link, u32 imports_start, u32 imports_end) { for (u32 addr = imports_start; addr < imports_end;) { @@ -577,7 +586,7 @@ static void ppu_load_imports(const std::shared_ptr& link, u32 const std::string module_name(lib.name.get_ptr()); - LOG_NOTICE(LOADER, "** Imported module '%s' (0x%x, 0x%x)", module_name, lib.unk4, lib.unk5); + LOG_NOTICE(LOADER, "** Imported module '%s' (ver=0x%x, attr=0x%x, 0x%x, 0x%x) [0x%x]", module_name, lib.version, lib.attributes, lib.unk4, lib.unk5, addr); if (lib.num_tlsvar) { @@ -607,21 +616,17 @@ static void ppu_load_imports(const std::shared_ptr& link, u32 flink.imports.emplace(faddr); mlink.imported = true; - // Link if available - if (flink.export_addr) - { - vm::write32(faddr, flink.export_addr); - } - else - { - vm::write32(faddr, ppu_function_manager::addr); - } + // Link address (special HLE function by default) + const u32 link_addr = flink.export_addr ? flink.export_addr : ppu_function_manager::addr; - //weak imports, possibly - if (((lib.attributes & 0x2000) == 0x2000) && fnids[i + lib.num_func] != 0) //0x2000 seems to be correct flag + // Write import table + vm::write32(faddr, link_addr); + + // Patch refs if necessary (0x2000 seems to be correct flag indicating the presence of additional info) + if (const u32 frefs = (lib.attributes & 0x2000) ? +fnids[i + lib.num_func] : 0) { - flink.weak_imports.emplace(fnids[i + lib.num_func]); - ppu_patch_refs(fnids[i + lib.num_func], flink.export_addr); + flink.frefss.emplace(frefs); + ppu_patch_refs(&relocs, frefs, link_addr); } //LOG_WARNING(LOADER, "Imported function '%s' in module '%s' (0x%x)", ppu_get_function_name(module_name, fnid), module_name, faddr); @@ -644,10 +649,7 @@ static void ppu_load_imports(const std::shared_ptr& link, u32 mlink.imported = true; // Link if available - if (vlink.export_addr) - { - ppu_patch_refs(vref, vlink.export_addr); - } + ppu_patch_refs(&relocs, vref, vlink.export_addr); //LOG_WARNING(LOADER, "Imported variable '%s' in module '%s' (0x%x)", ppu_get_variable_name(module_name, vnid), module_name, vlink.first); } @@ -656,10 +658,13 @@ static void ppu_load_imports(const std::shared_ptr& link, u32 } } -std::shared_ptr ppu_load_prx(const ppu_prx_object& elf, const std::string& name) +std::shared_ptr ppu_load_prx(const ppu_prx_object& elf, const std::string& path) { - std::vector> segments; - std::vector> sections; + // Create new PRX object + const auto prx = idm::make_ptr(); + + // Access linkage information object + const auto link = fxm::get_always(); for (const auto& prog : elf.progs) { @@ -693,7 +698,12 @@ std::shared_ptr ppu_load_prx(const ppu_prx_object& elf, const std::stri ppu_register_range(addr, mem_size); } - segments.emplace_back(std::make_pair(addr, mem_size)); + ppu_segment _seg; + _seg.addr = addr; + _seg.size = mem_size; + _seg.type = p_type; + _seg.flags = prog.p_flags; + prx->segs.emplace_back(_seg); } break; @@ -714,13 +724,18 @@ std::shared_ptr ppu_load_prx(const ppu_prx_object& elf, const std::stri if (s.sh_type == 1 && addr && size) // TODO: some sections with addr=0 are valid { - for (auto i = 0; i < segments.size(); i++) + for (auto i = 0; i < prx->segs.size(); i++) { const u32 saddr = static_cast(elf.progs[i].p_vaddr); if (addr >= saddr && addr < saddr + elf.progs[i].p_memsz) { // "Relocate" section - sections.emplace_back(std::make_pair(addr - saddr + segments[i].first, size)); + ppu_segment _sec; + _sec.addr = addr - saddr + prx->segs[i].addr; + _sec.size = size; + _sec.type = s.sh_type; + _sec.flags = s.sh_flags & 7; + prx->secs.emplace_back(_sec); break; } } @@ -749,10 +764,13 @@ std::shared_ptr ppu_load_prx(const ppu_prx_object& elf, const std::stri { const auto& rel = reinterpret_cast(prog.bin[i]); - const u32 raddr = vm::cast(segments.at(rel.index_addr).first + rel.offset, HERE); - const u64 rdata = segments.at(rel.index_value).first + rel.ptr.addr(); + ppu_reloc _rel; + const u32 raddr = _rel.addr = vm::cast(prx->segs.at(rel.index_addr).addr + rel.offset, HERE); + const u32 rtype = _rel.type = rel.type; + const u64 rdata = _rel.data = prx->segs.at(rel.index_value).addr + rel.ptr.addr(); + prx->relocs.emplace_back(_rel); - switch (const u32 type = rel.type) + switch (rtype) { case 1: // R_PPC64_ADDR32 { @@ -817,7 +835,12 @@ std::shared_ptr ppu_load_prx(const ppu_prx_object& elf, const std::stri break; } - default: LOG_ERROR(LOADER, "**** RELOCATION(%u): Illegal/Unknown type! (addr=0x%x; 0x%llx)", type, raddr, rdata); + default: LOG_ERROR(LOADER, "**** RELOCATION(%u): Illegal/Unknown type! (addr=0x%x; 0x%llx)", rtype, raddr, rdata); + } + + if (rdata == 0) + { + LOG_TODO(LOADER, "**** RELOCATION(%u): 0x%x <- (zero-based value)", rtype, raddr); } } @@ -826,12 +849,6 @@ std::shared_ptr ppu_load_prx(const ppu_prx_object& elf, const std::stri } } - // Access linkage information object - const auto link = fxm::get_always(); - - // Create new PRX object - auto prx = idm::make_ptr(); - if (!elf.progs.empty() && elf.progs[0].p_paddr) { struct ppu_prx_library_info @@ -847,16 +864,16 @@ std::shared_ptr ppu_load_prx(const ppu_prx_object& elf, const std::stri }; // Access library information (TODO) - const auto& lib_info = vm::cptr(vm::cast(segments[0].first + elf.progs[0].p_paddr - elf.progs[0].p_offset, HERE)); + const auto& lib_info = vm::cptr(vm::cast(prx->segs[0].addr + elf.progs[0].p_paddr - elf.progs[0].p_offset, HERE)); const auto& lib_name = std::string(lib_info->name); LOG_WARNING(LOADER, "Library %s (rtoc=0x%x):", lib_name, lib_info->toc); prx->specials = ppu_load_exports(link, lib_info->exports_start, lib_info->exports_end); - ppu_load_imports(link, lib_info->imports_start, lib_info->imports_end); + ppu_load_imports(prx->relocs, link, lib_info->imports_start, lib_info->imports_end); - prx->funcs = ppu_analyse(segments, sections, lib_info->toc, 0); + prx->analyse(lib_info->toc, 0); } else { @@ -868,7 +885,8 @@ std::shared_ptr ppu_load_prx(const ppu_prx_object& elf, const std::stri prx->exit.set(prx->specials[0x3ab9a95e]); prx->prologue.set(prx->specials[0x0D10FD3F]); prx->epilogue.set(prx->specials[0x330F7005]); - prx->name = name; + prx->name = path.substr(path.find_last_of('/') + 1); + prx->path = path; return prx; } @@ -879,15 +897,12 @@ void ppu_load_exec(const ppu_exec_object& elf) LOG_TODO(LOADER, "'Hook static functions' option deactivated"); } + // Set for delayed initialization in ppu_initialize() + const auto _main = fxm::make(); + // Access linkage information object const auto link = fxm::get_always(); - // Segment info - std::vector> segments; - - // Section info (optional) - std::vector> sections; - // TLS information u32 tls_vaddr = 0; u32 tls_fsize = 0; @@ -904,10 +919,13 @@ void ppu_load_exec(const ppu_exec_object& elf) { LOG_NOTICE(LOADER, "** 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); - const u32 addr = vm::cast(prog.p_vaddr, HERE); - const u32 size = ::narrow(prog.p_memsz, "p_memsz" HERE); - - if (prog.p_type == 0x1 /* LOAD */ && prog.p_memsz) + ppu_segment _seg; + const u32 addr = _seg.addr = vm::cast(prog.p_vaddr, HERE); + const u32 size = _seg.size = ::narrow(prog.p_memsz, "p_memsz" HERE); + const u32 type = _seg.type = prog.p_type; + const u32 flag = _seg.flags = prog.p_flags; + + if (type == 0x1 /* LOAD */ && prog.p_memsz) { if (prog.bin.size() > size || prog.bin.size() != prog.p_filesz) fmt::throw_exception("Invalid binary size (0x%llx, memsz=0x%x)", prog.bin.size(), size); @@ -924,7 +942,8 @@ void ppu_load_exec(const ppu_exec_object& elf) ppu_register_range(addr, size); } - segments.emplace_back(std::make_pair(addr, size)); + // Store only LOAD segments (TODO) + _main->segs.emplace_back(_seg); } } @@ -933,12 +952,15 @@ void ppu_load_exec(const ppu_exec_object& elf) { LOG_NOTICE(LOADER, "** Section: sh_type=0x%x, addr=0x%llx, size=0x%llx, flags=0x%x", s.sh_type, s.sh_addr, s.sh_size, s.sh_flags); - const u32 addr = vm::cast(s.sh_addr); - const u32 size = vm::cast(s.sh_size); + ppu_segment _sec; + const u32 addr = _sec.addr = vm::cast(s.sh_addr); + const u32 size = _sec.size = vm::cast(s.sh_size); + const u32 type = _sec.type = s.sh_type; + const u32 flag = _sec.flags = s.sh_flags & 7; if (s.sh_type == 1 && addr && size) { - sections.emplace_back(std::make_pair(addr, size)); + _main->secs.emplace_back(_sec); } } @@ -1038,7 +1060,7 @@ void ppu_load_exec(const ppu_exec_object& elf) } ppu_load_exports(link, proc_prx_param.libent_start, proc_prx_param.libent_end); - ppu_load_imports(link, proc_prx_param.libstub_start, proc_prx_param.libstub_end); + ppu_load_imports(_main->relocs, link, proc_prx_param.libstub_start, proc_prx_param.libstub_end); } break; } @@ -1209,7 +1231,7 @@ void ppu_load_exec(const ppu_exec_object& elf) { LOG_WARNING(LOADER, "Loading library: %s", name); - auto prx = ppu_load_prx(obj, name); + auto prx = ppu_load_prx(obj, lle_dir + name); if (prx->funcs.empty()) { @@ -1218,7 +1240,7 @@ void ppu_load_exec(const ppu_exec_object& elf) else { // TODO: fix arguments - ppu_validate(lle_dir + name, prx->funcs, prx->funcs[0].addr); + prx->validate(prx->funcs[0].addr); } loaded_modules.emplace_back(std::move(prx)); @@ -1230,17 +1252,15 @@ void ppu_load_exec(const ppu_exec_object& elf) } } - { - // Analyse executable (TODO) - ppu_module _main; - _main.funcs = ppu_analyse(segments, sections, 0, elf.header.e_entry); + // Set path (TODO) + _main->name = ""; + _main->path = vfs::get(Emu.GetPath()); - // Validate analyser results (not required) - ppu_validate(vfs::get(Emu.GetPath()), _main.funcs, 0); + // Analyse executable (TODO) + _main->analyse(0, static_cast(elf.header.e_entry)); - // Set for delayed initialization in ppu_initialize() - fxm::make(std::move(_main)); - } + // Validate analyser results (not required) + _main->validate(0); // Set SDK version g_ps3_sdk_version = sdk_version; @@ -1262,7 +1282,7 @@ void ppu_load_exec(const ppu_exec_object& elf) *argv++ = arg_addr; } - argv -= args.size(); + argv -= ::size32(args); // Initialize main thread auto ppu = idm::make_ptr("main_thread", primary_prio, primary_stacksize); diff --git a/rpcs3/Emu/Cell/PPUThread.cpp b/rpcs3/Emu/Cell/PPUThread.cpp index a6295bf032..9b461b6364 100644 --- a/rpcs3/Emu/Cell/PPUThread.cpp +++ b/rpcs3/Emu/Cell/PPUThread.cpp @@ -982,24 +982,31 @@ extern void ppu_initialize(const ppu_module& info) // Worker threads std::vector jthreads; - // Global variables (pointers) to initialize - std::vector> globals; + // Global variables to initialize + std::vector> globals; // Split module into fragments <= 1 MiB std::size_t fpos = 0; - // Copy module information - ppu_module part = info; + // Difference between function name and current location + const u32 reloc = info.name.empty() ? 0 : info.segs.at(0).addr; while (fpos < info.funcs.size()) { + // First function in current module part const auto fstart = fpos; - std::size_t bsize = 0; - + // Copy module information (TODO: optimize) + ppu_module part = info; part.funcs.clear(); part.funcs.reserve(16000); + // Unique suffix for each module part + const u32 suffix = info.funcs.at(fstart).addr - reloc; + + // Overall block size in bytes + std::size_t bsize = 0; + while (fpos < info.funcs.size()) { auto& func = info.funcs[fpos]; @@ -1018,28 +1025,28 @@ extern void ppu_initialize(const ppu_module& info) entry.addr = block.first; entry.size = block.second; entry.toc = func.toc; - fmt::append(entry.name, "__0x%x", block.first); + fmt::append(entry.name, "__0x%x", block.first - reloc); part.funcs.emplace_back(std::move(entry)); } fpos++; } - part.name.clear(); + // Version, module name and hash: vX-liblv2.sprx-0123456789ABCDEF.obj + std::string obj_name = "v2"; if (info.name.size()) { - part.name += '-'; - part.name += info.name; + obj_name += '-'; + obj_name += info.name; } if (fstart || fpos < info.funcs.size()) { - fmt::append(part.name, "+%06X", info.funcs.at(fstart).addr); + fmt::append(obj_name, "+%06X", suffix); } // Compute module hash - std::string obj_name; { sha1_context ctx; u8 output[20]; @@ -1052,28 +1059,32 @@ extern void ppu_initialize(const ppu_module& info) continue; } - const be_t addr = func.addr; + const be_t addr = func.addr - reloc; const be_t size = func.size; sha1_update(&ctx, reinterpret_cast(&addr), sizeof(addr)); sha1_update(&ctx, reinterpret_cast(&size), sizeof(size)); for (const auto& block : func.blocks) { - if (block.second == 0) + if (block.second == 0 || reloc) { continue; } + // TODO: relocations must be taken into account (TODO) sha1_update(&ctx, vm::ps3::_ptr(block.first), block.second); } + if (reloc) + { + continue; + } + sha1_update(&ctx, vm::ps3::_ptr(func.addr), func.size); } sha1_finish(&ctx, output); - - // Version, module name and hash: vX-liblv2.sprx-0123456789ABCDEF.obj - fmt::append(obj_name, "v2%s-%016X-%s.obj", part.name, reinterpret_cast&>(output), jit.cpu()); + fmt::append(obj_name, "-%016X-%s.obj", reinterpret_cast&>(output), jit.cpu()); } if (Emu.IsStopped()) @@ -1081,19 +1092,49 @@ extern void ppu_initialize(const ppu_module& info) break; } - globals.emplace_back(fmt::format("__mptr%x", part.funcs[0].addr), vm::g_base_addr); - globals.emplace_back(fmt::format("__cptr%x", part.funcs[0].addr), vm::g_exec_addr); + globals.emplace_back(fmt::format("__mptr%x", suffix), (u64)vm::g_base_addr); + globals.emplace_back(fmt::format("__cptr%x", suffix), (u64)vm::g_exec_addr); + + // Initialize segments for relocations + for (u32 i = 0; i < info.segs.size(); i++) + { + globals.emplace_back(fmt::format("__seg%u_%x", i, suffix), info.segs[i].addr); + } + + // Get cache path for this executable + std::string cache_path; + + if (info.name.empty()) + { + cache_path = Emu.GetCachePath(); + } + else + { + cache_path = vfs::get("/dev_flash/"); + + if (info.path.compare(0, cache_path.size(), cache_path) == 0) + { + // Remove prefix for dev_flash files + cache_path.clear(); + } + else + { + cache_path = Emu.GetTitleID(); + } + + cache_path = fs::get_data_dir(cache_path, info.path); + } // Check object file - if (fs::is_file(Emu.GetCachePath() + obj_name)) + if (fs::is_file(cache_path + obj_name)) { semaphore_lock lock(jmutex); - ppu_initialize2(jit, part, Emu.GetCachePath(), obj_name); + ppu_initialize2(jit, part, cache_path, obj_name); continue; } // Create worker thread for compilation - jthreads.emplace_back([&jit, &jmutex, &jcores, obj_name = obj_name, part = std::move(part)]() + jthreads.emplace_back([&jit, &jmutex, &jcores, obj_name = obj_name, part = std::move(part), cache_path = std::move(cache_path)]() { // Set low priority thread_ctrl::set_native_priority(-1); @@ -1109,17 +1150,17 @@ extern void ppu_initialize(const ppu_module& info) // Use another JIT instance jit_compiler jit2({}, g_cfg.core.llvm_cpu); - ppu_initialize2(jit2, part, Emu.GetCachePath(), obj_name); + ppu_initialize2(jit2, part, cache_path, obj_name); } - if (Emu.IsStopped()) + if (Emu.IsStopped() || !fs::is_file(cache_path + obj_name)) { return; } // Proceed with original JIT instance semaphore_lock lock(jmutex); - ppu_initialize2(jit, part, Emu.GetCachePath(), obj_name); + ppu_initialize2(jit, part, cache_path, obj_name); }); } @@ -1145,7 +1186,7 @@ extern void ppu_initialize(const ppu_module& info) { if (block.second) { - ppu_ref(block.first) = ::narrow(jit.get(fmt::format("__0x%x", block.first))); + ppu_ref(block.first) = ::narrow(jit.get(fmt::format("__0x%x", block.first - reloc))); } } } @@ -1155,7 +1196,7 @@ extern void ppu_initialize(const ppu_module& info) { if (u64 addr = jit.get(var.first)) { - *reinterpret_cast(addr) = var.second; + *reinterpret_cast(addr) = var.second; } } #endif @@ -1256,10 +1297,16 @@ static void ppu_initialize2(jit_compiler& jit, const ppu_module& module_part, co }); // Translate - const auto func = translator.Translate(module_part.funcs[fi]); - - // Run optimization passes - pm.run(*func); + if (const auto func = translator.Translate(module_part.funcs[fi])) + { + // Run optimization passes + pm.run(*func); + } + else + { + Emu.Pause(); + return; + } } } diff --git a/rpcs3/Emu/Cell/PPUTranslator.cpp b/rpcs3/Emu/Cell/PPUTranslator.cpp index e8b43ed27b..3d76bbbc99 100644 --- a/rpcs3/Emu/Cell/PPUTranslator.cpp +++ b/rpcs3/Emu/Cell/PPUTranslator.cpp @@ -1,4 +1,4 @@ -#ifdef LLVM_AVAILABLE +#ifdef LLVM_AVAILABLE #include "PPUTranslator.h" #include "PPUThread.h" @@ -18,8 +18,11 @@ PPUTranslator::PPUTranslator(LLVMContext& context, Module* module, const ppu_mod , m_info(info) , m_pure_attr(AttributeSet::get(m_context, AttributeSet::FunctionIndex, {Attribute::NoUnwind, Attribute::ReadNone})) { + // There is no weak linkage on JIT, so let's create variables with different names for each module part + const u32 gsuffix = m_info.name.empty() ? info.funcs[0].addr : info.funcs[0].addr - m_info.segs[0].addr; + // Memory base - m_base = new GlobalVariable(*module, ArrayType::get(GetType(), 0x100000000)->getPointerTo(), true, GlobalValue::ExternalLinkage, 0, fmt::format("__mptr%x", info.funcs[0].addr)); + m_base = new GlobalVariable(*module, ArrayType::get(GetType(), 0x100000000)->getPointerTo(), true, GlobalValue::ExternalLinkage, 0, fmt::format("__mptr%x", gsuffix)); m_base->setInitializer(ConstantPointerNull::get(cast(m_base->getType()->getPointerElementType()))); m_base->setExternallyInitialized(true); @@ -44,7 +47,7 @@ PPUTranslator::PPUTranslator(LLVMContext& context, Module* module, const ppu_mod m_thread_type = StructType::create(m_context, thread_struct, "context_t"); // Callable - m_call = new GlobalVariable(*module, ArrayType::get(GetType(), 0x40000000)->getPointerTo(), true, GlobalValue::ExternalLinkage, 0, fmt::format("__cptr%x", info.funcs[0].addr)); + m_call = new GlobalVariable(*module, ArrayType::get(GetType(), 0x40000000)->getPointerTo(), true, GlobalValue::ExternalLinkage, 0, fmt::format("__cptr%x", gsuffix)); m_call->setInitializer(ConstantPointerNull::get(cast(m_call->getType()->getPointerElementType()))); m_call->setExternallyInitialized(true); @@ -55,6 +58,56 @@ PPUTranslator::PPUTranslator(LLVMContext& context, Module* module, const ppu_mod // Metadata for branch weights m_md_likely = MDTuple::get(m_context, {md_name, md_high, md_low}); m_md_unlikely = MDTuple::get(m_context, {md_name, md_low, md_high}); + + // Create segment variables + for (const auto& seg : m_info.segs) + { + auto gv = new GlobalVariable(*module, GetType(), true, GlobalValue::ExternalLinkage, 0, fmt::format("__seg%u_%x", m_segs.size(), gsuffix)); + gv->setInitializer(ConstantInt::get(GetType(), seg.addr)); + gv->setExternallyInitialized(true); + m_segs.emplace_back(gv); + } + + // Sort relevant relocations (TODO) + const auto caddr = m_info.segs[0].addr; + const auto cend = caddr + m_info.segs[0].size; + + for (const auto& rel : m_info.relocs) + { + if (rel.addr >= caddr && rel.addr < cend) + { + // Check relocation type + switch (rel.type) + { + case 20: + case 22: + case 38: + case 43: + case 44: + case 45: + case 46: + case 51: + case 68: + case 73: + case 78: + { + LOG_ERROR(PPU, "64-bit relocation at 0x%x (%u)", rel.addr, rel.type); + continue; + } + } + + // Align relocation address (TODO) + if (!m_relocs.emplace(rel.addr & ~3, &rel).second) + { + LOG_ERROR(PPU, "Relocation repeated at 0x%x (%u)", rel.addr, rel.type); + } + } + } + + if (!m_info.name.empty()) + { + m_reloc = &m_info.segs[0]; + } } PPUTranslator::~PPUTranslator() @@ -69,8 +122,6 @@ Type* PPUTranslator::GetContextType() Function* PPUTranslator::Translate(const ppu_function& info) { m_function = m_module->getFunction(info.name); - m_start_addr = info.addr; - m_end_addr = info.addr + info.size; std::fill(std::begin(m_globals), std::end(m_globals), nullptr); std::fill(std::begin(m_locals), std::end(m_locals), nullptr); @@ -78,6 +129,10 @@ Function* PPUTranslator::Translate(const ppu_function& info) IRBuilder<> irb(m_entry = BasicBlock::Create(m_context, "__entry", m_function)); m_ir = &irb; + // Instruction address is (m_addr + base) + const u64 base = m_reloc ? m_reloc->addr : 0; + m_addr = info.addr - base; + m_thread = &*m_function->getArgumentList().begin(); m_base_loaded = m_ir->CreateLoad(m_base); @@ -90,7 +145,7 @@ Function* PPUTranslator::Translate(const ppu_function& info) // Create tail call to the check function m_ir->SetInsertPoint(vcheck); - Call(GetType(), "__check", m_thread, m_ir->getInt64(m_start_addr))->setTailCallKind(llvm::CallInst::TCK_Tail); + Call(GetType(), "__check", m_thread, GetAddr())->setTailCallKind(llvm::CallInst::TCK_Tail); m_ir->CreateRetVoid(); m_ir->SetInsertPoint(m_body); @@ -104,28 +159,58 @@ Function* PPUTranslator::Translate(const ppu_function& info) } // Process the instructions - for (m_current_addr = block.first; m_current_addr < block.first + block.second; m_current_addr += 4) + for (m_addr = block.first - base; m_addr < block.first + block.second - base; m_addr += 4) { if (m_body->getTerminator()) { break; } - const u32 op = vm::ps3::read32(vm::cast(m_current_addr)); + // Find the relocation at current address + const auto rel_found = m_relocs.find(m_addr + base); + + if (rel_found != m_relocs.end()) + { + m_rel = rel_found->second; + } + else + { + m_rel = nullptr; + } + + const u32 op = vm::ps3::read32(vm::cast(m_addr + base)); (this->*(s_ppu_decoder.decode(op)))({op}); + + if (m_rel) + { + // This is very bæd + LOG_ERROR(PPU, "LLVM: [0x%x] Unsupported relocation(%u) in '%s'. Please report.", rel_found->first, m_rel->type, m_info.name); + return nullptr; + } } // Finalize current block if necessary (create branch to the next address) if (!m_body->getTerminator()) { FlushRegisters(); - CallFunction(m_current_addr); + CallFunction(m_addr); } } return m_function; } +Value* PPUTranslator::GetAddr(u64 _add) +{ + if (m_reloc) + { + // Load segment address from global variable, compute actual instruction address + return m_ir->CreateAdd(m_ir->getInt64(m_addr + _add), m_ir->CreateLoad(m_segs[m_reloc - m_info.segs.data()])); + } + + return m_ir->getInt64(m_addr + _add); +} + Type* PPUTranslator::ScaleType(Type* type, s32 pow2) { verify(HERE), (type->getScalarType()->isIntegerTy()); @@ -159,9 +244,9 @@ void PPUTranslator::CallFunction(u64 target, Value* indirect) if (!indirect) { - if (target < 0x10000 || target >= -0x10000) + if ((!m_reloc && target < 0x10000) || target >= -0x10000) { - Trap(m_current_addr); + Trap(); return; } @@ -414,11 +499,11 @@ void PPUTranslator::UseCondition(MDNode* hint, Value* cond) if (cond) { - const auto local = BasicBlock::Create(m_context, fmt::format("loc_%llx.cond", m_current_addr), m_function); - const auto next = BasicBlock::Create(m_context, fmt::format("loc_%llx.next", m_current_addr), m_function); + const auto local = BasicBlock::Create(m_context, "__cond", m_function); + const auto next = BasicBlock::Create(m_context, "__next", m_function); m_ir->CreateCondBr(cond, local, next, hint); m_ir->SetInsertPoint(next); - CallFunction(m_current_addr + 4); + CallFunction(m_addr + 4); m_ir->SetInsertPoint(local); } } @@ -462,7 +547,7 @@ void PPUTranslator::WriteMemory(Value* addr, Value* value, bool is_be, u32 align void PPUTranslator::CompilationError(const std::string& error) { - LOG_ERROR(PPU, "[0x%08llx] 0x%08llx: Error: %s", m_start_addr, m_current_addr, error); + LOG_ERROR(PPU, "LLVM: [0x%08x] Error: %s", m_addr + (m_reloc ? m_reloc->addr : 0), error); } @@ -1535,13 +1620,13 @@ void PPUTranslator::VXOR(ppu_opcode_t op) void PPUTranslator::TDI(ppu_opcode_t op) { UseCondition(m_md_unlikely, CheckTrapCondition(op.bo, GetGpr(op.ra), m_ir->getInt64(op.simm16))); - Trap(m_current_addr); + Trap(); } void PPUTranslator::TWI(ppu_opcode_t op) { UseCondition(m_md_unlikely, CheckTrapCondition(op.bo, GetGpr(op.ra, 32), m_ir->getInt32(op.simm16))); - Trap(m_current_addr); + Trap(); } void PPUTranslator::MULLI(ppu_opcode_t op) @@ -1570,8 +1655,15 @@ void PPUTranslator::CMPI(ppu_opcode_t op) void PPUTranslator::ADDIC(ppu_opcode_t op) { + Value* imm = m_ir->getInt64(op.simm16); + + if (m_rel && m_rel->type == 4) + { + imm = SExt(ReadMemory(GetAddr(+2), GetType()), GetType()); + m_rel = nullptr; + } + const auto a = GetGpr(op.ra); - const auto imm = m_ir->getInt64(op.simm16); const auto result = m_ir->CreateAdd(a, imm); SetGpr(op.rd, result); SetCarry(m_ir->CreateICmpULT(result, imm)); @@ -1580,25 +1672,44 @@ void PPUTranslator::ADDIC(ppu_opcode_t op) void PPUTranslator::ADDI(ppu_opcode_t op) { - const auto imm = m_ir->getInt64(op.simm16); + Value* imm = m_ir->getInt64(op.simm16); + + if (m_rel && m_rel->type == 4) + { + imm = SExt(ReadMemory(GetAddr(+2), GetType()), GetType()); + m_rel = nullptr; + } + SetGpr(op.rd, op.ra ? m_ir->CreateAdd(GetGpr(op.ra), imm) : imm); } void PPUTranslator::ADDIS(ppu_opcode_t op) { - const auto imm = m_ir->getInt64(op.simm16 << 16); + Value* imm = m_ir->getInt64(op.simm16 << 16); + + if (m_rel && m_rel->type == 6) + { + imm = m_ir->CreateShl(SExt(ReadMemory(GetAddr(+2), GetType()), GetType()), 16); + m_rel = nullptr; + } + SetGpr(op.rd, op.ra ? m_ir->CreateAdd(GetGpr(op.ra), imm) : imm); } void PPUTranslator::BC(ppu_opcode_t op) { - const u64 target = (op.aa ? 0 : m_current_addr) + op.bt14; + const u64 target = (op.aa ? 0 : m_addr) + op.bt14; + + if (op.aa && m_reloc) + { + CompilationError("Branch with absolute address"); + } UseCondition(CheckBranchProbability(op.bo), CheckBranchCondition(op.bo, op.bi)); if (op.lk) { - m_ir->CreateStore(m_ir->getInt64(m_current_addr + 4), m_ir->CreateStructGEP(nullptr, m_thread, &m_lr - m_locals)); + m_ir->CreateStore(GetAddr(+4), m_ir->CreateStructGEP(nullptr, m_thread, &m_lr - m_locals)); } CallFunction(target); @@ -1612,7 +1723,7 @@ void PPUTranslator::SC(ppu_opcode_t op) } const auto num = GetGpr(11); - RegStore(m_ir->getInt32(m_current_addr), m_cia); + RegStore(Trunc(GetAddr()), m_cia); FlushRegisters(); if (!op.lev && isa(num)) @@ -1635,11 +1746,16 @@ void PPUTranslator::SC(ppu_opcode_t op) void PPUTranslator::B(ppu_opcode_t op) { - const u64 target = (op.aa ? 0 : m_current_addr) + op.bt24; + const u64 target = (op.aa ? 0 : m_addr) + op.bt24; + + if (op.aa && m_reloc) + { + CompilationError("Branch with absolute address"); + } if (op.lk) { - RegStore(m_ir->getInt64(m_current_addr + 4), m_lr); + RegStore(GetAddr(+4), m_lr); } FlushRegisters(); @@ -1663,7 +1779,7 @@ void PPUTranslator::BCLR(ppu_opcode_t op) if (op.lk) { - m_ir->CreateStore(m_ir->getInt64(m_current_addr + 4), m_ir->CreateStructGEP(nullptr, m_thread, &m_lr - m_locals)); + m_ir->CreateStore(GetAddr(+4), m_ir->CreateStructGEP(nullptr, m_thread, &m_lr - m_locals)); } CallFunction(0, target); @@ -1726,7 +1842,7 @@ void PPUTranslator::BCCTR(ppu_opcode_t op) if (op.lk) { - m_ir->CreateStore(m_ir->getInt64(m_current_addr + 4), m_ir->CreateStructGEP(nullptr, m_thread, &m_lr - m_locals)); + m_ir->CreateStore(GetAddr(+4), m_ir->CreateStructGEP(nullptr, m_thread, &m_lr - m_locals)); } CallFunction(0, target); @@ -1870,12 +1986,34 @@ void PPUTranslator::RLWNM(ppu_opcode_t op) void PPUTranslator::ORI(ppu_opcode_t op) { - SetGpr(op.ra, m_ir->CreateOr(GetGpr(op.rs), op.uimm16)); + Value* imm = m_ir->getInt64(op.uimm16); + + if (m_rel && m_rel->type == 4) + { + imm = ZExt(ReadMemory(GetAddr(+2), GetType()), GetType()); + m_rel = nullptr; + } + + SetGpr(op.ra, m_ir->CreateOr(GetGpr(op.rs), imm)); } void PPUTranslator::ORIS(ppu_opcode_t op) { - SetGpr(op.ra, m_ir->CreateOr(GetGpr(op.rs), op.uimm16 << 16)); + Value* imm = m_ir->getInt64(op.uimm16 << 16); + + if (m_rel && m_rel->type == 5) + { + imm = m_ir->CreateShl(ZExt(ReadMemory(GetAddr(+2), GetType()), GetType()), 16); + m_rel = nullptr; + } + + if (m_rel && m_rel->type == 6) + { + imm = m_ir->CreateShl(ZExt(ReadMemory(GetAddr(+2), GetType()), GetType()), 16); + m_rel = nullptr; + } + + SetGpr(op.ra, m_ir->CreateOr(GetGpr(op.rs), imm)); } void PPUTranslator::XORI(ppu_opcode_t op) @@ -2050,7 +2188,7 @@ void PPUTranslator::TW(ppu_opcode_t op) FlushRegisters(); } - Trap(m_current_addr); + Trap(); } void PPUTranslator::LVSL(ppu_opcode_t op) @@ -2266,7 +2404,7 @@ void PPUTranslator::ANDC(ppu_opcode_t op) void PPUTranslator::TD(ppu_opcode_t op) { UseCondition(m_md_unlikely, CheckTrapCondition(op.bo, GetGpr(op.ra), GetGpr(op.rb))); - Trap(m_current_addr); + Trap(); } void PPUTranslator::LVEWX(ppu_opcode_t op) @@ -3136,84 +3274,196 @@ void PPUTranslator::DCBZ(ppu_opcode_t op) void PPUTranslator::LWZ(ppu_opcode_t op) { - SetGpr(op.rd, ReadMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)) : m_ir->getInt64(op.simm16), GetType())); + Value* imm = m_ir->getInt64(op.simm16); + + if (m_rel && m_rel->type == 4) + { + imm = SExt(ReadMemory(GetAddr(+2), GetType()), GetType()); + m_rel = nullptr; + } + + SetGpr(op.rd, ReadMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), imm) : imm, GetType())); } void PPUTranslator::LWZU(ppu_opcode_t op) { - const auto addr = m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)); + Value* imm = m_ir->getInt64(op.simm16); + + if (m_rel && m_rel->type == 4) + { + imm = SExt(ReadMemory(GetAddr(+2), GetType()), GetType()); + m_rel = nullptr; + } + + const auto addr = m_ir->CreateAdd(GetGpr(op.ra), imm); SetGpr(op.rd, ReadMemory(addr, GetType())); SetGpr(op.ra, addr); } void PPUTranslator::LBZ(ppu_opcode_t op) { - SetGpr(op.rd, ReadMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)) : m_ir->getInt64(op.simm16), GetType())); + Value* imm = m_ir->getInt64(op.simm16); + + if (m_rel && m_rel->type == 4) + { + imm = SExt(ReadMemory(GetAddr(+2), GetType()), GetType()); + m_rel = nullptr; + } + + SetGpr(op.rd, ReadMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), imm) : imm, GetType())); } void PPUTranslator::LBZU(ppu_opcode_t op) { - const auto addr = m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)); + Value* imm = m_ir->getInt64(op.simm16); + + if (m_rel && m_rel->type == 4) + { + imm = SExt(ReadMemory(GetAddr(+2), GetType()), GetType()); + m_rel = nullptr; + } + + const auto addr = m_ir->CreateAdd(GetGpr(op.ra), imm); SetGpr(op.rd, ReadMemory(addr, GetType())); SetGpr(op.ra, addr); } void PPUTranslator::STW(ppu_opcode_t op) { - WriteMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)) : m_ir->getInt64(op.simm16), GetGpr(op.rs, 32)); + Value* imm = m_ir->getInt64(op.simm16); + + if (m_rel && m_rel->type == 4) + { + imm = SExt(ReadMemory(GetAddr(+2), GetType()), GetType()); + m_rel = nullptr; + } + + WriteMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), imm) : imm, GetGpr(op.rs, 32)); } void PPUTranslator::STWU(ppu_opcode_t op) { - const auto addr = m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)); + Value* imm = m_ir->getInt64(op.simm16); + + if (m_rel && m_rel->type == 4) + { + imm = SExt(ReadMemory(GetAddr(+2), GetType()), GetType()); + m_rel = nullptr; + } + + const auto addr = m_ir->CreateAdd(GetGpr(op.ra), imm); WriteMemory(addr, GetGpr(op.rs, 32)); SetGpr(op.ra, addr); } void PPUTranslator::STB(ppu_opcode_t op) { - WriteMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)) : m_ir->getInt64(op.simm16), GetGpr(op.rs, 8)); + Value* imm = m_ir->getInt64(op.simm16); + + if (m_rel && m_rel->type == 4) + { + imm = SExt(ReadMemory(GetAddr(+2), GetType()), GetType()); + m_rel = nullptr; + } + + WriteMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), imm) : imm, GetGpr(op.rs, 8)); } void PPUTranslator::STBU(ppu_opcode_t op) { - const auto addr = m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)); + Value* imm = m_ir->getInt64(op.simm16); + + if (m_rel && m_rel->type == 4) + { + imm = SExt(ReadMemory(GetAddr(+2), GetType()), GetType()); + m_rel = nullptr; + } + + const auto addr = m_ir->CreateAdd(GetGpr(op.ra), imm); WriteMemory(addr, GetGpr(op.rs, 8)); SetGpr(op.ra, addr); } void PPUTranslator::LHZ(ppu_opcode_t op) { - SetGpr(op.rd, ReadMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)) : m_ir->getInt64(op.simm16), GetType())); + Value* imm = m_ir->getInt64(op.simm16); + + if (m_rel && m_rel->type == 4) + { + imm = SExt(ReadMemory(GetAddr(+2), GetType()), GetType()); + m_rel = nullptr; + } + + SetGpr(op.rd, ReadMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), imm) : imm, GetType())); } void PPUTranslator::LHZU(ppu_opcode_t op) { - const auto addr = m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)); + Value* imm = m_ir->getInt64(op.simm16); + + if (m_rel && m_rel->type == 4) + { + imm = SExt(ReadMemory(GetAddr(+2), GetType()), GetType()); + m_rel = nullptr; + } + + const auto addr = m_ir->CreateAdd(GetGpr(op.ra), imm); SetGpr(op.rd, ReadMemory(addr, GetType())); SetGpr(op.ra, addr); } void PPUTranslator::LHA(ppu_opcode_t op) { - SetGpr(op.rd, SExt(ReadMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)) : m_ir->getInt64(op.simm16), GetType()), GetType())); + Value* imm = m_ir->getInt64(op.simm16); + + if (m_rel && m_rel->type == 4) + { + imm = SExt(ReadMemory(GetAddr(+2), GetType()), GetType()); + m_rel = nullptr; + } + + SetGpr(op.rd, SExt(ReadMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), imm) : imm, GetType()), GetType())); } void PPUTranslator::LHAU(ppu_opcode_t op) { - const auto addr = m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)); + Value* imm = m_ir->getInt64(op.simm16); + + if (m_rel && m_rel->type == 4) + { + imm = SExt(ReadMemory(GetAddr(+2), GetType()), GetType()); + m_rel = nullptr; + } + + const auto addr = m_ir->CreateAdd(GetGpr(op.ra), imm); SetGpr(op.rd, SExt(ReadMemory(addr, GetType()), GetType())); SetGpr(op.ra, addr); } void PPUTranslator::STH(ppu_opcode_t op) { - WriteMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)) : m_ir->getInt64(op.simm16), GetGpr(op.rs, 16)); + Value* imm = m_ir->getInt64(op.simm16); + + if (m_rel && m_rel->type == 4) + { + imm = SExt(ReadMemory(GetAddr(+2), GetType()), GetType()); + m_rel = nullptr; + } + + WriteMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), imm) : imm, GetGpr(op.rs, 16)); } void PPUTranslator::STHU(ppu_opcode_t op) { - const auto addr = m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)); + Value* imm = m_ir->getInt64(op.simm16); + + if (m_rel && m_rel->type == 4) + { + imm = SExt(ReadMemory(GetAddr(+2), GetType()), GetType()); + m_rel = nullptr; + } + + const auto addr = m_ir->CreateAdd(GetGpr(op.ra), imm); WriteMemory(addr, GetGpr(op.rs, 16)); SetGpr(op.ra, addr); } @@ -3236,77 +3486,181 @@ void PPUTranslator::STMW(ppu_opcode_t op) void PPUTranslator::LFS(ppu_opcode_t op) { - SetFpr(op.frd, ReadMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)) : m_ir->getInt64(op.simm16), GetType())); + Value* imm = m_ir->getInt64(op.simm16); + + if (m_rel && m_rel->type == 4) + { + imm = SExt(ReadMemory(GetAddr(+2), GetType()), GetType()); + m_rel = nullptr; + } + + SetFpr(op.frd, ReadMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), imm) : imm, GetType())); } void PPUTranslator::LFSU(ppu_opcode_t op) { - const auto addr = m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)); + Value* imm = m_ir->getInt64(op.simm16); + + if (m_rel && m_rel->type == 4) + { + imm = SExt(ReadMemory(GetAddr(+2), GetType()), GetType()); + m_rel = nullptr; + } + + const auto addr = m_ir->CreateAdd(GetGpr(op.ra), imm); SetFpr(op.frd, ReadMemory(addr, GetType())); SetGpr(op.ra, addr); } void PPUTranslator::LFD(ppu_opcode_t op) { - SetFpr(op.frd, ReadMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)) : m_ir->getInt64(op.simm16), GetType())); + Value* imm = m_ir->getInt64(op.simm16); + + if (m_rel && m_rel->type == 4) + { + imm = SExt(ReadMemory(GetAddr(+2), GetType()), GetType()); + m_rel = nullptr; + } + + SetFpr(op.frd, ReadMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), imm) : imm, GetType())); } void PPUTranslator::LFDU(ppu_opcode_t op) { - const auto addr = m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)); + Value* imm = m_ir->getInt64(op.simm16); + + if (m_rel && m_rel->type == 4) + { + imm = SExt(ReadMemory(GetAddr(+2), GetType()), GetType()); + m_rel = nullptr; + } + + const auto addr = m_ir->CreateAdd(GetGpr(op.ra), imm); SetFpr(op.frd, ReadMemory(addr, GetType())); SetGpr(op.ra, addr); } void PPUTranslator::STFS(ppu_opcode_t op) { - WriteMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)) : m_ir->getInt64(op.simm16), GetFpr(op.frs, 32)); + Value* imm = m_ir->getInt64(op.simm16); + + if (m_rel && m_rel->type == 4) + { + imm = SExt(ReadMemory(GetAddr(+2), GetType()), GetType()); + m_rel = nullptr; + } + + WriteMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), imm) : imm, GetFpr(op.frs, 32)); } void PPUTranslator::STFSU(ppu_opcode_t op) { - const auto addr = m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)); + Value* imm = m_ir->getInt64(op.simm16); + + if (m_rel && m_rel->type == 4) + { + imm = SExt(ReadMemory(GetAddr(+2), GetType()), GetType()); + m_rel = nullptr; + } + + const auto addr = m_ir->CreateAdd(GetGpr(op.ra), imm); WriteMemory(addr, GetFpr(op.frs, 32)); SetGpr(op.ra, addr); } void PPUTranslator::STFD(ppu_opcode_t op) { - WriteMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)) : m_ir->getInt64(op.simm16), GetFpr(op.frs)); + Value* imm = m_ir->getInt64(op.simm16); + + if (m_rel && m_rel->type == 4) + { + imm = SExt(ReadMemory(GetAddr(+2), GetType()), GetType()); + m_rel = nullptr; + } + + WriteMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), imm) : imm, GetFpr(op.frs)); } void PPUTranslator::STFDU(ppu_opcode_t op) { - const auto addr = m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.simm16)); + Value* imm = m_ir->getInt64(op.simm16); + + if (m_rel && m_rel->type == 4) + { + imm = SExt(ReadMemory(GetAddr(+2), GetType()), GetType()); + m_rel = nullptr; + } + + const auto addr = m_ir->CreateAdd(GetGpr(op.ra), imm); WriteMemory(addr, GetFpr(op.frs)); SetGpr(op.ra, addr); } void PPUTranslator::LD(ppu_opcode_t op) { - SetGpr(op.rd, ReadMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.ds << 2)) : m_ir->getInt64(op.ds << 2), GetType())); + Value* imm = m_ir->getInt64(op.ds << 2); + + if (m_rel && m_rel->type == 57) + { + imm = m_ir->CreateAnd(SExt(ReadMemory(GetAddr(+2), GetType()), GetType()), ~3); + m_rel = nullptr; + } + + SetGpr(op.rd, ReadMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), imm) : imm, GetType())); } void PPUTranslator::LDU(ppu_opcode_t op) { - const auto addr = m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.ds << 2)); + Value* imm = m_ir->getInt64(op.ds << 2); + + if (m_rel && m_rel->type == 57) + { + imm = m_ir->CreateAnd(SExt(ReadMemory(GetAddr(+2), GetType()), GetType()), ~3); + m_rel = nullptr; + } + + const auto addr = m_ir->CreateAdd(GetGpr(op.ra), imm); SetGpr(op.rd, ReadMemory(addr, GetType())); SetGpr(op.ra, addr); } void PPUTranslator::LWA(ppu_opcode_t op) { - SetGpr(op.rd, SExt(ReadMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.ds << 2)) : m_ir->getInt64(op.ds << 2), GetType()))); + Value* imm = m_ir->getInt64(op.ds << 2); + + if (m_rel && m_rel->type == 57) + { + imm = m_ir->CreateAnd(SExt(ReadMemory(GetAddr(+2), GetType()), GetType()), ~3); + m_rel = nullptr; + } + + SetGpr(op.rd, SExt(ReadMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), imm) : imm, GetType()))); } void PPUTranslator::STD(ppu_opcode_t op) { - WriteMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.ds << 2)) : m_ir->getInt64(op.ds << 2), GetGpr(op.rs)); + Value* imm = m_ir->getInt64(op.ds << 2); + + if (m_rel && m_rel->type == 57) + { + imm = m_ir->CreateAnd(SExt(ReadMemory(GetAddr(+2), GetType()), GetType()), ~3); + m_rel = nullptr; + } + + WriteMemory(op.ra ? m_ir->CreateAdd(GetGpr(op.ra), imm) : imm, GetGpr(op.rs)); } void PPUTranslator::STDU(ppu_opcode_t op) { - const auto addr = m_ir->CreateAdd(GetGpr(op.ra), m_ir->getInt64(op.ds << 2)); + Value* imm = m_ir->getInt64(op.ds << 2); + + if (m_rel && m_rel->type == 57) + { + imm = m_ir->CreateAnd(SExt(ReadMemory(GetAddr(+2), GetType()), GetType()), ~3); + m_rel = nullptr; + } + + const auto addr = m_ir->CreateAdd(GetGpr(op.ra), imm); WriteMemory(addr, GetGpr(op.rs)); SetGpr(op.ra, addr); } @@ -3862,7 +4216,7 @@ void PPUTranslator::FCFID(ppu_opcode_t op) void PPUTranslator::UNK(ppu_opcode_t op) { FlushRegisters(); - Call(GetType(), "__error", m_thread, m_ir->getInt64(m_current_addr), m_ir->getInt32(op.opcode))->setTailCallKind(llvm::CallInst::TCK_Tail); + Call(GetType(), "__error", m_thread, GetAddr(), m_ir->getInt32(op.opcode))->setTailCallKind(llvm::CallInst::TCK_Tail); m_ir->CreateRetVoid(); } @@ -4123,9 +4477,9 @@ Value* PPUTranslator::CheckTrapCondition(u32 to, Value* left, Value* right) return trap_condition; } -void PPUTranslator::Trap(u64 addr) +void PPUTranslator::Trap() { - Call(GetType(), "__trap", m_thread, m_ir->getInt64(m_current_addr))->setTailCallKind(llvm::CallInst::TCK_Tail); + Call(GetType(), "__trap", m_thread, GetAddr())->setTailCallKind(llvm::CallInst::TCK_Tail); m_ir->CreateRetVoid(); } diff --git a/rpcs3/Emu/Cell/PPUTranslator.h b/rpcs3/Emu/Cell/PPUTranslator.h index 24f8f70b81..24aed77c3a 100644 --- a/rpcs3/Emu/Cell/PPUTranslator.h +++ b/rpcs3/Emu/Cell/PPUTranslator.h @@ -116,6 +116,9 @@ class PPUTranslator final //: public CPUTranslator // PPU Module const ppu_module& m_info; + // Relevant relocations + std::map m_relocs; + // Attributes for function calls which are "pure" and may be optimized away if their results are unused const llvm::AttributeSet m_pure_attr; @@ -125,14 +128,23 @@ class PPUTranslator final //: public CPUTranslator // LLVM function llvm::Function* m_function; - // Function range - u64 m_start_addr, m_end_addr, m_current_addr; - llvm::MDNode* m_md_unlikely; llvm::MDNode* m_md_likely; + // Current position-independent address + u64 m_addr = 0; + + // Relocation info + const ppu_segment* m_reloc = nullptr; + + // Set by instruction code after processing the relocation + const ppu_reloc* m_rel = nullptr; + /* Variables */ + // Segments + std::vector m_segs; + // Memory base llvm::GlobalVariable* m_base; llvm::Value* m_base_loaded; @@ -179,6 +191,9 @@ class PPUTranslator final //: public CPUTranslator #undef DEF_VALUE public: + // Get current instruction address + llvm::Value* GetAddr(u64 _add = 0); + // Change integer size for integer or integer vector type (by 2^degree) llvm::Type* ScaleType(llvm::Type*, s32 pow2 = 0); @@ -346,8 +361,8 @@ public: // Check condition for trap instructions llvm::Value* CheckTrapCondition(u32 to, llvm::Value* left, llvm::Value* right); - // Emit trap - void Trap(u64 addr); + // Emit trap for current address + void Trap(); // Get condition for branch instructions llvm::Value* CheckBranchCondition(u32 bo, u32 bi); diff --git a/rpcs3/Emu/Cell/lv2/sys_prx.cpp b/rpcs3/Emu/Cell/lv2/sys_prx.cpp index 73d3d6eedd..bf80f0c0ab 100644 --- a/rpcs3/Emu/Cell/lv2/sys_prx.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_prx.cpp @@ -109,7 +109,7 @@ error_code prx_load_module(std::string path, u64 flags, vm::ptr