1
0
mirror of https://github.com/RPCS3/rpcs3.git synced 2024-11-22 18:53:28 +01:00

PRX: export functions on start()

This commit is contained in:
Eladash 2022-11-05 17:14:34 +02:00 committed by Ivan
parent 0a35a62235
commit b875a86e1d
5 changed files with 139 additions and 14 deletions

View File

@ -153,6 +153,7 @@ struct ppu_linkage_info
// Module map
std::map<std::string, module_data> modules{};
std::map<std::string, std::shared_ptr<atomic_t<bool>>, std::less<>> lib_lock;
shared_mutex mutex;
};
@ -628,8 +629,43 @@ extern u32 ppu_get_exported_func_addr(u32 fnid, const std::string& module_name)
return g_fxo->get<ppu_linkage_info>().modules[module_name].functions[fnid].export_addr;
}
extern bool ppu_register_library_lock(std::string_view libname, bool lock_lib)
{
auto link = g_fxo->try_get<ppu_linkage_info>();
if (!link || libname.empty())
{
return false;
}
reader_lock lock(link->mutex);
if (auto it = std::as_const(link->lib_lock).find(libname); it != link->lib_lock.cend() && it->second)
{
return lock_lib ? !it->second->test_and_set() : it->second->test_and_reset();
}
if (!lock_lib)
{
// If lock hasn't been installed it wasn't locked in the first place
return false;
}
lock.upgrade();
auto& lib_lock = link->lib_lock.emplace(std::string{libname}, nullptr).first->second;
if (!lib_lock)
{
lib_lock = std::make_shared<atomic_t<bool>>(true);
return true;
}
return !lib_lock->test_and_set();
}
// 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)
static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 exports_end, bool for_observing_callbacks = false)
{
std::unordered_map<u32, u32> result;
@ -663,6 +699,12 @@ static auto ppu_load_exports(ppu_linkage_info* link, u32 exports_start, u32 expo
continue;
}
if (for_observing_callbacks)
{
addr += lib.size ? lib.size : sizeof(ppu_prx_module_info);
continue;
}
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);
@ -873,6 +915,12 @@ void ppu_manual_load_imports_exports(u32 imports_start, u32 imports_size, u32 ex
auto& link = g_fxo->get<ppu_linkage_info>();
ppu_load_exports(&link, exports_start, exports_start + exports_size);
if (!imports_size)
{
return;
}
ppu_load_imports(_main.relocs, &link, imports_start, imports_start + imports_size);
}
@ -1419,10 +1467,13 @@ std::shared_ptr<lv2_prx> ppu_load_prx(const ppu_prx_object& elf, const std::stri
prx->module_info_version[0] = lib_info->version[0];
prx->module_info_version[1] = lib_info->version[1];
prx->module_info_attributes = lib_info->attributes;
prx->exports_start = lib_info->exports_start;
prx->exports_end = lib_info->exports_end;
ppu_loader.warning("Library %s (rtoc=0x%x):", lib_name, lib_info->toc);
prx->specials = ppu_load_exports(&link, lib_info->exports_start, lib_info->exports_end);
prx->specials = ppu_load_exports(&link, prx->exports_start, prx->exports_end, true);
prx->imports = ppu_load_imports(prx->relocs, &link, lib_info->imports_start, lib_info->imports_end);
std::stable_sort(prx->relocs.begin(), prx->relocs.end());
toc = lib_info->toc;
@ -2055,6 +2106,8 @@ bool ppu_load_exec(const ppu_exec_object& elf, utils::serial* ar)
ppu_loader.warning("Loading library: %s", name);
auto prx = ppu_load_prx(obj, lle_dir + name, 0, nullptr);
prx->state = PRX_STATE_STARTED;
prx->load_exports();
if (prx->funcs.empty())
{

View File

@ -3192,6 +3192,13 @@ extern void ppu_initialize()
if (ptr->path.starts_with(firmware_sprx_path))
{
compile_fw |= ppu_initialize(*ptr, true);
// Fixup for compatibility with old savestates
if (Emu.DeserialManager() && ptr->name == "liblv2.sprx")
{
static_cast<lv2_prx*>(ptr)->state = PRX_STATE_STARTED;
static_cast<lv2_prx*>(ptr)->load_exports();
}
}
}
}

View File

@ -170,6 +170,8 @@ extern const std::map<std::string_view, int> g_prx_list
{ "libwmadec.sprx", 0 },
};
bool ppu_register_library_lock(std::string_view libname, bool lock_lib);
static error_code prx_load_module(const std::string& vpath, u64 flags, vm::ptr<sys_prx_load_module_option_t> /*pOpt*/, fs::file src = {}, s64 file_offset = 0)
{
if (flags != 0)
@ -191,16 +193,6 @@ static error_code prx_load_module(const std::string& vpath, u64 flags, vm::ptr<s
const std::string path = vfs::get(vpath, nullptr, &vpath0);
const std::string name = vpath0.substr(vpath0.find_last_of('/') + 1);
const auto existing = idm::select<lv2_obj, lv2_prx>([&](u32, lv2_prx& prx)
{
return prx.path == path && prx.offset == file_offset;
});
if (existing)
{
return CELL_PRX_ERROR_LIBRARY_FOUND;
}
bool ignore = false;
constexpr std::string_view firmware_sprx_dir = "/dev_flash/sys/external/";
@ -295,6 +287,8 @@ fs::file make_file_view(fs::file&& _file, u64 offset);
std::shared_ptr<void> lv2_prx::load(utils::serial& ar)
{
[[maybe_unused]] const s32 version = GET_SERIALIZATION_VERSION(lv2_prx_overlay);
const std::string path = vfs::get(ar.operator std::string());
const s64 offset = ar;
const u32 state = ar;
@ -320,6 +314,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);
if (version >= 2 && state == PRX_STATE_STARTED)
{
ensure(ppu_register_library_lock(prx->module_info_name, true));
prx->load_exports();
}
if (version == 1)
{
prx->load_exports();
}
ensure(prx);
}
else
@ -498,9 +504,35 @@ error_code _sys_prx_start_module(ppu_thread& ppu, u32 id, u64 flags, vm::ptr<sys
{
case 1:
{
std::lock_guard lock(prx->mutex);
if (prx->state != PRX_STATE_INITIALIZED)
{
if (prx->state == PRX_STATE_DESTROYED)
{
return CELL_ESRCH;
}
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;
}
@ -590,6 +622,7 @@ error_code _sys_prx_stop_module(ppu_thread& ppu, u32 id, u64 flags, vm::ptr<sys_
case PRX_STATE_STOPPED: return CELL_PRX_ERROR_ALREADY_STOPPED;
case PRX_STATE_STOPPING: return CELL_PRX_ERROR_ALREADY_STOPPING; // Internal error
case PRX_STATE_STARTING: return CELL_PRX_ERROR_ERROR; // Internal error
case PRX_STATE_DESTROYED: return CELL_ESRCH;
case PRX_STATE_STARTED: break;
default:
fmt::throw_exception("Invalid prx state (%d)", old);
@ -607,6 +640,8 @@ error_code _sys_prx_stop_module(ppu_thread& ppu, u32 id, u64 flags, vm::ptr<sys_
case 0:
{
// 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->state.compare_and_swap_test(PRX_STATE_STOPPING, PRX_STATE_STOPPED));
return CELL_OK;
}
@ -628,6 +663,7 @@ error_code _sys_prx_stop_module(ppu_thread& ppu, u32 id, u64 flags, vm::ptr<sys_
case PRX_STATE_STOPPED: return CELL_PRX_ERROR_ALREADY_STOPPED;
case PRX_STATE_STOPPING: return CELL_PRX_ERROR_ALREADY_STOPPING; // Internal error
case PRX_STATE_STARTING: return CELL_PRX_ERROR_ERROR; // Internal error
case PRX_STATE_DESTROYED: return CELL_ESRCH;
case PRX_STATE_STARTED: break;
default:
fmt::throw_exception("Invalid prx state (%d)", old);
@ -660,7 +696,16 @@ error_code _sys_prx_unload_module(ppu_thread& ppu, u32 id, u64 flags, vm::ptr<sy
// Get the PRX, free the used memory and delete the object and its ID
const auto prx = idm::withdraw<lv2_obj, lv2_prx>(id, [](lv2_prx& prx) -> CellPrxError
{
switch (prx.state)
switch (prx.state.fetch_op([](u32& value)
{
if (value == PRX_STATE_INITIALIZED || value == PRX_STATE_STOPPED)
{
value = PRX_STATE_DESTROYED;
return true;
}
return false;
}).first)
{
case PRX_STATE_INITIALIZED:
case PRX_STATE_STOPPED:
@ -683,6 +728,8 @@ error_code _sys_prx_unload_module(ppu_thread& ppu, u32 id, u64 flags, vm::ptr<sy
sys_prx.success("_sys_prx_unload_module(id=0x%x, flags=0x%x, pOpt=*0x%x): name='%s'", id, flags, pOpt, prx->name);
prx->mutex.lock_unlock();
ppu_unload_prx(*prx);
ppu_finalize(*prx);
@ -694,6 +741,17 @@ error_code _sys_prx_unload_module(ppu_thread& ppu, u32 id, u64 flags, vm::ptr<sy
void ppu_manual_load_imports_exports(u32 imports_start, u32 imports_size, u32 exports_start, u32 exports_size);
void lv2_prx::load_exports()
{
if (exports_end <= exports_start)
{
// Nothing to load
return;
}
ppu_manual_load_imports_exports(0, 0, exports_start, exports_end - exports_start);
}
error_code _sys_prx_register_module(ppu_thread& ppu, vm::cptr<char> name, vm::ptr<void> opt)
{
ppu.state += cpu_flag::wait;

View File

@ -169,6 +169,7 @@ enum : u32
PRX_STATE_STARTED,
PRX_STATE_STOPPING, // In-between state between started and stopped (internal)
PRX_STATE_STOPPED, // Last state, the module cannot be restarted
PRX_STATE_DESTROYED, // Last state, the module cannot be restarted
};
struct lv2_prx final : lv2_obj, ppu_module
@ -176,6 +177,7 @@ struct lv2_prx final : lv2_obj, ppu_module
static const u32 id_base = 0x23000000;
atomic_t<u32> state = PRX_STATE_INITIALIZED;
shared_mutex mutex;
std::unordered_map<u32, u32> specials;
std::unordered_map<u32, void*> imports;
@ -190,6 +192,11 @@ struct lv2_prx final : lv2_obj, ppu_module
u8 module_info_version[2];
be_t<u16> module_info_attributes;
u32 exports_start = umax;
u32 exports_end = 0;
void load_exports(); // (Re)load exports
lv2_prx() noexcept = default;
lv2_prx(utils::serial&) {}
static std::shared_ptr<void> load(utils::serial&);

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)
SERIALIZATION_VER(lv2_prx_overlay, 7, 1, 2/*PRX dynamic exports*/)
SERIALIZATION_VER(lv2_memory, 8, 1)
SERIALIZATION_VER(lv2_config, 9, 1)