From 594656581e1092743eb7b4997af7196642d99d5b Mon Sep 17 00:00:00 2001 From: boykopovar Date: Thu, 24 Sep 2026 10:28:33 +0300 Subject: [PATCH] feat(libs): add new std symbols + unused filter off by default --- README.md | 2 +- core/libs/prx/libc/src/exception/Exports.cpp | 1 + .../libc/src/exception/StandardExceptions.cpp | 6 ++ core/libs/prx/libc/src/exception/TypeInfo.cpp | 68 +++++++++++++++++++ core/libs/tests/WindowsExceptions.cpp | 46 +++++++++++++ core/relinker/cli/include/Cli.hpp | 2 +- .../src/windows/WindowsPePatcher.cpp | 18 +++++ 7 files changed, 141 insertions(+), 2 deletions(-) create mode 100644 core/libs/prx/libc/src/exception/TypeInfo.cpp diff --git a/README.md b/README.md index 75871a2e9..0af89dc85 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ The [shader recompiler](core/shader/recompiler/Recompiler.cpp) successfully prod The real game reaches the render loop and **successfully presents its first frames** (logo). -Current error: [relinker](core/relinker/relinker/src/analysis/UnusedNidFilter.cpp) with `unused-filter=1` removes the used [_ZSt21_sceLibcClassicLocale](core/libs/prx/libc/src/LocaleSupport.cpp) import, causing a null-pointer access. `unused_filter=2` preserves imports for functions that are not yet implemented in the [prx](core/libs/prx). +Current error: Access Violation after `Could not open file /_sd/SAVEDATA00/-saveindex` at ~frame 71, presumably caused by a null pointer dereference. ## Build diff --git a/core/libs/prx/libc/src/exception/Exports.cpp b/core/libs/prx/libc/src/exception/Exports.cpp index bc22127bd..2c7f8be9d 100644 --- a/core/libs/prx/libc/src/exception/Exports.cpp +++ b/core/libs/prx/libc/src/exception/Exports.cpp @@ -1,5 +1,6 @@ #include "Unwind.cpp" #include "Runtime.cpp" +#include "TypeInfo.cpp" #include "ExceptionPointer.cpp" #include "StandardExceptions.cpp" #include "ArraySupport.cpp" diff --git a/core/libs/prx/libc/src/exception/StandardExceptions.cpp b/core/libs/prx/libc/src/exception/StandardExceptions.cpp index 3e0fcf7f0..cb6be8a40 100644 --- a/core/libs/prx/libc/src/exception/StandardExceptions.cpp +++ b/core/libs/prx/libc/src/exception/StandardExceptions.cpp @@ -1,6 +1,7 @@ #include "prx/libc/include/exceptions/Runtime.hpp" #include #include +#include namespace LibcException { struct TypeRecord { const void* const* vtable; const char* name; const TypeRecord* base; }; @@ -110,12 +111,17 @@ ExceptionObject* Assign(ExceptionObject* object, const ExceptionObject* source) } extern "C" { +LibcException::TypeRecord _ZTISt8ios_base_nid_postfix {LibcException::ClassTypeVtable + 2, "St8ios_base", nullptr}; LibcException::TypeRecord _ZTISt9exception_nid_postfix {LibcException::ClassTypeVtable + 2, "St9exception", nullptr}; LibcException::ExceptionVtable _ZTVSt9exception_nid_postfix {0, &_ZTISt9exception_nid_postfix, LibcException::DestroyPlain, LibcException::DeletePlain, LibcException::PlainWhat}; void APS5_VABI _ZNSt9exceptionD1Ev_nid_postfix(LibcException::ExceptionObject* self) { LibcException::DestroyPlain(self); } void APS5_VABI _ZNSt9exceptionD2Ev_nid_postfix(LibcException::ExceptionObject* self) { LibcException::DestroyPlain(self); } void APS5_VABI _ZNSt9exceptionD0Ev_nid_postfix(LibcException::ExceptionObject* self) { LibcException::DeletePlain(self); } const char* APS5_VABI _ZNKSt9exception4whatEv_nid_postfix(const LibcException::ExceptionObject* self) { return LibcException::PlainWhat(self); } +[[noreturn]] void APS5_VABI _ZNKSt9exception8_DoraiseEv_nid_postfix(const LibcException::ExceptionObject* self) { + if (!self) throw std::invalid_argument("exception object is null"); + LibcException::ThrowPlain(_ZTVSt9exception_nid_postfix); +} void APS5_VABI _ZNSt9exceptionC1Ev_nid_postfix(LibcException::ExceptionObject* self) { self->vtable = &_ZTVSt9exception_nid_postfix.destroy; } void APS5_VABI _ZNSt9exceptionC2Ev_nid_postfix(LibcException::ExceptionObject* self) { self->vtable = &_ZTVSt9exception_nid_postfix.destroy; } diff --git a/core/libs/prx/libc/src/exception/TypeInfo.cpp b/core/libs/prx/libc/src/exception/TypeInfo.cpp new file mode 100644 index 000000000..c745eda72 --- /dev/null +++ b/core/libs/prx/libc/src/exception/TypeInfo.cpp @@ -0,0 +1,68 @@ +#include "prx/libc/include/exceptions/Runtime.hpp" +#include + +namespace LibcException { +namespace { + +using ClassTypeInfo = __cxxabiv1::__class_type_info; + +void APS5_VABI DestroyTypeInfo(std::type_info*) {} + +void APS5_VABI DeleteTypeInfo(std::type_info*) { + throw std::runtime_error("deleting guest RTTI is not supported"); +} + +bool APS5_VABI IsPointerTypeInfo(const std::type_info*) { return false; } + +bool APS5_VABI CatchTypeInfo(const std::type_info* self, const std::type_info* thrown, void** object, unsigned) { + if (!self || !thrown || !object) throw std::invalid_argument("invalid RTTI catch arguments"); + return Match(self, thrown, *object); +} + +bool APS5_VABI UpcastTypeInfo(const std::type_info* self, const ClassTypeInfo* base, void** object) { + if (!self || !base || !object) throw std::invalid_argument("invalid RTTI upcast arguments"); + Search search {base}; + Bases(self, *object, search); + if (search.count != 1) return false; + *object = search.found; + return true; +} + +bool APS5_VABI UpcastTypeInfoResult(const ClassTypeInfo*, const ClassTypeInfo*, const void*, ClassTypeInfo::__upcast_result&) { + throw std::runtime_error("RTTI __upcast_result is not supported"); +} + +bool APS5_VABI DynamicCastTypeInfo(const ClassTypeInfo*, std::ptrdiff_t, ClassTypeInfo::__sub_kind, const ClassTypeInfo*, const void*, const ClassTypeInfo*, const void*, ClassTypeInfo::__dyncast_result&) { + throw std::runtime_error("RTTI __dyncast_result is not supported"); +} + +ClassTypeInfo::__sub_kind APS5_VABI FindPublicTypeInfo(const ClassTypeInfo*, std::ptrdiff_t, const void*, const ClassTypeInfo*, const void*) { + throw std::runtime_error("RTTI __do_find_public_src is not supported"); +} + +} + +struct TypeInfoVtable { + std::ptrdiff_t offset; + const std::type_info* type; + decltype(&DestroyTypeInfo) destroy = DestroyTypeInfo; + decltype(&DeleteTypeInfo) deleteObject = DeleteTypeInfo; + decltype(&IsPointerTypeInfo) isPointer = IsPointerTypeInfo; + decltype(&IsPointerTypeInfo) isFunction = IsPointerTypeInfo; + decltype(&CatchTypeInfo) catchType = CatchTypeInfo; + decltype(&UpcastTypeInfo) upcast = UpcastTypeInfo; + decltype(&UpcastTypeInfoResult) upcastResult = UpcastTypeInfoResult; + decltype(&DynamicCastTypeInfo) dynamicCast = DynamicCastTypeInfo; + decltype(&FindPublicTypeInfo) findPublic = FindPublicTypeInfo; +}; + +static_assert(offsetof(TypeInfoVtable, destroy) == 2 * sizeof(void*)); +static_assert(sizeof(TypeInfoVtable) == 11 * sizeof(void*)); + +} + +extern "C" { +LibcException::TypeInfoVtable _ZTVN10__cxxabiv117__class_type_infoE_nid_postfix {0, &typeid(__cxxabiv1::__class_type_info)}; +LibcException::TypeInfoVtable _ZTVN10__cxxabiv120__si_class_type_infoE_nid_postfix {0, &typeid(__cxxabiv1::__si_class_type_info)}; +LibcException::TypeInfoVtable _ZTVN10__cxxabiv121__vmi_class_type_infoE_nid_postfix {0, &typeid(__cxxabiv1::__vmi_class_type_info)}; +} diff --git a/core/libs/tests/WindowsExceptions.cpp b/core/libs/tests/WindowsExceptions.cpp index ec1b7c6cd..2e13adb43 100644 --- a/core/libs/tests/WindowsExceptions.cpp +++ b/core/libs/tests/WindowsExceptions.cpp @@ -5,6 +5,51 @@ #include #include #include +#include +#include +#include "../prx/libc/include/general/VabiMacros.hpp" + +extern "C" { +extern const unsigned char _ZTVN10__cxxabiv117__class_type_infoE_nid_postfix[]; +extern const unsigned char _ZTVN10__cxxabiv120__si_class_type_infoE_nid_postfix[]; +extern const unsigned char _ZTVN10__cxxabiv121__vmi_class_type_infoE_nid_postfix[]; +} + +static void TestTypeInfoVtables() { + struct TypeRecord { const void* vtable; const char* name; }; + struct SingleRecord { TypeRecord type; const TypeRecord* base; }; + struct BaseRecord { const TypeRecord* type; std::ptrdiff_t flags; }; + struct MultipleRecord { TypeRecord type; unsigned flags; unsigned count; BaseRecord bases[2]; }; + const auto* classTable = _ZTVN10__cxxabiv117__class_type_infoE_nid_postfix; + const auto* singleTable = _ZTVN10__cxxabiv120__si_class_type_infoE_nid_postfix; + const auto* multipleTable = _ZTVN10__cxxabiv121__vmi_class_type_infoE_nid_postfix; + const unsigned char* tables[] {classTable, singleTable, multipleTable}; + const char* names[] {"N10__cxxabiv117__class_type_infoE", "N10__cxxabiv120__si_class_type_infoE", "N10__cxxabiv121__vmi_class_type_infoE"}; + for (std::size_t i = 0; i < 3; ++i) { + const std::type_info* category; + std::memcpy(&category, tables[i] + sizeof(void*), sizeof(category)); + if (std::strcmp(category->name(), names[i])) throw std::runtime_error("incorrect RTTI category"); + } + TypeRecord base {classTable + 2 * sizeof(void*), "4Base"}; + TypeRecord other {classTable + 2 * sizeof(void*), "5Other"}; + SingleRecord single {{singleTable + 2 * sizeof(void*), "6Single"}, &base}; + MultipleRecord multiple {{multipleTable + 2 * sizeof(void*), "8Multiple"}, 0, 2, {{&other, 2}, {&base, (16 << 8) | 2}}}; + using CatchType = bool (APS5_VABI *)(const void*, const void*, void**, unsigned); + using UpcastType = bool (APS5_VABI *)(const void*, const void*, void**); + CatchType catchType; + UpcastType upcastType; + std::memcpy(&catchType, classTable + 6 * sizeof(void*), sizeof(catchType)); + std::memcpy(&upcastType, multipleTable + 7 * sizeof(void*), sizeof(upcastType)); + std::array storage {}; + void* object = storage.data(); + if (!catchType(&base, &single, &object, 0) || object != storage.data()) throw std::runtime_error("single RTTI catch failed"); + object = storage.data(); + if (!catchType(&base, &multiple, &object, 0) || object != storage.data() + 16) throw std::runtime_error("multiple RTTI catch failed"); + object = storage.data(); + if (!upcastType(&multiple, &base, &object) || object != storage.data() + 16) throw std::runtime_error("multiple RTTI upcast failed"); + object = storage.data(); + if (catchType(&other, &single, &object, 0)) throw std::runtime_error("unrelated RTTI catch succeeded"); +} extern "C" void NotImplemented_nid_no_patch(const char*); @@ -61,6 +106,7 @@ static void testExceptionPointer() { } int main() { + TestTypeInfoVtables(); testExceptionPointer(); try { Rethrow(); diff --git a/core/relinker/cli/include/Cli.hpp b/core/relinker/cli/include/Cli.hpp index 1e3cd0166..cf9e1b30a 100644 --- a/core/relinker/cli/include/Cli.hpp +++ b/core/relinker/cli/include/Cli.hpp @@ -12,7 +12,7 @@ struct Args { bool lazyBinding = false; bool autorun = false; bool windowsDiagnostics = false; - std::uint32_t unusedFilterLevel = 1; + std::uint32_t unusedFilterLevel = 0; std::string inputPath; std::string outputPath; std::string runPath = "$ORIGIN/libs"; diff --git a/core/relinker/elfpatcher/src/windows/WindowsPePatcher.cpp b/core/relinker/elfpatcher/src/windows/WindowsPePatcher.cpp index 460a1d390..0910e7aad 100644 --- a/core/relinker/elfpatcher/src/windows/WindowsPePatcher.cpp +++ b/core/relinker/elfpatcher/src/windows/WindowsPePatcher.cpp @@ -5,12 +5,28 @@ #include #include #include +#include +#include +#include #include namespace Elfpatcher::Windows { namespace { +void writeDiagnosticsImports(const std::vector& imports) { + const std::filesystem::path path = std::filesystem::absolute("windows-diagnostics-imports.txt"); + std::ofstream stream(path, std::ios::trunc); + if (!stream) + throw Domain::RelinkerException("Cannot open windows-diagnostics-imports.txt for writing"); + for (const auto& import : imports) + stream << import.Name << '\n'; + if (!stream) + throw Domain::RelinkerException("Cannot write windows-diagnostics-imports.txt"); + stream.close(); + std::cout << "Wrote Windows import diagnostics to " << path.string() << '\n'; +} + void writeGotStub(std::vector& sections, const std::uint32_t targetRva, const std::uint32_t stubRva) { for (auto& section : sections) { if (targetRva < section.Rva || targetRva - section.Rva > section.Data.size() - 4) @@ -63,6 +79,8 @@ std::vector WindowsPePatcher::Patch(const std::vector