Files
AnyPS5/core/libs/tests/GuestTlsGetAddr.cpp
T
memob cf27259538 feat(libkernel): implement __tls_get_addr
Resolve a FreeBSD/ELF tls_index {module, offset} the way the relinker's
DTPMOD64 values are laid out: on Windows through the PE _tls_index the
module word points at and the thread's ThreadLocalStoragePointer, on
Linux through glibc's __tls_get_addr.
2026-10-24 20:29:20 -04:00

97 lines
2.1 KiB
C++

#include "SceTypes.hpp"
#include "prx/libc/include/general/VabiMacros.hpp"
#include <cstdint>
#include <cstdlib>
#include <stdexcept>
#include <thread>
#if defined(_WIN32)
#include <windows.h>
#else
#include <link.h>
#endif
namespace {
struct TlsIndex {
std::uint64_t module;
std::uint64_t offset;
};
}
extern "C" void* APS5_VABI __tls_get_addr_nid_postfix(const TlsIndex* index);
#if defined(_WIN32)
extern "C" ULONG _tls_index;
namespace {
using Cell = char;
constexpr Cell Initial = 0;
TlsIndex CellIndex() { return {reinterpret_cast<std::uintptr_t>(&_tls_index), 0}; }
Cell* ThreadCell() {
auto* blocks = *reinterpret_cast<char***>(reinterpret_cast<char*>(NtCurrentTeb()) + 0x58);
return blocks[_tls_index];
}
}
#else
namespace {
using Cell = std::uint32_t;
constexpr Cell Initial = 0x5ca1ab1e;
thread_local Cell Value = Initial;
struct Module {
std::size_t id = 0;
void* data = nullptr;
};
Module MainModule() {
Module module;
dl_iterate_phdr([](dl_phdr_info* info, std::size_t, void* out) {
auto& found = *static_cast<Module*>(out);
found = {info->dlpi_tls_modid, info->dlpi_tls_data};
return 1;
}, &module);
return module;
}
TlsIndex CellIndex() {
const auto module = MainModule();
return {module.id, static_cast<std::uint64_t>(reinterpret_cast<char*>(&Value) - static_cast<char*>(module.data))};
}
Cell* ThreadCell() { return &Value; }
}
#endif
namespace {
void Require(bool value) { if (!value) std::abort(); }
}
int main() {
const auto index = CellIndex();
auto* own = static_cast<Cell*>(__tls_get_addr_nid_postfix(&index));
Require(own == ThreadCell() && *own == Initial);
*own = 7;
Cell* other = nullptr;
Cell seen = 1;
std::thread([&] {
other = static_cast<Cell*>(__tls_get_addr_nid_postfix(&index));
Require(other == ThreadCell());
seen = *other;
*other = 9;
}).join();
Require(other != own && seen == Initial && *own == 7);
*own = Initial;
bool threw = false;
try { __tls_get_addr_nid_postfix(nullptr); } catch (const std::invalid_argument&) { threw = true; }
Require(threw);
}