Files
AnyPS5/core/libs/tests/ExportLookup.hpp
T
Yunus Shaikh 60baf80572 test(prx): look up each pending export by name in the built library
Release builds fold a declared function's address compared with nullptr
to false, so the tables' loops compiled away together with the tables'
references to the exports, and the checks passed without the library
exporting anything. Each test now gets its library's built file from its
add_test line, opens it, looks up every name in its table, and requires
one name the library doesn't export not to resolve. The table entries
are read through a volatile copy, so the test binaries keep importing
them.
2026-10-28 10:25:40 -07:00

47 lines
1.3 KiB
C++

#pragma once
#include <cstddef>
#include <cstdio>
#ifdef _WIN32
#include <windows.h>
#else
#include <dlfcn.h>
#endif
inline bool LibraryExportsName(void* library, const char* name) {
#ifdef _WIN32
return GetProcAddress(static_cast<HMODULE>(library), name) != nullptr;
#else
return dlsym(library, name) != nullptr;
#endif
}
template <typename Export, std::size_t Count>
bool ExportsResolveByName(const char* path, const Export (&exports)[Count], const char* absentName) {
if (path == nullptr) {
std::fprintf(stderr, "no library path was given\n");
return false;
}
#ifdef _WIN32
void* library = LoadLibraryA(path);
#else
void* library = dlopen(path, RTLD_NOW | RTLD_LOCAL);
#endif
if (library == nullptr) {
std::fprintf(stderr, "%s did not load\n", path);
return false;
}
for (const auto& entry : exports) {
auto* volatile function = entry.function;
if (function == nullptr || !LibraryExportsName(library, entry.name)) {
std::fprintf(stderr, "%s is not exported\n", entry.name);
return false;
}
}
if (LibraryExportsName(library, absentName)) {
std::fprintf(stderr, "%s resolved although nothing exports it\n", absentName);
return false;
}
return true;
}