#include #include #include #include #include #include #include #ifdef _WIN32 #include #include #else #include "prx/libc/include/specifics/linux/HostMapping.hpp" #endif #include "SceTypes.hpp" #include "ModuleTable.hpp" #include "prx/libc/include/General.hpp" extern "C" void* APS5_VABI dlopen_nid_postfix(const char* path, int flags); namespace { constexpr int SCE_SYSMODULE_ERROR_INVALID_VALUE = static_cast(0x805A1000); constexpr int SCE_SYSMODULE_ERROR_UNLOADED = static_cast(0x805A1001); constexpr int kRtldNow = 2; 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 gLoadCount; std::unordered_map gHandles; void* openHostModule(const char* name) { const std::string relative = std::string("libs/") + name + ".prx"; return dlopen_nid_postfix(relative.c_str(), kRtldNow); } #ifdef _WIN32 struct ImageRegion { std::uint64_t end = 0; std::uintptr_t image = 0; DWORD stamp = 0; DWORD size = 0; std::string name; }; std::mutex gRegionsMutex; std::map gRegions; bool imageIdentity(std::uintptr_t image, DWORD& stamp, DWORD& size) { const auto* dos = reinterpret_cast(image); if (dos->e_magic != IMAGE_DOS_SIGNATURE) return false; const auto* nt = reinterpret_cast(image + dos->e_lfanew); if (nt->Signature != IMAGE_NT_SIGNATURE) return false; stamp = nt->FileHeader.TimeDateStamp; size = nt->OptionalHeader.SizeOfImage; return true; } std::uintptr_t loaderImage(std::uint64_t addr) { PVOID image = nullptr; if (RtlPcToFileHeader(reinterpret_cast(addr), &image) == nullptr) return 0; return reinterpret_cast(image); } void writeUnwindRegion(std::uint64_t begin, std::uint64_t size, const std::string& name, ModuleInfoForUnwind* info) { info->st_size = sizeof(ModuleInfoForUnwind); info->eh_frame_hdr_addr = 0; info->eh_frame_addr = 0; info->eh_frame_size = 0; info->seg0_addr = begin; info->seg0_size = size; std::strncpy(info->name, name.c_str(), sizeof(info->name) - 1); info->name[sizeof(info->name) - 1] = '\0'; } bool fillCachedImageRegion(std::uint64_t addr, std::uintptr_t image, ModuleInfoForUnwind* info) { std::lock_guard lock(gRegionsMutex); auto found = gRegions.upper_bound(addr); if (found == gRegions.begin()) return false; --found; const auto& region = found->second; if (addr >= region.end) return false; DWORD stamp = 0; DWORD size = 0; if (region.image != image || !imageIdentity(image, stamp, size) || stamp != region.stamp || size != region.size) { gRegions.erase(found); return false; } constexpr std::uint64_t page = 4096; const std::uint64_t begin = addr & ~(page - 1); writeUnwindRegion(begin, region.end - begin, region.name, info); return true; } #endif bool fillModuleInfoForUnwind(std::uint64_t addr, ModuleInfoForUnwind* info) { #ifdef _WIN32 const auto image = loaderImage(addr); if (image != 0 && fillCachedImageRegion(addr, image, info)) return true; MEMORY_BASIC_INFORMATION mbi{}; if (!VirtualQuery(reinterpret_cast(addr), &mbi, sizeof(mbi))) { return false; } char path[4096] = {}; DWORD len = GetMappedFileNameA(GetCurrentProcess(), mbi.BaseAddress, path, sizeof(path) - 1); path[len] = '\0'; const auto begin = reinterpret_cast(mbi.BaseAddress); writeUnwindRegion(begin, mbi.RegionSize, path, info); ImageRegion region; if (mbi.Type == MEM_IMAGE && image != 0 && imageIdentity(image, region.stamp, region.size) && begin >= image && begin + mbi.RegionSize <= image + region.size) { region.end = begin + mbi.RegionSize; region.image = image; region.name = path; std::lock_guard lock(gRegionsMutex); gRegions.insert_or_assign(begin, std::move(region)); } return true; #else const auto mapping = HostMapping::Find(addr, "sceSysmoduleGetModuleInfoForUnwind"); if (!mapping) { return false; } info->st_size = sizeof(ModuleInfoForUnwind); std::strncpy(info->name, mapping->path.c_str(), 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 = mapping->start; info->seg0_size = mapping->end - mapping->start; return true; #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 sceSysmoduleGetModuleHandleInternal(std::uint32_t id, std::int32_t* handle) { if ((id & 0x7fffffffu) == 0) { throw std::runtime_error("sceSysmoduleGetModuleHandleInternal: invalid id 0"); } if (!handle) { throw std::runtime_error("sceSysmoduleGetModuleHandleInternal: null handle"); } const char* name = findModuleName(id); if (!name) { throw std::runtime_error(std::string("sceSysmoduleGetModuleHandleInternal: unknown id ") + std::to_string(id)); } std::lock_guard lock(gMutex); const auto loaded = gLoadCount.find(id); if (loaded == gLoadCount.end() || loaded->second < 1) { return SCE_SYSMODULE_ERROR_UNLOADED; } const auto existing = gHandles.find(id); if (existing != gHandles.end()) { *handle = existing->second; return 0; } void* module = openHostModule(name); if (!module) { throw std::runtime_error(std::string("sceSysmoduleGetModuleHandleInternal: failed to open ") + name); } const auto value = static_cast(reinterpret_cast(module)); gHandles.emplace(id, value); *handle = value; return 0; } int APS5_VABI sceSysmoduleIsLoaded(std::uint16_t id) { if (id == 0) { return SCE_SYSMODULE_ERROR_INVALID_VALUE; } if (!findModuleName(id)) { APS5_LOG_OUT("unknown id: %u", static_cast(id)); return 0; } std::lock_guard lock(gMutex); auto it = gLoadCount.find(id); if (it == gLoadCount.end() || it->second < 1) { return SCE_SYSMODULE_ERROR_UNLOADED; } return 0; } int APS5_VABI sceSysmoduleLoadModule(std::uint16_t id) { if (id == 0) { return SCE_SYSMODULE_ERROR_INVALID_VALUE; } if (!findModuleName(id)) { APS5_LOG_OUT("unknown id: %u", static_cast(id)); return 0; } std::lock_guard 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) { return SCE_SYSMODULE_ERROR_INVALID_VALUE; } if (!findModuleName(id)) { throw std::runtime_error(std::string("sceSysmoduleLoadModuleInternalWithArg: unknown id ") + std::to_string(id)); } std::lock_guard lock(gMutex); gLoadCount[id]++; if (ret) { *ret = 0; } return 0; } int APS5_VABI sceSysmoduleUnloadModule(std::uint16_t id) { if (id == 0) { return SCE_SYSMODULE_ERROR_INVALID_VALUE; } if (!findModuleName(id)) { APS5_LOG_OUT("unknown id: %u", static_cast(id)); return 0; } std::lock_guard lock(gMutex); auto it = gLoadCount.find(id); if (it == gLoadCount.end() || it->second < 1) { return SCE_SYSMODULE_ERROR_UNLOADED; } it->second--; return 0; } int APS5_VABI sceSysmoduleLoadModuleInternal(std::uint32_t id) { if ((id & 0x7fffffffu) == 0) { return SCE_SYSMODULE_ERROR_INVALID_VALUE; } if (!findModuleName(id)) { throw std::runtime_error(std::string("sceSysmoduleLoadModuleInternal: unknown id ") + std::to_string(id)); } std::lock_guard lock(gMutex); gLoadCount[id]++; return 0; } int APS5_VABI sceSysmoduleUnloadModuleInternal(std::uint32_t id) { if ((id & 0x7fffffffu) == 0) { return SCE_SYSMODULE_ERROR_INVALID_VALUE; } if (!findModuleName(id)) { throw std::runtime_error(std::string("sceSysmoduleUnloadModuleInternal: unknown id ") + std::to_string(id)); } std::lock_guard lock(gMutex); auto it = gLoadCount.find(id); if (it == gLoadCount.end() || it->second < 1) { return SCE_SYSMODULE_ERROR_UNLOADED; } it->second--; return 0; } }