#include "prx/libSceLibcInternal/include/Api.hpp" #include "prx/libc/include/Api.hpp" #include "prx/libc/include/ApplicationHeap.hpp" #include "prx/libc/include/general/VabiMacros.hpp" #include #include #include #include #include #include namespace LibcException { struct TypeInfoVtable; } extern "C" { extern LibcException::TypeInfoVtable _ZTVN10__cxxabiv117__class_type_infoE_nid_postfix; extern LibcException::TypeInfoVtable _ZTVN10__cxxabiv120__si_class_type_infoE_nid_postfix; } namespace { struct TypeRecord { const void* vtable; const char* name; const TypeRecord* base; }; using Allocate = void* (APS5_VABI*)(void*, std::size_t, std::size_t); using Deallocate = void (APS5_VABI*)(void*, void*, std::size_t, std::size_t); using IsEqual = bool (APS5_VABI*)(const void*, const void*); using Destroy = void (APS5_VABI*)(void*); void Require(bool condition, const char* message) { if (condition) return; std::fprintf(stderr, "libSceLibcInternal memory resource: %s\n", message); std::abort(); } const void* const* Vtable(const void* resource) { return *static_cast(resource); } const TypeRecord* TypeOf(const void* resource) { return static_cast(Vtable(resource)[-1]); } template TFunction Slot(const void* resource, std::size_t index) { return reinterpret_cast(const_cast(Vtable(resource)[index])); } const void* TypeInfoVptr(LibcException::TypeInfoVtable& vtable) { return reinterpret_cast(&vtable) + 2 * sizeof(void*); } void CheckTypes(const void* resource, const char* name) { Require(reinterpret_cast(Vtable(resource)[-2]) == 0, "offset to top is 0"); const TypeRecord* type = TypeOf(resource); Require(std::strcmp(type->name, name) == 0, name); Require(type->vtable == TypeInfoVptr(_ZTVN10__cxxabiv120__si_class_type_infoE_nid_postfix), "resource type is a single-base class"); const TypeRecord* identity = type->base; Require(std::strcmp(identity->name, "NSt3pmr24_Identity_equal_resourceE") == 0, "_Identity_equal_resource base"); Require(identity->vtable == TypeInfoVptr(_ZTVN10__cxxabiv120__si_class_type_infoE_nid_postfix), "_Identity_equal_resource is a single-base class"); const TypeRecord* root = identity->base; Require(std::strcmp(root->name, "NSt3pmr15memory_resourceE") == 0, "memory_resource root"); Require(root->vtable == TypeInfoVptr(_ZTVN10__cxxabiv117__class_type_infoE_nid_postfix), "memory_resource has no base"); } } int main() { std::array api{}; ApplicationHeapRegister_nid_no_patch(api.data()); void* newDelete = sceLibcInternalPmrNewDeleteResource_nid_no_patch(); void* nullRes = sceLibcInternalPmrNullMemoryResource_nid_no_patch(); void* aligned = _Aligned_new_delete_resource_nid_postfix(); void* unaligned = _Unaligned_new_delete_resource_nid_postfix(); void* libcNull = null_memory_resource_nid_postfix(); Require(newDelete != nullptr, "new_delete_resource returned null"); Require(nullRes != nullptr, "null_memory_resource returned null"); Require(newDelete == sceLibcInternalPmrNewDeleteResource_nid_no_patch(), "new_delete_resource is stable"); Require(nullRes == sceLibcInternalPmrNullMemoryResource_nid_no_patch(), "null_memory_resource is stable"); Require(newDelete != nullRes, "new_delete and null resources must be distinct"); Require(newDelete != aligned && newDelete != unaligned && newDelete != libcNull, "standard new_delete is distinct from __sce_v2 family"); Require(nullRes != aligned && nullRes != unaligned && nullRes != libcNull, "standard null_memory_resource is distinct from __sce_v2 family"); CheckTypes(newDelete, "NSt3pmr33_Aligned_new_delete_resource_implE"); CheckTypes(nullRes, "ZNSt3pmr20null_memory_resourceEvE14_Null_resource"); for (void* resource : {newDelete, nullRes}) { Require(Slot(resource, 4)(resource, resource), "is_equal to itself"); Slot(resource, 0)(resource); } Require(!Slot(newDelete, 4)(newDelete, nullRes), "new_delete is not null_memory_resource"); Require(!Slot(nullRes, 4)(nullRes, newDelete), "null_memory_resource is not new_delete"); Require(!Slot(newDelete, 4)(newDelete, aligned), "standard new_delete is not __sce_v2 aligned"); Require(!Slot(nullRes, 4)(nullRes, libcNull), "standard null is not __sce_v2 null"); for (const std::size_t alignment : {std::size_t{1}, std::size_t{16}, std::size_t{32}, std::size_t{64}, std::size_t{4096}}) { auto* block = static_cast(Slot(newDelete, 2)(newDelete, 200, alignment)); Require(block != nullptr && reinterpret_cast(block) % alignment == 0, "new_delete allocate honours alignment"); std::memset(block, 0x5a, 200); Slot(newDelete, 3)(newDelete, block, 200, alignment); } Slot(nullRes, 3)(nullRes, nullptr, 0, 1); Slot(nullRes, 3)(nullRes, newDelete, 64, 8); void* defaultRes = sceLibcInternalPmrGetDefaultResource_nid_no_patch(); Require(defaultRes == newDelete, "initial default resource matches new_delete_resource"); Require(_Aligned_get_default_resource_nid_postfix() == aligned, "initial aligned default matches aligned new_delete"); Require(_Unaligned_get_default_resource_nid_postfix() == unaligned, "initial unaligned default matches unaligned new_delete"); void* previousDefault = sceLibcInternalPmrSetDefaultResource_nid_no_patch(nullRes); Require(previousDefault == newDelete, "set_default_resource returned previous default"); Require(sceLibcInternalPmrGetDefaultResource_nid_no_patch() == nullRes, "get_default_resource reflects update"); Require(_Aligned_get_default_resource_nid_postfix() == aligned, "standard set_default_resource does not affect _Aligned_ default"); Require(_Unaligned_get_default_resource_nid_postfix() == unaligned, "standard set_default_resource does not affect _Unaligned_ default"); Require(_Aligned_set_default_resource_nid_postfix(libcNull) == aligned, "_Aligned_set_default_resource returned previous aligned default"); Require(_Aligned_get_default_resource_nid_postfix() == libcNull, "_Aligned_get_default_resource reflects update"); Require(sceLibcInternalPmrGetDefaultResource_nid_no_patch() == nullRes, "_Aligned_set_default_resource does not affect standard default"); void* previousFromNull = sceLibcInternalPmrSetDefaultResource_nid_no_patch(nullptr); Require(previousFromNull == nullRes, "set_default_resource with null returns previous default"); Require(sceLibcInternalPmrGetDefaultResource_nid_no_patch() == newDelete, "set_default_resource with null resets default to new_delete"); Require(_Aligned_get_default_resource_nid_postfix() == libcNull, "standard reset does not affect _Aligned_ default"); Require(_Aligned_set_default_resource_nid_postfix(nullptr) == libcNull, "_Aligned_set_default_resource with null returns previous default"); Require(_Aligned_get_default_resource_nid_postfix() == aligned, "_Aligned_get_default_resource restored"); return 0; }