diff --git a/rpcs3/Emu/Cell/PPUModule.cpp b/rpcs3/Emu/Cell/PPUModule.cpp index dfdadb2a41..e59a9dbc87 100644 --- a/rpcs3/Emu/Cell/PPUModule.cpp +++ b/rpcs3/Emu/Cell/PPUModule.cpp @@ -22,6 +22,7 @@ #include "Emu/Cell/Modules/StaticHLE.h" #include +#include #include #include #include @@ -154,6 +155,7 @@ struct ppu_linkage_info // Module map std::map modules{}; std::map, std::less<>> lib_lock; + shared_mutex lib_lock_mutex; shared_mutex mutex; }; @@ -601,6 +603,12 @@ static void ppu_patch_refs(std::vector* out_relocs, u32 fref, u32 fad } } +enum PRX_EXPORT_ATTRIBUTES : u16 +{ + PRX_EXPORT_LIBRARY_FLAG = 1, + PRX_EXPORT_PRX_MANAGEMENT_FUNCTIONS_FLAG = 0x8000, +}; + // Export or import module struct struct ppu_prx_module_info { @@ -639,7 +647,7 @@ extern bool ppu_register_library_lock(std::string_view libname, bool lock_lib) return false; } - reader_lock lock(link->mutex); + reader_lock lock(link->lib_lock_mutex); if (auto it = link->lib_lock.find(libname); it != link->lib_lock.cend()) { @@ -660,17 +668,31 @@ extern bool ppu_register_library_lock(std::string_view libname, bool lock_lib) } // Load and register exports; return special exports found (nameless module) -static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 exports_end, bool for_observing_callbacks = false) +static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 exports_end, bool for_observing_callbacks = false, std::basic_string* loaded_flags = nullptr) { std::unordered_map result; + // Flags were already provided meaning it's an unload operation + const bool unload_exports = loaded_flags && !loaded_flags->empty(); + std::lock_guard lock(link->mutex); - for (u32 addr = exports_start; addr < exports_end;) - { - const auto& lib = vm::_ref(addr); + usz unload_index = 0; - if (!lib.name) + for (u32 addr = exports_start; addr < exports_end; unload_index++) + { + ppu_prx_module_info lib{}; + std::memcpy(&lib, vm::base(addr), sizeof(lib)); + + const bool is_library = !!(lib.attributes & PRX_EXPORT_LIBRARY_FLAG); + const bool is_management = !is_library && !!(lib.attributes & PRX_EXPORT_PRX_MANAGEMENT_FUNCTIONS_FLAG); + + if (loaded_flags && !unload_exports) + { + loaded_flags->push_back(false); + } + + if (is_management) { // Set special exports for (u32 i = 0, end = lib.num_func + lib.num_var; i < end; i++) @@ -694,6 +716,13 @@ static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 expo continue; } + if (!is_library) + { + // Skipped if none of the flags is set + addr += lib.size ? lib.size : sizeof(ppu_prx_module_info); + continue; + } + if (for_observing_callbacks) { addr += lib.size ? lib.size : sizeof(ppu_prx_module_info); @@ -702,13 +731,38 @@ static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 expo const std::string module_name(lib.name.get_ptr()); - ppu_loader.notice("** Exported module '%s' (0x%x, 0x%x, 0x%x, 0x%x)", module_name, lib.vnids, lib.vstubs, lib.unk4, lib.unk5); + if (unload_exports) + { + if (::at32(*loaded_flags, unload_index)) + { + ppu_register_library_lock(module_name, false); + } + + addr += lib.size ? lib.size : sizeof(ppu_prx_module_info); + continue; + } + + ppu_loader.notice("** Exported module '%s' (vnids=0x%x, vstubs=0x%x, version=0x%x, attributes=0x%x, unk4=0x%x, unk5=0x%x)", module_name, lib.vnids, lib.vstubs, lib.version, lib.attributes, lib.unk4, lib.unk5); if (lib.num_tlsvar) { ppu_loader.fatal("Unexpected num_tlsvar (%u)!", lib.num_tlsvar); } + const bool should_load = ppu_register_library_lock(module_name, true); + + if (loaded_flags) + { + loaded_flags->back() = should_load; + } + + if (!should_load) + { + ppu_loader.notice("** Skipped module '%s' (already loaded)", module_name); + addr += lib.size ? lib.size : sizeof(ppu_prx_module_info); + continue; + } + // Static module const auto _sm = ppu_module_manager::get_module(module_name); @@ -904,12 +958,12 @@ static auto ppu_load_imports(std::vector& relocs, ppu_linkage_info* l } // For _sys_prx_register_module -void ppu_manual_load_imports_exports(u32 imports_start, u32 imports_size, u32 exports_start, u32 exports_size) +void ppu_manual_load_imports_exports(u32 imports_start, u32 imports_size, u32 exports_start, u32 exports_size, std::basic_string& loaded_flags) { auto& _main = g_fxo->get(); auto& link = g_fxo->get(); - ppu_load_exports(&link, exports_start, exports_start + exports_size); + ppu_load_exports(&link, exports_start, exports_start + exports_size, false, &loaded_flags); if (!imports_size) { diff --git a/rpcs3/Emu/Cell/lv2/sys_prx.cpp b/rpcs3/Emu/Cell/lv2/sys_prx.cpp index a8539f1332..a8c47f29fa 100644 --- a/rpcs3/Emu/Cell/lv2/sys_prx.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_prx.cpp @@ -307,6 +307,13 @@ std::shared_ptr lv2_prx::load(utils::serial& ar) if (seg_count) { + std::basic_string loaded_flags; + + if (version >= 3) + { + ar(loaded_flags); + } + fs::file file{path.substr(0, path.size() - (offset ? fmt::format("_x%x", offset).size() : 0))}; if (file) @@ -314,13 +321,18 @@ std::shared_ptr lv2_prx::load(utils::serial& ar) u128 klic = g_fxo->get().last_key(); file = make_file_view(std::move(file), offset); prx = ppu_load_prx(ppu_prx_object{ decrypt_self(std::move(file), reinterpret_cast(&klic)) }, path, 0, &ar); + prx->m_loaded_flags = std::move(loaded_flags); - if (version >= 2 && state == PRX_STATE_STARTED) + if (version == 2 && state == PRX_STATE_STARTED) { - ensure(ppu_register_library_lock(prx->module_info_name, true)); prx->load_exports(); } + if (version == 3 && state == PRX_STATE_STARTED) + { + prx->restore_exports(); + } + if (version == 1) { prx->load_exports(); @@ -361,6 +373,11 @@ void lv2_prx::save(utils::serial& ar) // Save segments count ar.serialize_vle(segs.size()); + if (!segs.empty()) + { + ar(m_loaded_flags); + } + for (const ppu_segment& seg : segs) { if (seg.type == 0x1u && seg.size) ar(seg.addr); @@ -506,7 +523,7 @@ error_code _sys_prx_start_module(ppu_thread& ppu, u32 id, u64 flags, vm::ptrmutex); - if (prx->state != PRX_STATE_INITIALIZED) + if (!prx->state.compare_and_swap_test(PRX_STATE_INITIALIZED, PRX_STATE_STARTING)) { if (prx->state == PRX_STATE_DESTROYED) { @@ -516,26 +533,7 @@ error_code _sys_prx_start_module(ppu_thread& ppu, u32 id, u64 flags, vm::ptrexports_end > prx->exports_start && !ppu_register_library_lock(prx->module_info_name, true)) - { - return {CELL_PRX_ERROR_LIBRARY_FOUND, +prx->module_info_name}; - } - prx->load_exports(); - - if (!prx->state.compare_and_swap_test(PRX_STATE_INITIALIZED, PRX_STATE_STARTING)) - { - // The only error code here - ensure(prx->exports_end <= prx->exports_start || ppu_register_library_lock(prx->module_info_name, false)); - - if (prx->state == PRX_STATE_DESTROYED) - { - return CELL_ESRCH; - } - - return CELL_PRX_ERROR_ERROR; - } - break; } case 2: @@ -557,6 +555,7 @@ error_code _sys_prx_start_module(ppu_thread& ppu, u32 id, u64 flags, vm::ptrstate = PRX_STATE_STOPPED; + prx->unload_exports(); _sys_prx_unload_module(ppu, id, 0, vm::null); // Return the exact value returned by the start function (as an error) @@ -641,7 +640,10 @@ error_code _sys_prx_stop_module(ppu_thread& ppu, u32 id, u64 flags, vm::ptrmutex); - ensure(prx->exports_end <= prx->exports_start || (prx->state == PRX_STATE_STOPPING && ppu_register_library_lock(prx->module_info_name, false))); + ensure(prx->exports_end <= prx->exports_start || (prx->state == PRX_STATE_STOPPING)); + + prx->unload_exports(); + ensure(prx->state.compare_and_swap_test(PRX_STATE_STOPPING, PRX_STATE_STOPPED)); return CELL_OK; } @@ -739,7 +741,7 @@ error_code _sys_prx_unload_module(ppu_thread& ppu, u32 id, u64 flags, vm::ptr& loaded_flags); void lv2_prx::load_exports() { @@ -749,7 +751,40 @@ void lv2_prx::load_exports() return; } - ppu_manual_load_imports_exports(0, 0, exports_start, exports_end - exports_start); + if (!m_loaded_flags.empty()) + { + // Already loaded + return; + } + + ppu_manual_load_imports_exports(0, 0, exports_start, exports_end - exports_start, m_loaded_flags); +} + +void lv2_prx::restore_exports() +{ + constexpr usz sizeof_export_data = 0x1C; + + std::basic_string loaded_flags_empty; + + for (usz start = exports_start, i = 0; start < exports_end; i++, start += sizeof_export_data) + { + if (::at32(m_loaded_flags, i)) + { + loaded_flags_empty.clear(); + ppu_manual_load_imports_exports(0, 0, start, sizeof_export_data, loaded_flags_empty); + } + } +} + +void lv2_prx::unload_exports() +{ + if (m_loaded_flags.empty()) + { + // Not loaded + return; + } + + ppu_manual_load_imports_exports(0, 0, exports_start, exports_end - exports_start, m_loaded_flags); } error_code _sys_prx_register_module(ppu_thread& ppu, vm::cptr name, vm::ptr opt) @@ -799,7 +834,7 @@ error_code _sys_prx_register_module(ppu_thread& ppu, vm::cptr name, vm::pt { if (g_ps3_process_info.get_cellos_appname() == "vsh.self"sv) { - ppu_manual_load_imports_exports(info.lib_stub_ea.addr(), info.lib_stub_size, info.lib_entries_ea.addr(), info.lib_entries_size); + ppu_manual_load_imports_exports(info.lib_stub_ea.addr(), info.lib_stub_size, info.lib_entries_ea.addr(), info.lib_entries_size, *std::make_unique>()); } else { diff --git a/rpcs3/Emu/Cell/lv2/sys_prx.h b/rpcs3/Emu/Cell/lv2/sys_prx.h index b152312788..fc7c6f4381 100644 --- a/rpcs3/Emu/Cell/lv2/sys_prx.h +++ b/rpcs3/Emu/Cell/lv2/sys_prx.h @@ -63,17 +63,17 @@ struct sys_prx_segment_info_t struct sys_prx_module_info_t { - be_t size; - char name[30]; - char version[2]; - be_t modattribute; - be_t start_entry; - be_t stop_entry; - be_t all_segments_num; - vm::bptr filename; - be_t filename_size; - vm::bptr segments; - be_t segments_num; + be_t size; // 0 + char name[30]; // 8 + char version[2]; // 0x26 + be_t modattribute; // 0x28 + be_t start_entry; // 0x2c + be_t stop_entry; // 0x30 + be_t all_segments_num; // 0x34 + vm::bptr filename; // 0x38 + be_t filename_size; // 0x3c + vm::bptr segments; // 0x40 + be_t segments_num; // 0x44 }; struct sys_prx_module_info_option_t @@ -188,14 +188,18 @@ struct lv2_prx final : lv2_obj, ppu_module vm::ptr argv)> epilogue = vm::null; vm::ptr exit = vm::null; - char module_info_name[28]; - u8 module_info_version[2]; - be_t module_info_attributes; + char module_info_name[28]{}; + u8 module_info_version[2]{}; + be_t module_info_attributes{}; u32 exports_start = umax; u32 exports_end = 0; + std::basic_string m_loaded_flags; + void load_exports(); // (Re)load exports + void restore_exports(); // For savestates + void unload_exports(); lv2_prx() noexcept = default; lv2_prx(utils::serial&) {} diff --git a/rpcs3/Emu/Cell/lv2/sys_timer.cpp b/rpcs3/Emu/Cell/lv2/sys_timer.cpp index 2c3712ba65..84ef5ba281 100644 --- a/rpcs3/Emu/Cell/lv2/sys_timer.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_timer.cpp @@ -396,7 +396,7 @@ error_code sys_timer_sleep(ppu_thread& ppu, u32 sleep_time) { ppu.state += cpu_flag::wait; - sys_timer.warning("sys_timer_sleep(sleep_time=%d)", sleep_time); + sys_timer.trace("sys_timer_sleep(sleep_time=%d)", sleep_time); return sys_timer_usleep(ppu, sleep_time * u64{1000000}); } diff --git a/rpcs3/Emu/savestate_utils.cpp b/rpcs3/Emu/savestate_utils.cpp index 12c348cb6b..5ca35f771d 100644 --- a/rpcs3/Emu/savestate_utils.cpp +++ b/rpcs3/Emu/savestate_utils.cpp @@ -42,7 +42,7 @@ SERIALIZATION_VER(lv2_sync, 3, 1) SERIALIZATION_VER(lv2_vm, 4, 1) SERIALIZATION_VER(lv2_net, 5, 1) SERIALIZATION_VER(lv2_fs, 6, 1) -SERIALIZATION_VER(lv2_prx_overlay, 7, 1, 2/*PRX dynamic exports*/) +SERIALIZATION_VER(lv2_prx_overlay, 7, 1, 2/*PRX dynamic exports*/, 3/*Conditionally Loaded Local Exports*/) SERIALIZATION_VER(lv2_memory, 8, 1) SERIALIZATION_VER(lv2_config, 9, 1) @@ -57,7 +57,7 @@ namespace np } #ifdef _MSC_VER -// Compiler bug, lambda function body does seem to inherit used namespace atleast for function decleration +// Compiler bug, lambda function body does seem to inherit used namespace atleast for function declaration SERIALIZATION_VER(rsx, 10) SERIALIZATION_VER(sceNp, 11) #endif diff --git a/rpcs3/rpcs3qt/kernel_explorer.cpp b/rpcs3/rpcs3qt/kernel_explorer.cpp index 3e7b25e811..eb42958c70 100644 --- a/rpcs3/rpcs3qt/kernel_explorer.cpp +++ b/rpcs3/rpcs3qt/kernel_explorer.cpp @@ -466,7 +466,7 @@ void kernel_explorer::update() break; } - const QString text = qstr(fmt::format("PRX 0x%08x: '%s'", id, prx.name)); + const QString text = qstr(fmt::format("PRX 0x%08x: '%s', attr=0x%x, lib=%s", id, prx.name, prx.module_info_attributes, prx.module_info_name)); QTreeWidgetItem* prx_tree = add_solid_node(node, text, text); display_program_segments(prx_tree, prx); break;