Merge pull request #864 from jeffrey12cali/fix/dlsym-default-scope

fix(module): resolve the default dlsym scope against the main program
This commit is contained in:
Adrià 2026-10-08 14:52:14 +02:00 committed by GitHub
commit b82c1c2f70
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 68 additions and 1 deletions

View File

@ -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 $<TARGET_FILE_NAME:guest_module_fixture>)
set_tests_properties(guest_dynamic_loader PROPERTIES
WORKING_DIRECTORY "$<TARGET_FILE_DIR:guest_module_fixture>")

View File

@ -15,6 +15,7 @@
#include <windows.h>
#else
#include <dlfcn.h>
#include <link.h>
#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<Scan*>(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<std::uintptr_t>(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<std::shared_ptr<Module>> search;
{
std::lock_guard lock(modulesMutex);
if (handle == reinterpret_cast<void*>(static_cast<std::intptr_t>(-2))) {
if (handle == nullptr || handle == reinterpret_cast<void*>(static_cast<std::intptr_t>(-2))) {
for (const auto& [key, module] : modules) if (module->global) search.push_back(module);
} else {
auto found = modules.find(reinterpret_cast<std::uintptr_t>(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<void*>(static_cast<std::intptr_t>(-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; }

View File

@ -2,6 +2,9 @@
#include <cstdlib>
#include <filesystem>
#include <fstream>
#ifndef _WIN32
#include <dlfcn.h>
#endif
#include <stdexcept>
#include <thread>
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<typename TFunction>
static bool ThrowsRuntimeError(TFunction function) {
try {
@ -38,6 +44,7 @@ int main(int argc, char** argv) {
auto add = reinterpret_cast<Add>(dlsym_nid_postfix(module, "GuestModuleAdd"));
Require(add && add(17, 25) == 42);
Require(dlsym_nid_postfix(reinterpret_cast<void*>(-2), "GuestModuleAdd") == reinterpret_cast<void*>(add));
Require(dlsym_nid_postfix(nullptr, "GuestModuleAdd") == reinterpret_cast<void*>(add));
#ifndef _WIN32
auto mul = reinterpret_cast<Add>(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<const void*>(&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<void*>(&GuestDefaultScopeOnly));
Require(dlsym_nid_postfix(reinterpret_cast<void*>(-2), "GuestDefaultScopeOnly") == reinterpret_cast<void*>(&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
}