PRX: Implement multi-referenced library management

This commit is contained in:
Eladash 2022-12-24 06:50:49 +02:00 committed by Ivan
parent 5b95cfda40
commit 820e692e57
6 changed files with 146 additions and 53 deletions

View File

@ -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)
{

View File

@ -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
{

View File

@ -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&) {}

View File

@ -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});
}

View File

@ -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

View File

@ -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;