From 869d73e4d82f4711cf973dbc4e3fc0d1bbc5dedd Mon Sep 17 00:00:00 2001 From: Gustavo Ariel Gamarra Rojas <111788638+GustavoGamarra95@users.noreply.github.com> Date: Sat, 10 Oct 2026 21:23:19 -0300 Subject: [PATCH] test(libSceSysmodule): compare sceSysmoduleGetModuleInfoForUnwind with /proc/self/maps Covers code, stack and an anonymous page, and an unmapped address. On Linux the test runs again with APS5_NO_PROCMAP_QUERY so the line scan stays covered. --- core/libs/prx/libSceSysmodule/CMakeLists.txt | 4 ++ core/libs/tests/GuestSysmodule.cpp | 67 ++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/core/libs/prx/libSceSysmodule/CMakeLists.txt b/core/libs/prx/libSceSysmodule/CMakeLists.txt index b9111e032..4d5f4a73e 100644 --- a/core/libs/prx/libSceSysmodule/CMakeLists.txt +++ b/core/libs/prx/libSceSysmodule/CMakeLists.txt @@ -7,6 +7,10 @@ if(BUILD_TESTING) target_include_directories(guest_sysmodule_tests PRIVATE ${LIBS_INCLUDE_DIR}) target_link_libraries(guest_sysmodule_tests PRIVATE libSceSysmodule) add_test(NAME guest_sysmodule COMMAND guest_sysmodule_tests) + if(UNIX AND NOT APPLE) + add_test(NAME guest_sysmodule_maps_scan COMMAND guest_sysmodule_tests) + set_tests_properties(guest_sysmodule_maps_scan PROPERTIES ENVIRONMENT "APS5_NO_PROCMAP_QUERY=1") + endif() if(WIN32) set_tests_properties(guest_sysmodule PROPERTIES ENVIRONMENT_MODIFICATION "PATH=path_list_prepend:$") diff --git a/core/libs/tests/GuestSysmodule.cpp b/core/libs/tests/GuestSysmodule.cpp index f3ffdecb0..f4f999aa3 100644 --- a/core/libs/tests/GuestSysmodule.cpp +++ b/core/libs/tests/GuestSysmodule.cpp @@ -1,6 +1,15 @@ +#include "SceTypes.hpp" #include "prx/libc/include/general/VabiMacros.hpp" #include #include +#ifdef __linux__ +#include +#include +#include +#include +#include +#include +#endif extern "C" { int APS5_VABI sceSysmoduleIsLoaded(std::uint16_t id); @@ -8,6 +17,7 @@ int APS5_VABI sceSysmoduleLoadModule(std::uint16_t id); int APS5_VABI sceSysmoduleUnloadModule(std::uint16_t id); int APS5_VABI sceSysmoduleLoadModuleInternal(std::uint32_t id); int APS5_VABI sceSysmoduleUnloadModuleInternal(std::uint32_t id); +int APS5_VABI sceSysmoduleGetModuleInfoForUnwind(std::uint64_t addr, int flags, ModuleInfoForUnwind* info); } namespace { @@ -18,6 +28,60 @@ constexpr std::uint16_t kFiberModuleId = 0x0006; constexpr std::uint16_t kUltModuleId = 0x0007; constexpr int kModuleUnloaded = static_cast(0x805A1001); +#ifdef __linux__ +struct Mapping { + std::uint64_t start = 0; + std::uint64_t end = 0; + std::string path; +}; + +Mapping MapsEntry(std::uint64_t address) { + std::ifstream maps("/proc/self/maps"); + std::string line; + while (std::getline(maps, line)) { + unsigned long long start = 0; + unsigned long long end = 0; + char path[4096] = {}; + const int parsed = std::sscanf(line.c_str(), "%llx-%llx %*s %*x %*x:%*x %*u %4095s", &start, &end, path); + if (parsed >= 2 && address >= start && address < end) return Mapping{start, end, parsed == 3 ? path : ""}; + } + std::abort(); +} + +void RequireUnwindMatchesMaps(const void* pointer) { + const auto address = reinterpret_cast(pointer); + const auto expected = MapsEntry(address); + ModuleInfoForUnwind info{}; + Require(sceSysmoduleGetModuleInfoForUnwind(address, 0, &info) == 0); + Require(info.st_size == sizeof(ModuleInfoForUnwind)); + Require(info.seg0_addr == expected.start && info.seg0_size == expected.end - expected.start); + Require(std::string(info.name) == expected.path.substr(0, sizeof(info.name) - 1)); + Require(info.eh_frame_hdr_addr == 0 && info.eh_frame_addr == 0 && info.eh_frame_size == 0); +} + +void CheckUnwindInfo() { + RequireUnwindMatchesMaps(reinterpret_cast(&RequireUnwindMatchesMaps)); + RequireUnwindMatchesMaps(reinterpret_cast(&sceSysmoduleGetModuleInfoForUnwind)); + int local = 0; + RequireUnwindMatchesMaps(&local); + const auto page = static_cast(sysconf(_SC_PAGESIZE)); + void* anonymous = mmap(nullptr, page, PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + Require(anonymous != MAP_FAILED); + RequireUnwindMatchesMaps(anonymous); + Require(munmap(anonymous, page) == 0); + void* gap = mmap(nullptr, page, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + Require(gap != MAP_FAILED && munmap(gap, page) == 0); + ModuleInfoForUnwind info{}; + bool threw = false; + try { + sceSysmoduleGetModuleInfoForUnwind(reinterpret_cast(gap), 0, &info); + } catch (const std::runtime_error&) { + threw = true; + } + Require(threw); +} +#endif + } int main() { @@ -37,4 +101,7 @@ int main() { Require(sceSysmoduleIsLoaded(kUltModuleId) == 0); Require(sceSysmoduleUnloadModule(kUltModuleId) == 0); Require(sceSysmoduleIsLoaded(kUltModuleId) == kModuleUnloaded); +#ifdef __linux__ + CheckUnwindInfo(); +#endif }