Files
AnyPS5/core/libs/prx/libSceSysmodule/Export.cpp
T
Phi1ow 75c2ea8548 feat(sysmodule): implement sceSysmoduleLoadModuleInternal and sceSysmoduleUnloadModuleInternal
Two exports were missing. System modules are linked statically, so load and unload are
reference counts over the known module table, exactly like the existing
sceSysmoduleLoadModuleInternalWithArg and sceSysmoduleUnloadModule:

- sceSysmoduleLoadModuleInternal(uint32_t) -> increments the module's load count, returns 0
- sceSysmoduleUnloadModuleInternal(uint32_t) -> decrements it, or 0x80A90003 when the module
  was not loaded

Unknown or zero ids throw, like the sibling internal loader. Adds a CMakeLists.txt so the
library can carry its guest test. Signatures follow the public OpenOrbis SDK headers.

New test core/libs/tests/GuestSysmodule.cpp loads a module twice, unloads it twice and checks
the not-loaded error on a third unload; it fails without the change (the symbols are
undefined) and passes with it.
2026-10-04 21:56:29 +02:00

198 lines
5.9 KiB
C++

#include <cstdint>
#include <cstring>
#include <mutex>
#include <stdexcept>
#include <string>
#include <unordered_map>
#ifdef _WIN32
#include <windows.h>
#include <psapi.h>
#else
#include <fstream>
#endif
#include "SceTypes.hpp"
#include "ModuleTable.hpp"
#include "prx/libc/include/General.hpp"
namespace {
const char* findModuleName(const std::uint32_t id) {
const auto it = kModuleTable.find(id);
return it != kModuleTable.end() ? it->second : nullptr;
}
std::mutex gMutex;
std::unordered_map<std::uint32_t, std::int32_t> gLoadCount;
bool fillModuleInfoForUnwind(std::uint64_t addr, ModuleInfoForUnwind* info) {
#ifdef _WIN32
MEMORY_BASIC_INFORMATION mbi{};
if (!VirtualQuery(reinterpret_cast<LPCVOID>(addr), &mbi, sizeof(mbi))) {
return false;
}
info->st_size = sizeof(ModuleInfoForUnwind);
info->eh_frame_hdr_addr = 0;
info->eh_frame_addr = 0;
info->eh_frame_size = 0;
info->seg0_addr = reinterpret_cast<std::uint64_t>(mbi.BaseAddress);
info->seg0_size = mbi.RegionSize;
char path[4096] = {};
DWORD len = GetMappedFileNameA(GetCurrentProcess(), mbi.BaseAddress, path, sizeof(path) - 1);
path[len] = '\0';
std::strncpy(info->name, path, sizeof(info->name) - 1);
info->name[sizeof(info->name) - 1] = '\0';
return true;
#else
std::ifstream maps("/proc/self/maps");
if (!maps) {
throw std::runtime_error("sceSysmoduleGetModuleInfoForUnwind: failed to open /proc/self/maps");
}
std::string line;
while (std::getline(maps, line)) {
std::uint64_t start = 0;
std::uint64_t end = 0;
char perms[8] = {};
std::uint64_t offset = 0;
unsigned int devMajor = 0;
unsigned int devMinor = 0;
std::uint64_t inode = 0;
char path[4096] = {};
int parsed = std::sscanf(
line.c_str(),
"%llx-%llx %7s %llx %x:%x %llu %4095s",
(unsigned long long*)&start,
(unsigned long long*)&end,
perms,
(unsigned long long*)&offset,
&devMajor,
&devMinor,
(unsigned long long*)&inode,
path
);
if (parsed < 7 || addr < start || addr >= end) {
continue;
}
info->st_size = sizeof(ModuleInfoForUnwind);
std::strncpy(info->name, parsed >= 8 ? path : "", sizeof(info->name) - 1);
info->name[sizeof(info->name) - 1] = '\0';
info->eh_frame_hdr_addr = 0;
info->eh_frame_addr = 0;
info->eh_frame_size = 0;
info->seg0_addr = start;
info->seg0_size = end - start;
return true;
}
return false;
#endif
}
}
extern "C" {
int APS5_VABI sceSysmoduleGetModuleInfoForUnwind(std::uint64_t addr, int flags, ModuleInfoForUnwind* info) {
(void)flags;
if (!fillModuleInfoForUnwind(addr, info)) {
throw std::runtime_error("sceSysmoduleGetModuleInfoForUnwind: address not found");
}
return 0;
}
int APS5_VABI sceSysmoduleIsLoaded(std::uint16_t id) {
if (id == 0) {
throw std::runtime_error("sceSysmoduleIsLoaded: invalid id 0");
}
if (!findModuleName(id)) {
APS5_LOG_OUT("unknown id: %u", static_cast<unsigned>(id));
// throw std::runtime_error(std::string("sceSysmoduleIsLoaded: unknown id ") + std::to_string(id));
return 0;
}
std::lock_guard<std::mutex> lock(gMutex);
auto it = gLoadCount.find(id);
if (it == gLoadCount.end() || it->second < 1) {
return 0x80A90002;
}
return 0;
}
int APS5_VABI sceSysmoduleLoadModule(std::uint16_t id) {
if (id == 0) {
throw std::runtime_error("sceSysmoduleLoadModule: invalid id 0");
}
if (!findModuleName(id)) {
APS5_LOG_OUT("unknown id: %u", static_cast<unsigned>(id));
// throw std::runtime_error(std::string("sceSysmoduleLoadModule: unknown id ") + std::to_string(id));
return 0;
}
std::lock_guard<std::mutex> lock(gMutex);
gLoadCount[id]++;
return 0;
}
int APS5_VABI sceSysmoduleLoadModuleInternalWithArg(std::uint32_t id, int argc, void* argv, std::uint64_t unk, int* ret) {
(void)argc;
(void)argv;
(void)unk;
if ((id & 0x7fffffffu) == 0) {
throw std::runtime_error("sceSysmoduleLoadModuleInternalWithArg: invalid id 0");
}
if (!findModuleName(id)) {
throw std::runtime_error(std::string("sceSysmoduleLoadModuleInternalWithArg: unknown id ") + std::to_string(id));
}
std::lock_guard<std::mutex> lock(gMutex);
gLoadCount[id]++;
if (ret) {
*ret = 0;
}
return 0;
}
int APS5_VABI sceSysmoduleUnloadModule(std::uint16_t id) {
if (id == 0) {
throw std::runtime_error("sceSysmoduleUnloadModule: invalid id 0");
}
if (!findModuleName(id)) {
APS5_LOG_OUT("unknown id: %u", static_cast<unsigned>(id));
return 0;
}
std::lock_guard<std::mutex> lock(gMutex);
auto it = gLoadCount.find(id);
if (it == gLoadCount.end() || it->second < 1) {
return 0x80A90003;
}
it->second--;
return 0;
}
int APS5_VABI sceSysmoduleLoadModuleInternal(std::uint32_t id) {
if ((id & 0x7fffffffu) == 0) {
throw std::runtime_error("sceSysmoduleLoadModuleInternal: invalid id 0");
}
if (!findModuleName(id)) {
throw std::runtime_error(std::string("sceSysmoduleLoadModuleInternal: unknown id ") + std::to_string(id));
}
std::lock_guard<std::mutex> lock(gMutex);
gLoadCount[id]++;
return 0;
}
int APS5_VABI sceSysmoduleUnloadModuleInternal(std::uint32_t id) {
if ((id & 0x7fffffffu) == 0) {
throw std::runtime_error("sceSysmoduleUnloadModuleInternal: invalid id 0");
}
if (!findModuleName(id)) {
throw std::runtime_error(std::string("sceSysmoduleUnloadModuleInternal: unknown id ") + std::to_string(id));
}
std::lock_guard<std::mutex> lock(gMutex);
auto it = gLoadCount.find(id);
if (it == gLoadCount.end() || it->second < 1) {
return 0x80A90003;
}
it->second--;
return 0;
}
}