diff --git a/core/libs/prx/libkernel/CMakeLists.txt b/core/libs/prx/libkernel/CMakeLists.txt index 6055d0de5..2520ab477 100644 --- a/core/libs/prx/libkernel/CMakeLists.txt +++ b/core/libs/prx/libkernel/CMakeLists.txt @@ -104,6 +104,9 @@ if(BUILD_TESTING) target_link_libraries(guest_dynamic_loader_tests PRIVATE libkernel libc) configure_windows_unwind(guest_dynamic_loader_tests) add_dependencies(guest_dynamic_loader_tests guest_module_fixture) + if(NOT WIN32) + set_target_properties(guest_dynamic_loader_tests PROPERTIES ENABLE_EXPORTS ON) + endif() add_test(NAME guest_dynamic_loader COMMAND guest_dynamic_loader_tests $) set_tests_properties(guest_dynamic_loader PROPERTIES WORKING_DIRECTORY "$") diff --git a/core/libs/prx/libkernel/Module/src/DynamicLoader.cpp b/core/libs/prx/libkernel/Module/src/DynamicLoader.cpp index d2604c94d..509194a50 100644 --- a/core/libs/prx/libkernel/Module/src/DynamicLoader.cpp +++ b/core/libs/prx/libkernel/Module/src/DynamicLoader.cpp @@ -15,6 +15,7 @@ #include #else #include +#include #endif namespace { @@ -49,6 +50,39 @@ void* Symbol(Module& module, const char* name) { return ::dlsym(module.native, name); #endif } +#ifndef _WIN32 +std::uintptr_t MainProgramBase() { + static const std::uintptr_t base = [] { + struct Scan { + std::uintptr_t value = 0; + }; + Scan scan; + ::dl_iterate_phdr( + [](dl_phdr_info* info, size_t, void* data) { + auto* found = static_cast(data); + if (found->value == 0 && info->dlpi_name != nullptr && info->dlpi_name[0] == '\0') found->value = info->dlpi_addr; + return found->value == 0 ? 0 : 1; + }, + &scan); + return scan.value; + }(); + return base; +} + +bool FromGuestModule(void* address) { + Dl_info info{}; + if (address == nullptr || !::dladdr(address, &info) || info.dli_fname == nullptr) return false; + if (reinterpret_cast(info.dli_fbase) == MainProgramBase()) return true; + const std::string path(info.dli_fname); + return path.ends_with(".prx"); +} + +void* DefaultScopeSymbol(const char* symbolName) { + auto* result = ::dlsym(RTLD_DEFAULT, symbolName); + return FromGuestModule(result) ? result : nullptr; +} +#endif + void* FindSymbol(Module& module, const char* name) { if (auto* symbol = Symbol(module, name)) return symbol; const auto nid = Nid::ComputeNid(name, ""); @@ -116,7 +150,7 @@ void* APS5_VABI dlsym_nid_postfix(void* handle, const char* name) { std::vector> search; { std::lock_guard lock(modulesMutex); - if (handle == reinterpret_cast(static_cast(-2))) { + if (handle == nullptr || handle == reinterpret_cast(static_cast(-2))) { for (const auto& [key, module] : modules) if (module->global) search.push_back(module); } else { auto found = modules.find(reinterpret_cast(handle)); @@ -125,6 +159,13 @@ void* APS5_VABI dlsym_nid_postfix(void* handle, const char* name) { } } for (const auto& module : search) if (auto* result = FindSymbol(*module, name)) return result; +#ifndef _WIN32 + if (handle == nullptr || handle == reinterpret_cast(static_cast(-2))) { + if (auto* result = DefaultScopeSymbol(name)) return result; + const auto nid = Nid::ComputeNid(name, ""); + if (auto* result = DefaultScopeSymbol(nid.c_str())) return result; + } +#endif Error("dlsym: symbol not found in supported module scope"); return nullptr; } catch (const std::exception& error) { Error(error.what()); return nullptr; } diff --git a/core/libs/tests/GuestDynamicLoader.cpp b/core/libs/tests/GuestDynamicLoader.cpp index 1b3172e67..74223730f 100644 --- a/core/libs/tests/GuestDynamicLoader.cpp +++ b/core/libs/tests/GuestDynamicLoader.cpp @@ -2,6 +2,9 @@ #include #include #include +#ifndef _WIN32 +#include +#endif #include #include extern "C" { @@ -13,6 +16,9 @@ int APS5_VABI _sceKernelRtldThreadAtexitIncrement_nid_postfix(const void*); int APS5_VABI _sceKernelRtldThreadAtexitDecrement_nid_postfix(const void*); } static void Require(bool value) { if (!value) std::abort(); } +#ifndef _WIN32 +extern "C" int APS5_VABI GuestDefaultScopeOnly(int a, int b) { return a * b; } +#endif template static bool ThrowsRuntimeError(TFunction function) { try { @@ -38,6 +44,7 @@ int main(int argc, char** argv) { auto add = reinterpret_cast(dlsym_nid_postfix(module, "GuestModuleAdd")); Require(add && add(17, 25) == 42); Require(dlsym_nid_postfix(reinterpret_cast(-2), "GuestModuleAdd") == reinterpret_cast(add)); + Require(dlsym_nid_postfix(nullptr, "GuestModuleAdd") == reinterpret_cast(add)); #ifndef _WIN32 auto mul = reinterpret_cast(dlsym_nid_postfix(module, "GuestModuleMul")); Require(mul && mul(6, 7) == 42); @@ -88,4 +95,20 @@ int main(int argc, char** argv) { Require(ThrowsRuntimeError([] { _sceKernelRtldThreadAtexitDecrement_nid_postfix(reinterpret_cast(&Require)); })); int local = 0; Require(ThrowsRuntimeError([&] { _sceKernelRtldThreadAtexitIncrement_nid_postfix(&local); })); +#ifndef _WIN32 + Require(dlsym_nid_postfix(nullptr, "malloc") == nullptr); + Require(dlsym_nid_postfix(nullptr, "dlopen") == nullptr); + Require(dlsym_nid_postfix(nullptr, "pthread_create") == nullptr); + Require(dlsym_nid_postfix(nullptr, "GuestDefaultScopeOnly") == reinterpret_cast(&GuestDefaultScopeOnly)); + Require(dlsym_nid_postfix(reinterpret_cast(-2), "GuestDefaultScopeOnly") == reinterpret_cast(&GuestDefaultScopeOnly)); + const auto hostLoaded = std::filesystem::absolute("anyps5-host-loaded-module-for-test.prx"); + std::filesystem::copy_file(argv[1], hostLoaded, std::filesystem::copy_options::overwrite_existing); + void* host = ::dlopen(hostLoaded.c_str(), RTLD_NOW | RTLD_GLOBAL); + Require(host != nullptr); + void* hostAdd = ::dlsym(host, "3qcG+yBRS+s"); + Require(hostAdd != nullptr); + Require(dlsym_nid_postfix(nullptr, "GuestModuleAdd") == hostAdd); + Require(dlsym_nid_postfix(nullptr, "malloc") == nullptr); + Require(::dlclose(host) == 0); +#endif }