diff --git a/src/libs/libSysmodule.cpp b/src/libs/libSysmodule.cpp index ac032dbf2..e6589b6d8 100644 --- a/src/libs/libSysmodule.cpp +++ b/src/libs/libSysmodule.cpp @@ -6,6 +6,9 @@ #include "libs/libs.h" #include "loader/symbolDatabase.h" +#include +#include + namespace Libs { LIB_VERSION("Sysmodule", 1, "Sysmodule", 1, 1); @@ -17,6 +20,16 @@ int KYTY_SYSV_ABI KernelGetModuleInfoForUnwind(uint64_t addr, int flags, ModuleI namespace Sysmodule { +constexpr int SYSMODULE_ERROR_UNLOADED = static_cast(0x805a1001u); + +static std::mutex g_module_mutex; +static std::unordered_map g_module_refs; + +static void LoadModule(uint16_t id) { + std::scoped_lock lock(g_module_mutex); + ++g_module_refs[id]; +} + static KYTY_SYSV_ABI int SysmoduleGetModuleInfoForUnwind(uint64_t addr, int flags, LibKernel::ModuleInfoForUnwind* info) { return LibKernel::KernelGetModuleInfoForUnwind(addr, flags, info); @@ -27,6 +40,7 @@ static KYTY_SYSV_ABI int SysmoduleLoadModule(uint16_t id) { LOGF("\t id = %d\n", static_cast(id)); + LoadModule(id); return 0; } @@ -35,6 +49,14 @@ static KYTY_SYSV_ABI int SysmoduleUnloadModule(uint16_t id) { LOGF("\t id = %d\n", static_cast(id)); + std::scoped_lock lock(g_module_mutex); + const auto module = g_module_refs.find(id); + if (module == g_module_refs.end()) { + return SYSMODULE_ERROR_UNLOADED; + } + if (--module->second == 0) { + g_module_refs.erase(module); + } return 0; } @@ -49,6 +71,7 @@ static KYTY_SYSV_ABI int SysmoduleLoadModuleInternalWithArg(uint16_t id, int arg EXIT_IF(arg3 != 0); EXIT_IF(ret == nullptr); + LoadModule(id); *ret = 0; return 0; @@ -59,7 +82,8 @@ static KYTY_SYSV_ABI int SysmoduleIsLoaded(uint16_t id) { LOGF("\t id = %d\n", static_cast(id)); - return 0; + std::scoped_lock lock(g_module_mutex); + return g_module_refs.contains(id) ? 0 : SYSMODULE_ERROR_UNLOADED; } } // namespace Sysmodule diff --git a/tests/KernelFileSystemTests.cpp b/tests/KernelFileSystemTests.cpp index b0aeb86f9..f0f7cb976 100644 --- a/tests/KernelFileSystemTests.cpp +++ b/tests/KernelFileSystemTests.cpp @@ -45,6 +45,7 @@ void InitLibKernel_1_Apr(Loader::SymbolDatabase *symbols); namespace Libs { void InitLibKernel_1(Loader::SymbolDatabase *symbols); +void InitSysmodule_1(Loader::SymbolDatabase *symbols); } namespace Libs::LibAmpr { @@ -164,6 +165,45 @@ void TestFileDescriptorFlags() { } } +void TestSysmoduleReferences() { + Loader::SymbolDatabase symbols; + Libs::InitSysmodule_1(&symbols); + using ModuleCall = int (KYTY_SYSV_ABI *)(uint16_t); + const auto resolve = [&](const char* nid) { + const auto* symbol = symbols.Find( + {nid, "Sysmodule", 1, "Sysmodule", 1, 1, Loader::SymbolType::Func}); + Check(symbol != nullptr, "Sysmodule export resolves"); + return reinterpret_cast(symbol->vaddr); + }; + const auto load = resolve("g8cM39EUZ6o"); + const auto unload = resolve("eR2bZFAAU0Q"); + const auto is_loaded = resolve("fMP5NHUOaMk"); + constexpr int unloaded = static_cast(0x805a1001u); + Check(load(0x113) == OK && is_loaded(0xb4) == unloaded, + "loading entitlement access does not mark AppContent loaded"); + Check(unload(0x113) == OK, "release independent module reference"); + for (const uint16_t id : {0x113, 0xb4}) { + Check(is_loaded(id) == unloaded, "unloaded module allows guest initialization"); + Check(unload(id) == unloaded, "unloading an absent module reports UNLOADED"); + Check(load(id) == OK && load(id) == OK && is_loaded(id) == OK, + "repeated loads retain references"); + Check(unload(id) == OK && is_loaded(id) == OK, + "one unload preserves the remaining reference"); + Check(unload(id) == OK && is_loaded(id) == unloaded, + "last unload restores unloaded status"); + } + const auto* internal_symbol = symbols.Find( + {"hHrGoGoNf+s", "Sysmodule", 1, "Sysmodule", 1, 1, Loader::SymbolType::Func}); + Check(internal_symbol != nullptr, "internal Sysmodule load export resolves"); + const auto internal_load = + reinterpret_cast( + internal_symbol->vaddr); + int result = -1; + Check(internal_load(0xb4, 0, 0, 0, &result) == OK && result == OK && + is_loaded(0xb4) == OK && unload(0xb4) == OK && is_loaded(0xb4) == unloaded, + "internal and public module operations share load state"); +} + void TestSaveOpenVisibility() { constexpr char Path[] = "/savedata0/visible-save.dat"; constexpr char Payload[] = "saved progress"; @@ -1532,6 +1572,7 @@ int main(int, char**) { TempDirectory temporary; FileSystem::Initialize(); + TestSysmoduleReferences(); TestRandomDevices(); TestFileDescriptorFlags(); CheckMountRoot(temporary.Path());