mirror of
https://github.com/RPCS3/rpcs3.git
synced 2025-01-30 03:32:55 +00:00
PRX: Implement multi-referenced library management
This commit is contained in:
parent
5b95cfda40
commit
820e692e57
@ -22,6 +22,7 @@
|
||||
#include "Emu/Cell/Modules/StaticHLE.h"
|
||||
|
||||
#include <map>
|
||||
#include <span>
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
#include <shared_mutex>
|
||||
@ -154,6 +155,7 @@ struct ppu_linkage_info
|
||||
// Module map
|
||||
std::map<std::string, module_data> modules{};
|
||||
std::map<std::string, atomic_t<bool>, std::less<>> lib_lock;
|
||||
shared_mutex lib_lock_mutex;
|
||||
shared_mutex mutex;
|
||||
};
|
||||
|
||||
@ -601,6 +603,12 @@ static void ppu_patch_refs(std::vector<ppu_reloc>* 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<bool>* loaded_flags = nullptr)
|
||||
{
|
||||
std::unordered_map<u32, u32> 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<const ppu_prx_module_info>(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<ppu_reloc>& 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<bool>& loaded_flags)
|
||||
{
|
||||
auto& _main = g_fxo->get<ppu_module>();
|
||||
auto& link = g_fxo->get<ppu_linkage_info>();
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -307,6 +307,13 @@ std::shared_ptr<void> lv2_prx::load(utils::serial& ar)
|
||||
|
||||
if (seg_count)
|
||||
{
|
||||
std::basic_string<bool> 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<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->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::ptr<sys
|
||||
{
|
||||
std::lock_guard lock(prx->mutex);
|
||||
|
||||
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::ptr<sys
|
||||
return CELL_PRX_ERROR_ERROR;
|
||||
}
|
||||
|
||||
if (prx->exports_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::ptr<sys
|
||||
|
||||
// Thread-safe if called from liblv2.sprx, due to internal lwmutex lock before it
|
||||
prx->state = 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::ptr<sys_
|
||||
{
|
||||
// No error code on invalid state, so throw on unexpected state
|
||||
std::lock_guard lock(prx->mutex);
|
||||
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<sy
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
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<bool>& 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<bool> 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<char> name, vm::ptr<void> opt)
|
||||
@ -799,7 +834,7 @@ error_code _sys_prx_register_module(ppu_thread& ppu, vm::cptr<char> 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<std::basic_string<bool>>());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -63,17 +63,17 @@ struct sys_prx_segment_info_t
|
||||
|
||||
struct sys_prx_module_info_t
|
||||
{
|
||||
be_t<u64> size;
|
||||
char name[30];
|
||||
char version[2];
|
||||
be_t<u32> modattribute;
|
||||
be_t<u32> start_entry;
|
||||
be_t<u32> stop_entry;
|
||||
be_t<u32> all_segments_num;
|
||||
vm::bptr<char> filename;
|
||||
be_t<u32> filename_size;
|
||||
vm::bptr<sys_prx_segment_info_t> segments;
|
||||
be_t<u32> segments_num;
|
||||
be_t<u64> size; // 0
|
||||
char name[30]; // 8
|
||||
char version[2]; // 0x26
|
||||
be_t<u32> modattribute; // 0x28
|
||||
be_t<u32> start_entry; // 0x2c
|
||||
be_t<u32> stop_entry; // 0x30
|
||||
be_t<u32> all_segments_num; // 0x34
|
||||
vm::bptr<char> filename; // 0x38
|
||||
be_t<u32> filename_size; // 0x3c
|
||||
vm::bptr<sys_prx_segment_info_t> segments; // 0x40
|
||||
be_t<u32> segments_num; // 0x44
|
||||
};
|
||||
|
||||
struct sys_prx_module_info_option_t
|
||||
@ -188,14 +188,18 @@ struct lv2_prx final : lv2_obj, ppu_module
|
||||
vm::ptr<s32(u64 callback, u64 argc, vm::ptr<void, u64> argv)> epilogue = vm::null;
|
||||
vm::ptr<s32()> exit = vm::null;
|
||||
|
||||
char module_info_name[28];
|
||||
u8 module_info_version[2];
|
||||
be_t<u16> module_info_attributes;
|
||||
char module_info_name[28]{};
|
||||
u8 module_info_version[2]{};
|
||||
be_t<u16> module_info_attributes{};
|
||||
|
||||
u32 exports_start = umax;
|
||||
u32 exports_end = 0;
|
||||
|
||||
std::basic_string<bool> 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&) {}
|
||||
|
@ -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});
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user