#include "prx/libkernel/Pthread/include/Mutex.hpp" #include #include #include #include #include #include #include #include struct Parameter { void* allocator; void* context; std::size_t bufferSize; std::int32_t mode; std::uint32_t reserved[3]; }; extern "C" { void APS5_VABI _ZN3sce4Json11InitializerC1Ev(void*); int APS5_VABI _ZN3sce4Json11Initializer10initializeEPKNS0_14InitParameter2E(void*, const Parameter*); int APS5_VABI _ZN3sce4Json11Initializer9terminateEv(void*); } static void Require(bool condition, const char* message) { if (!condition) { std::fprintf(stderr, "%s\n", message); std::abort(); } } struct Marker { const void* identity; }; struct InitializationExceptionTrace { void* allocator = nullptr; unsigned target = 0; std::vector requests; std::vector admitted; std::map live; unsigned releases = 0; }; static void* APS5_VABI AllocateExceptionTracked(void* self, std::size_t size, void* data) { auto& trace = *static_cast(data); Require(self == trace.allocator, "Initialization must preserve allocator receiver and context"); trace.requests.push_back(size); if (trace.requests.size() == trace.target + 1) throw Marker{&trace}; void* memory = std::malloc(size); Require(memory && trace.live.emplace(memory, trace.admitted.size()).second, "Every admitted allocation requires independent ownership"); std::memset(memory, 0xa5, size); trace.admitted.push_back(memory); return memory; } static void APS5_VABI ReleaseExceptionTracked(void* self, void* memory, void* data) { auto& trace = *static_cast(data); Require(self == trace.allocator && (!memory || trace.live.erase(memory) == 1), "Normal termination must release each admitted generation exactly once"); ++trace.releases; std::free(memory); } static void APS5_VABI NotifyExceptionTracked(void*, int, std::size_t, void*) { Require(false, "Thrown callbacks must not synthesize null-allocation notifications"); } static void* const Table[]{nullptr, nullptr, reinterpret_cast(AllocateExceptionTracked), reinterpret_cast(ReleaseExceptionTracked), reinterpret_cast(NotifyExceptionTracked)}; struct ExceptionAllocator { void* const* table = Table; }; int main(int argc, char** argv) { Require(argc == 2, "Each exception schedule requires an independent module process"); const auto target = static_cast(std::strtoul(argv[1], nullptr, 10)); Require(target <= 12, "Allocation schedule must include every initialization position and success"); ExceptionAllocator allocator; InitializationExceptionTrace trace; trace.allocator = &allocator; trace.target = target; Parameter parameter{&allocator, &trace, 16384, 0, {}}; std::uint8_t owner = 0; _ZN3sce4Json11InitializerC1Ev(&owner); bool caught = false; try { Require(_ZN3sce4Json11Initializer10initializeEPKNS0_14InitParameter2E(&owner, ¶meter) == 0, "Complete admission must initialize normally"); } catch (const Marker& marker) { Require(marker.identity == &trace, "Allocator exceptions must propagate with their original identity"); caught = true; } Require(caught == (target < 12) && owner == unsigned(target == 12) && trace.releases == 0 && trace.live.size() == target, "Thrown initialization callbacks must retain every prior allocation without publishing an active owner or rolling back"); const std::array sizes{24, 8, 24, 8, 32, 48, 8, 32, 104, 32, 8, 8}; const auto count = target < 12 ? target + 1 : 12; Require(trace.requests == std::vector(sizes.begin(), sizes.begin() + count), "Exception schedules must cross the exact initialization resource sequence"); const auto live = trace.live; const auto requests = trace.requests; const int notInitialized = static_cast(0x80848110), alreadyInitialized = static_cast(0x80848111); Require(_ZN3sce4Json11Initializer9terminateEv(&owner) == (caught ? notInitialized : 0), "An inactive owner must not reclaim the published partial context"); trace.target = 1000; if (target && caught) { std::uint8_t other = 0; _ZN3sce4Json11InitializerC1Ev(&other); Require(_ZN3sce4Json11Initializer10initializeEPKNS0_14InitParameter2E(&owner, ¶meter) == alreadyInitialized && _ZN3sce4Json11Initializer10initializeEPKNS0_14InitParameter2E(&other, ¶meter) == alreadyInitialized && _ZN3sce4Json11Initializer9terminateEv(&other) == notInitialized && !owner && !other && trace.live == live && trace.requests == requests && !trace.releases, "A published partial context must reject retries from both owners without changing allocations"); if (target > 10) Require(scePthreadMutexDestroy(static_cast(trace.admitted[10])) == 0, "The fixture must reclaim its orphaned host mutex separately"); for (const auto& [pointer, generation] : trace.live) std::free(pointer); trace.live.clear(); } else { Require(trace.live.empty() && trace.releases == (target == 12 ? 12u : 0u), "Normal termination must reclaim all twelve resources"); Require(_ZN3sce4Json11Initializer10initializeEPKNS0_14InitParameter2E(&owner, ¶meter) == 0 && owner && trace.live.size() == 12, "Unpublished exceptions and completed lifecycles must allow a fresh initialization"); Require(_ZN3sce4Json11Initializer9terminateEv(&owner) == 0 && !owner && trace.live.empty(), "Fresh initialization must retain complete teardown"); } return 0; }