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.
This commit is contained in:
Phi1ow 2026-10-04 21:56:29 +02:00
parent d7f0c49fdc
commit 75c2ea8548
3 changed files with 73 additions and 0 deletions

View File

@ -0,0 +1,20 @@
add_library(libSceSysmodule SHARED EXCLUDE_FROM_ALL Export.cpp)
target_include_directories(libSceSysmodule PRIVATE ${LIBS_INCLUDE_DIR})
configure_windows_unwind(libSceSysmodule)
if(MINGW)
foreach(runtimeLinkFlag IN ITEMS -static-libgcc -static-libstdc++ -static)
string(REPLACE "${runtimeLinkFlag}" "" CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}")
endforeach()
endif()
if(BUILD_TESTING)
add_test_executable(guest_sysmodule_tests ${LIBS_INCLUDE_DIR}/tests/GuestSysmodule.cpp)
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(WIN32)
set_tests_properties(guest_sysmodule PROPERTIES ENVIRONMENT_MODIFICATION
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceSysmodule>")
endif()
endif()

View File

@ -166,4 +166,32 @@ int APS5_VABI sceSysmoduleUnloadModule(std::uint16_t id) {
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;
}
}

View File

@ -0,0 +1,25 @@
#include "prx/libc/include/general/VabiMacros.hpp"
#include <cstdint>
#include <cstdlib>
extern "C" {
int APS5_VABI sceSysmoduleLoadModuleInternal(std::uint32_t id);
int APS5_VABI sceSysmoduleUnloadModuleInternal(std::uint32_t id);
}
namespace {
void Require(bool value) { if (!value) std::abort(); }
constexpr std::uint32_t kFiberModuleId = 0x00000006;
constexpr int kModuleNotLoaded = static_cast<int>(0x80A90003);
}
int main() {
Require(sceSysmoduleLoadModuleInternal(kFiberModuleId) == 0);
Require(sceSysmoduleLoadModuleInternal(kFiberModuleId) == 0);
Require(sceSysmoduleUnloadModuleInternal(kFiberModuleId) == 0);
Require(sceSysmoduleUnloadModuleInternal(kFiberModuleId) == 0);
Require(sceSysmoduleUnloadModuleInternal(kFiberModuleId) == kModuleNotLoaded);
}