From 2b21d42b7620b238482503b0f80a9d2d9b2f6d2f Mon Sep 17 00:00:00 2001 From: Alexandre Figueiredo Date: Wed, 28 Oct 2026 00:22:20 +0000 Subject: [PATCH] feat(libc): export the remaining aligned and array operator new and delete overloads --- core/libs/prx/libc/include/Api.hpp | 8 ++++ core/libs/prx/libc/src/StdioExtras.cpp | 40 ++++++++++++++++++++ core/libs/tests/GuestNewHandler.cpp | 51 +++++++++++++++++++++++++- docs/dev/debt/libc.md | 2 + 4 files changed, 100 insertions(+), 1 deletion(-) diff --git a/core/libs/prx/libc/include/Api.hpp b/core/libs/prx/libc/include/Api.hpp index 746baa5374..fb40e21dd8 100644 --- a/core/libs/prx/libc/include/Api.hpp +++ b/core/libs/prx/libc/include/Api.hpp @@ -918,6 +918,14 @@ void* APS5_VABI _Znam_nid_postfix(std::size_t size); void* APS5_VABI _ZnwmSt11align_val_t_nid_postfix(std::size_t size, std::size_t alignment); void* APS5_VABI _ZnwmRKSt9nothrow_t_nid_postfix(std::size_t size, const void*) noexcept; void* APS5_VABI _ZnamRKSt9nothrow_t_nid_postfix(std::size_t size, const void*) noexcept; +void* APS5_VABI _ZnwmSt11align_val_tRKSt9nothrow_t_nid_postfix(std::size_t size, std::size_t alignment, const void*) noexcept; +void* APS5_VABI _ZnamSt11align_val_t_nid_postfix(std::size_t size, std::size_t alignment); +void* APS5_VABI _ZnamSt11align_val_tRKSt9nothrow_t_nid_postfix(std::size_t size, std::size_t alignment, const void*) noexcept; +void APS5_VABI _ZdaPvRKSt9nothrow_t_nid_postfix(void* pointer, const void*) noexcept; +void APS5_VABI _ZdlPvSt11align_val_tRKSt9nothrow_t_nid_postfix(void* pointer, std::size_t alignment, const void*) noexcept; +void APS5_VABI _ZdaPvSt11align_val_t_nid_postfix(void* pointer, std::size_t alignment); +void APS5_VABI _ZdaPvmSt11align_val_t_nid_postfix(void* pointer, std::size_t, std::size_t alignment); +void APS5_VABI _ZdaPvSt11align_val_tRKSt9nothrow_t_nid_postfix(void* pointer, std::size_t alignment, const void*) noexcept; void APS5_VABI _ZdlPv_nid_postfix(void* pointer); void APS5_VABI _ZdlPvRKSt9nothrow_t_nid_postfix(void* pointer, const void*) noexcept; void APS5_VABI _ZdaPv_nid_postfix(void* pointer); diff --git a/core/libs/prx/libc/src/StdioExtras.cpp b/core/libs/prx/libc/src/StdioExtras.cpp index f6ebcc9784..621d57f3e9 100644 --- a/core/libs/prx/libc/src/StdioExtras.cpp +++ b/core/libs/prx/libc/src/StdioExtras.cpp @@ -156,4 +156,44 @@ void APS5_VABI _ZdlPvmSt11align_val_t_nid_postfix(void* pointer, std::size_t, st if (pointer != nullptr) ApplicationHeapFree_nid_no_patch(pointer); } +void* APS5_VABI _ZnwmSt11align_val_tRKSt9nothrow_t_nid_postfix(std::size_t size, std::size_t alignment, const void*) noexcept { + try { + return _ZnwmSt11align_val_t_nid_postfix(size, alignment); + } catch (const std::bad_alloc&) { + return nullptr; + } +} + +void* APS5_VABI _ZnamSt11align_val_t_nid_postfix(std::size_t size, std::size_t alignment) { + return _ZnwmSt11align_val_t_nid_postfix(size, alignment); +} + +void* APS5_VABI _ZnamSt11align_val_tRKSt9nothrow_t_nid_postfix(std::size_t size, std::size_t alignment, const void*) noexcept { + try { + return _ZnamSt11align_val_t_nid_postfix(size, alignment); + } catch (const std::bad_alloc&) { + return nullptr; + } +} + +void APS5_VABI _ZdaPvRKSt9nothrow_t_nid_postfix(void* pointer, const void*) noexcept { + _ZdaPv_nid_postfix(pointer); +} + +void APS5_VABI _ZdlPvSt11align_val_tRKSt9nothrow_t_nid_postfix(void* pointer, std::size_t alignment, const void*) noexcept { + _ZdlPvSt11align_val_t_nid_postfix(pointer, alignment); +} + +void APS5_VABI _ZdaPvSt11align_val_t_nid_postfix(void* pointer, std::size_t alignment) { + _ZdlPvSt11align_val_t_nid_postfix(pointer, alignment); +} + +void APS5_VABI _ZdaPvmSt11align_val_t_nid_postfix(void* pointer, std::size_t, std::size_t alignment) { + _ZdaPvSt11align_val_t_nid_postfix(pointer, alignment); +} + +void APS5_VABI _ZdaPvSt11align_val_tRKSt9nothrow_t_nid_postfix(void* pointer, std::size_t alignment, const void*) noexcept { + _ZdaPvSt11align_val_t_nid_postfix(pointer, alignment); +} + } diff --git a/core/libs/tests/GuestNewHandler.cpp b/core/libs/tests/GuestNewHandler.cpp index 29b5acfc3c..8e5d3c509d 100644 --- a/core/libs/tests/GuestNewHandler.cpp +++ b/core/libs/tests/GuestNewHandler.cpp @@ -17,7 +17,7 @@ using Handler = void (APS5_VABI*)(); namespace { -alignas(16) std::array storage{}; +alignas(256) std::array storage{}; unsigned failuresLeft = 0; unsigned allocations = 0; unsigned releases = 0; @@ -99,6 +99,9 @@ int RunDefaultHeap() { REQUIRE(Throws([] { _Znam_nid_postfix(huge); })); REQUIRE(_ZnwmRKSt9nothrow_t_nid_postfix(huge, nullptr) == nullptr); REQUIRE(_ZnamRKSt9nothrow_t_nid_postfix(huge, nullptr) == nullptr); + REQUIRE(Throws([] { _ZnamSt11align_val_t_nid_postfix(huge, 64); })); + REQUIRE(_ZnwmSt11align_val_tRKSt9nothrow_t_nid_postfix(huge, 64, nullptr) == nullptr); + REQUIRE(_ZnamSt11align_val_tRKSt9nothrow_t_nid_postfix(huge, 64, nullptr) == nullptr); handlerCalls = 0; REQUIRE(_ZSt15set_new_handlerPFvvE_nid_postfix(&uninstallingHandler) == nullptr); @@ -204,6 +207,52 @@ int main(int argc, char** argv) { REQUIRE(_ZnwmSt11align_val_t_nid_postfix(8, 16) == storage.data() && handlerCalls == 2 && allocations == 3); _ZSt15set_new_handlerPFvvE_nid_postfix(nullptr); + Reset(0); + void* array = _ZnamSt11align_val_t_nid_postfix(40, 64); + REQUIRE(array == storage.data() && lastSize == 40 && lastAlignment == 64 && allocations == 1); + _ZdaPvSt11align_val_t_nid_postfix(array, 64); + REQUIRE(releases == 1); + Reset(0); + REQUIRE(_ZnamSt11align_val_t_nid_postfix(0, 32) == storage.data() && lastSize == 1 && lastAlignment == 32); + Reset(1); + REQUIRE(Throws([] { _ZnamSt11align_val_t_nid_postfix(8, 32); }) && handlerCalls == 0 && allocations == 1); + + Reset(0); + REQUIRE(_ZnwmSt11align_val_tRKSt9nothrow_t_nid_postfix(24, 128, ¬hrowTag) == storage.data() && lastSize == 24 && lastAlignment == 128); + Reset(1); + REQUIRE(_ZnwmSt11align_val_tRKSt9nothrow_t_nid_postfix(24, 128, ¬hrowTag) == nullptr && allocations == 1); + Reset(0); + REQUIRE(_ZnamSt11align_val_tRKSt9nothrow_t_nid_postfix(56, 256, ¬hrowTag) == storage.data() && lastSize == 56 && lastAlignment == 256); + Reset(1); + REQUIRE(_ZnamSt11align_val_tRKSt9nothrow_t_nid_postfix(56, 256, ¬hrowTag) == nullptr && allocations == 1); + + _ZSt15set_new_handlerPFvvE_nid_postfix(&countingHandler); + Reset(2); + REQUIRE(_ZnamSt11align_val_t_nid_postfix(8, 64) == storage.data() && handlerCalls == 2 && allocations == 3); + Reset(1); + REQUIRE(_ZnwmSt11align_val_tRKSt9nothrow_t_nid_postfix(8, 64, ¬hrowTag) == storage.data() && handlerCalls == 1 && allocations == 2); + Reset(1); + REQUIRE(_ZnamSt11align_val_tRKSt9nothrow_t_nid_postfix(8, 64, ¬hrowTag) == storage.data() && handlerCalls == 1 && allocations == 2); + _ZSt15set_new_handlerPFvvE_nid_postfix(&throwingHandler); + Reset(1); + REQUIRE(_ZnwmSt11align_val_tRKSt9nothrow_t_nid_postfix(8, 64, ¬hrowTag) == nullptr && handlerCalls == 1 && allocations == 1); + Reset(1); + REQUIRE(_ZnamSt11align_val_tRKSt9nothrow_t_nid_postfix(8, 64, ¬hrowTag) == nullptr && handlerCalls == 1 && allocations == 1); + _ZSt15set_new_handlerPFvvE_nid_postfix(nullptr); + + Reset(0); + _ZdaPvRKSt9nothrow_t_nid_postfix(storage.data(), ¬hrowTag); + _ZdlPvSt11align_val_tRKSt9nothrow_t_nid_postfix(storage.data(), 64, ¬hrowTag); + _ZdaPvmSt11align_val_t_nid_postfix(storage.data(), 40, 64); + _ZdaPvSt11align_val_tRKSt9nothrow_t_nid_postfix(storage.data(), 64, ¬hrowTag); + REQUIRE(releases == 4); + _ZdaPvRKSt9nothrow_t_nid_postfix(nullptr, ¬hrowTag); + _ZdlPvSt11align_val_tRKSt9nothrow_t_nid_postfix(nullptr, 64, ¬hrowTag); + _ZdaPvSt11align_val_t_nid_postfix(nullptr, 64); + _ZdaPvmSt11align_val_t_nid_postfix(nullptr, 0, 64); + _ZdaPvSt11align_val_tRKSt9nothrow_t_nid_postfix(nullptr, 64, ¬hrowTag); + REQUIRE(releases == 4); + std::puts("Guest new handler checks passed"); return 0; } diff --git a/docs/dev/debt/libc.md b/docs/dev/debt/libc.md index 568e1931c1..1d14250e78 100644 --- a/docs/dev/debt/libc.md +++ b/docs/dev/debt/libc.md @@ -104,6 +104,8 @@ - [strtof and wcstof](../../../core/libs/prx/libc/src/Strings.cpp) (libc) round to the nearest float, where the console's `_Stof` in `libSceLibcInternal.sprx` (0x734a0) does not always: about a third of decimal inputs within a relative 2^-51 of a point halfway between two floats, and some hexadecimal inputs with more than 24 significant bits, come out one unit apart in the last place (`1e-40` is `0x1.16c3p-133` there and `0x1.16c2p-133` here). The console's own rounding is not reproduced - [strtof and wcstof](../../../core/libs/prx/libc/src/Strings.cpp) (libc) - `ERANGE` follows the `libc.prx` bundled with PPSA08427 (J03547016) and PPSA03671 (J03848509), both run side by side with this libc on a Linux host: a decimal input whose result is a nonzero subnormal sets it only when inexact, a hexadecimal one only when it lies below the smallest subnormal and rounds up to it, and underflow to zero and overflow set it. Both builds also take a `NAN(...)` payload in `strtof` from the sequence's leading digits (`nan(0x123)` is `0x7fc00123`, `-nan(5)` is `0xffc00005`); the sign is kept here, the payload is not. `libSceLibcInternal`'s `_Stof` differs: no `ERANGE` for hexadecimal input, a positive `nan`, and after a minus sign a failed parse is `-0.0` for a lone sign and `+0.0` before an `inf`/`nan` spelling, the reverse of `libc.prx`. Decimal `strtod` sets `ERANGE` for a nonzero subnormal in J03547016 but not in J03848509 or `libSceLibcInternal`, whose result is kept. +- [operator new](../../../core/libs/prx/libc/src/StdioExtras.cpp) (libc) - the `nothrow` forms return null only for `std::bad_alloc`, where the `libc.prx` bundled with PPSA21564 (build `J03093718`) catches every exception there, so a new handler that throws anything else ends the process instead of returning null. The aligned forms throw `std::bad_alloc` for an alignment of zero or one that is not a power of two, where that binary calls `abort`. + - [Guest time conversions](../../../core/libs/prx/libc/src/Time.cpp) rewrite the host process `TZ` when a guest changes its own `TZ`, so other host consumers of that variable (including libSceRtc local time) shift too; code outside libc does not take the zone lock. Zone syntax follows the host C runtime, not FreeBSD: Windows only accepts the `std offset [dst]` form, treats an empty guest `TZ` as `UTC0` and reads `:Europe/Berlin` as an abbreviation (for example `rop`); only Linux honours zone file names. If the guest accepts a `TZ` value that the host rejects (on Windows, for example, one longer than the host environment limit), conversions throw `std::runtime_error`. A `tm_zone` saved before a `TZ` change can refer to storage now holding the new zone name; `strftime` does not read it. - [cbrtf](../../../core/libs/prx/libc/src/Math.cpp) (libc) is FreeBSD msun's [`s_cbrtf.c`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/lib/msun/src/s_cbrtf.c): a 5-bit first guess from the exponent bits, then two Newton steps in double precision. It sets no `errno`, as the `libc.prx` that titles ship does not. Floating-point exceptions are not compared. It gives the same bits as the `cbrtf` of the `libc.prx` bundled with PPSA07923, PPSA05916 and PPSA13934 for all 2^32 arguments, run on a Windows host. - [logf](../../../core/libs/prx/libc/src/Math.cpp) (libc) is FreeBSD msun's [`e_logf.c`](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/lib/msun/src/e_logf.c), with `vzero` read at run time as msun's `volatile` read does. As in the `libc.prx` that titles ship, `errno` is set to `ERANGE` for ±0 and to `EDOM` for every argument with the sign bit set, negative NaNs and -inf included; positive NaNs and +inf leave it unchanged. Floating-point exceptions are not compared. It gives the same bits and `errno` as the `logf` of the `libc.prx` bundled with PPSA07923 and PPSA05916 for all 2^32 arguments, run on a Windows host. The FMA-contracted build bundled with PPSA13934 differs from it by 1 ulp on 961,881 arguments, so no single port matches every title's last bit.