diff --git a/core/libs/prx/libSceAmpr/Export.cpp b/core/libs/prx/libSceAmpr/Export.cpp index ca51ae70f2a6..8da65638666b 100644 --- a/core/libs/prx/libSceAmpr/Export.cpp +++ b/core/libs/prx/libSceAmpr/Export.cpp @@ -874,7 +874,7 @@ void APS5_VABI _Lock_shared_ptr_spin_lock_nid_postfix(void) { APS5_EXPORT("0hlfW1O4Aa4", sceAmprLocaleconv); SonyLconv* APS5_VABI sceAmprLocaleconv(void) { - return SonyLocaleconv(); + return SonyLocaleconv_nid_no_patch(); } APS5_EXPORT("802pFCwC9w0", sceAmprUdivti3); @@ -904,7 +904,7 @@ int APS5_VABI sceAmprWcscpyS(std::uint16_t* dest, std::size_t size, const std::u APS5_EXPORT("ENLfKJEZTjE", sceAmprSetConstraintHandlerS); void* APS5_VABI sceAmprSetConstraintHandlerS(void* handler) { - return SonySetConstraintHandlerS(handler); + return SonySetConstraintHandlerS_nid_no_patch(handler); } APS5_EXPORT("Jo9ON-AX9eU", sceAmprFltrounds); @@ -914,7 +914,7 @@ int APS5_VABI sceAmprFltrounds(void) { APS5_EXPORT("ODGONXcSmz4", sceAmprIgnoreHandlerS); void APS5_VABI sceAmprIgnoreHandlerS(const char* message, void* pointer, int error) { - SonyIgnoreHandlerS(message, pointer, error); + SonyIgnoreHandlerS_nid_no_patch(message, pointer, error); } APS5_EXPORT("sZLrjx-yEx4", sceAmprWcstombsS); diff --git a/core/libs/prx/libc/CMakeLists.txt b/core/libs/prx/libc/CMakeLists.txt index 233ff92c4435..65b723e2a1a3 100644 --- a/core/libs/prx/libc/CMakeLists.txt +++ b/core/libs/prx/libc/CMakeLists.txt @@ -89,6 +89,7 @@ target_sources(libc PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/RuntimeSupport.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/IntegerBuiltins.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/LocaleSupport.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/SonyCrt.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Process.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Hostname.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Environment.cpp diff --git a/core/libs/prx/libc/include/SonyCrt.hpp b/core/libs/prx/libc/include/SonyCrt.hpp index db53cd3d549e..0e40f962d993 100644 --- a/core/libs/prx/libc/include/SonyCrt.hpp +++ b/core/libs/prx/libc/include/SonyCrt.hpp @@ -22,24 +22,13 @@ extern "C" GuestTm* APS5_VABI localtime_s_nid_postfix(const std::int64_t* timer, using SonyConstraintHandler = void (APS5_VABI *)(const char* message, void* pointer, int error); -inline void APS5_VABI SonyDefaultConstraintHandler(const char* message, void* pointer, int error) { - (void)message; - (void)pointer; - (void)error; - std::abort(); -} - -inline std::mutex& SonyConstraintMutex() { - static std::mutex mutex; - return mutex; -} - -inline SonyConstraintHandler& SonyConstraintHandlerSlot() { - static SonyConstraintHandler handler = SonyDefaultConstraintHandler; - return handler; -} - -namespace { +std::mutex& SonyConstraintMutex(); +SonyConstraintHandler& SonyConstraintHandlerSlot(); +void APS5_VABI SonyDefaultConstraintHandler(const char* message, void* pointer, int error); +extern "C" void SonyInvokeConstraintHandler_nid_no_patch(const char* message, void* pointer, int error); +extern "C" void SonyIgnoreHandlerS_nid_no_patch(const char* message, void* pointer, int error); +extern "C" void* SonySetConstraintHandlerS_nid_no_patch(void* handler); +extern "C" SonyLconv* SonyLocaleconv_nid_no_patch(void); template inline TInteger ParseInteger(const char* text, char** end, int base, TInteger (*parse)(const char*, char**, int)) { @@ -56,12 +45,7 @@ inline TInteger ParseInteger(const char* text, char** end, int base, TInteger (* } inline void SonyInvokeConstraintHandler(const char* message, void* pointer, int error) { - SonyConstraintHandler handler = nullptr; - { - std::lock_guard lock(SonyConstraintMutex()); - handler = SonyConstraintHandlerSlot(); - } - if (handler) handler(message, pointer, error); + SonyInvokeConstraintHandler_nid_no_patch(message, pointer, error); } inline char* SonyStrtokS(char* str, const char* delim, char** context) { @@ -124,19 +108,6 @@ inline int SonyAsctimeS(char* buffer, std::size_t size, const GuestTm* time) { return SonyFormatAsctime(buffer, size, *time); } -inline void SonyIgnoreHandlerS(const char* message, void* pointer, int error) { - (void)message; - (void)pointer; - (void)error; -} - -inline void* SonySetConstraintHandlerS(void* handler) { - std::lock_guard lock(SonyConstraintMutex()); - void* previous = reinterpret_cast(SonyConstraintHandlerSlot()); - SonyConstraintHandlerSlot() = handler ? reinterpret_cast(handler) : SonyDefaultConstraintHandler; - return previous; -} - inline int SonyWcscpyS(std::uint16_t* dest, std::size_t size, const std::uint16_t* src) { constexpr int GuestEinval = 22; constexpr int GuestErange = 34; @@ -186,29 +157,6 @@ inline std::size_t SonyMbstowcs(std::uint16_t* dest, const char* src, std::size_ return converted; } -inline SonyLconv* SonyLocaleconv(void) { - static char empty[] = ""; - static char point[] = "."; - static char falseText[] = "false"; - static char trueText[] = "true"; - static SonyLconv value = [] { - SonyLconv init{}; - for (auto& pointer : init.monetary) pointer = empty; - for (auto& byte : init.chars) byte = CHAR_MAX; - init.decimal_point = point; - init.grouping = empty; - init.thousands_sep = empty; - init.frac_grouping = empty; - init.frac_sep = empty; - init.false_text = falseText; - init.true_text = trueText; - init.no_text = empty; - init.yes_text = empty; - return init; - }(); - return &value; -} - inline int SonyFpclassifyd(double value) { if (std::isnan(value)) return 2; if (std::isinf(value)) return 1; @@ -290,6 +238,4 @@ inline __int128 SonyModti3(__int128 dividend, __int128 divisor) { return (__int128)(negative ? (unsigned __int128)0 - remainder : remainder); } -} - #endif diff --git a/core/libs/prx/libc/src/FileAndHeap.cpp b/core/libs/prx/libc/src/FileAndHeap.cpp index 67f4ddd394df..a7732ac5d991 100644 --- a/core/libs/prx/libc/src/FileAndHeap.cpp +++ b/core/libs/prx/libc/src/FileAndHeap.cpp @@ -462,9 +462,15 @@ FileStream* APS5_VABI fopen_nid_postfix(const char* filename, const char* mode) int APS5_VABI fopen_s_nid_postfix(FileStream** result, const char* filename, const char* mode) { constexpr int GuestEinval = 22; - if (!result) return GuestEinval; + if (!result) { + SonyInvokeConstraintHandler("fopen_s", result, GuestEinval); + return GuestEinval; + } *result = nullptr; - if (!filename || !mode) return GuestEinval; + if (!filename || !mode) { + SonyInvokeConstraintHandler("fopen_s", result, GuestEinval); + return GuestEinval; + } *result = fopen_nid_postfix(filename, mode); return *result ? 0 : errno; } diff --git a/core/libs/prx/libc/src/LocaleSupport.cpp b/core/libs/prx/libc/src/LocaleSupport.cpp index e3384c2bdfbf..ac4a6f39f6de 100644 --- a/core/libs/prx/libc/src/LocaleSupport.cpp +++ b/core/libs/prx/libc/src/LocaleSupport.cpp @@ -519,7 +519,7 @@ void APS5_VABI init_env_nid_postfix(const InitEnvParams* params) { } SonyLconv* APS5_VABI localeconv_nid_postfix(void) { - return SonyLocaleconv(); + return SonyLocaleconv_nid_no_patch(); } } diff --git a/core/libs/prx/libc/src/SonyCrt.cpp b/core/libs/prx/libc/src/SonyCrt.cpp new file mode 100644 index 000000000000..0a562bbdab37 --- /dev/null +++ b/core/libs/prx/libc/src/SonyCrt.cpp @@ -0,0 +1,76 @@ +#include "prx/libc/include/SonyCrt.hpp" + +#include +#include +#include + +std::mutex& SonyConstraintMutex() { + static std::mutex mutex; + return mutex; +} + +SonyConstraintHandler& SonyConstraintHandlerSlot() { + static SonyConstraintHandler handler = SonyDefaultConstraintHandler; + return handler; +} + +void APS5_VABI SonyDefaultConstraintHandler(const char* message, void* pointer, int error) { + (void)message; + (void)pointer; + (void)error; + std::abort(); +} + +extern "C" void SonyIgnoreHandlerS_nid_no_patch(const char* message, void* pointer, int error) { + (void)message; + (void)pointer; + (void)error; +} + +extern "C" void* SonySetConstraintHandlerS_nid_no_patch(void* handler) { + std::lock_guard lock(SonyConstraintMutex()); + void* previous = reinterpret_cast(SonyConstraintHandlerSlot()); + SonyConstraintHandlerSlot() = handler ? reinterpret_cast(handler) : SonyDefaultConstraintHandler; + return previous; +} + +extern "C" void SonyInvokeConstraintHandler_nid_no_patch(const char* message, void* pointer, int error) { + SonyConstraintHandler handler = nullptr; + { + std::lock_guard lock(SonyConstraintMutex()); + handler = SonyConstraintHandlerSlot(); + } + if (handler) handler(message, pointer, error); +} + +namespace { + +char g_sonyLconvEmpty[] = ""; +char g_sonyLconvPoint[] = "."; +char g_sonyLconvFalseText[] = "false"; +char g_sonyLconvTrueText[] = "true"; + +SonyLconv& SonyAmprStorage() { + static SonyLconv value = [] { + SonyLconv init{}; + for (auto& pointer : init.monetary) pointer = g_sonyLconvEmpty; + for (auto& byte : init.chars) byte = CHAR_MAX; + init.decimal_point = g_sonyLconvPoint; + init.grouping = g_sonyLconvEmpty; + init.thousands_sep = g_sonyLconvEmpty; + init.frac_grouping = g_sonyLconvEmpty; + init.frac_sep = g_sonyLconvEmpty; + init.false_text = g_sonyLconvFalseText; + init.true_text = g_sonyLconvTrueText; + init.no_text = g_sonyLconvEmpty; + init.yes_text = g_sonyLconvEmpty; + return init; + }(); + return value; +} + +} + +extern "C" SonyLconv* SonyLocaleconv_nid_no_patch(void) { + return &SonyAmprStorage(); +} diff --git a/core/libs/prx/libc/src/WideConversion.cpp b/core/libs/prx/libc/src/WideConversion.cpp index 640effd1deec..5ed6a6ba1ad5 100644 --- a/core/libs/prx/libc/src/WideConversion.cpp +++ b/core/libs/prx/libc/src/WideConversion.cpp @@ -11,11 +11,11 @@ #include extern "C" void APS5_VABI ignore_handler_s_nid_postfix(const char* message, void* pointer, int error) { - SonyIgnoreHandlerS(message, pointer, error); + SonyIgnoreHandlerS_nid_no_patch(message, pointer, error); } extern "C" void* APS5_VABI set_constraint_handler_s_nid_postfix(void* handler) { - return SonySetConstraintHandlerS(handler); + return SonySetConstraintHandlerS_nid_no_patch(handler); } extern "C" std::size_t APS5_VABI wcrtomb_nid_postfix(char* destination, std::uint16_t value, void* state); diff --git a/core/libs/tests/GuestCrt.cpp b/core/libs/tests/GuestCrt.cpp index d2fe45d744d3..295c29529be0 100644 --- a/core/libs/tests/GuestCrt.cpp +++ b/core/libs/tests/GuestCrt.cpp @@ -37,12 +37,16 @@ __int128 APS5_VABI __modti3_nid_postfix(__int128 dividend, __int128 divisor); SonyLconv* APS5_VABI localeconv_nid_postfix(void); int APS5_VABI sceAmprFpclassifyd(double value); unsigned int APS5_VABI _ZSt14_Random_devicev_nid_postfix(); +void* APS5_VABI set_constraint_handler_s_nid_postfix(void* handler); +void APS5_VABI ignore_handler_s_nid_postfix(const char* message, void* pointer, int error); +int APS5_VABI wcscpy_s_nid_postfix(std::uint16_t* dest, std::size_t size, const std::uint16_t* src); +int APS5_VABI fopen_s_nid_postfix(void** result, const char* filename, const char* mode); } #define Require(condition) do { if (!(condition)) { std::fprintf(stderr, "guest_crt: line %d: %s\n", __LINE__, #condition); std::abort(); } } while (0) static int g_handlerCalls = 0; -static void CountingHandler(const char* message, void* pointer, int error) { +static void APS5_VABI CountingHandler(const char* message, void* pointer, int error) { (void)message; (void)pointer; (void)error; @@ -66,8 +70,12 @@ int main() { Require(sceAmprSinh(1.0, 2.0) == 2.0 * std::sinh(1.0)); Require(sceAmprSinh(0.0, 5.0) == 0.0); - SonyLconv* locale = sceAmprLocaleconv(); + SonyLconv* locale = localeconv_nid_postfix(); + Require(locale != nullptr); + SonyLconv* amprLocale = sceAmprLocaleconv(); + Require(amprLocale == locale); Require(reinterpret_cast(&locale->decimal_point) - reinterpret_cast(locale) == 0x48); + Require(reinterpret_cast(&rLocale->decimal_point) - reinterpret_cast(amprLocale) == 0x48); Require(std::strcmp(locale->decimal_point, ".") == 0); Require(std::memcmp(localeconv_nid_postfix()->chars, locale->chars, sizeof(locale->chars)) == 0); Require(std::strcmp(localeconv_nid_postfix()->decimal_point, ".") == 0); @@ -75,6 +83,7 @@ int main() { for (char value : locale->chars) Require(value == CHAR_MAX); Require(std::strcmp(locale->false_text, "false") == 0 && std::strcmp(locale->true_text, "true") == 0); Require(locale->grouping[0] == '\0' && locale->thousands_sep[0] == '\0'); + Require(SonyLocaleconv_nid_no_patch() == locale); Require(__udivti3_nid_postfix(100, 3) == 33); Require(SonyUdivti3(100, 3) == 33); @@ -159,23 +168,37 @@ int main() { } Require(trapped); - void* previous = sceAmprSetConstraintHandlerS(reinterpret_cast(&CountingHandler)); + void* previous = set_constraint_handler_s_nid_postfix(reinterpret_cast(&CountingHandler)); Require(previous != nullptr); Require(previous != reinterpret_cast(&CountingHandler)); const std::uint16_t hello[] = {'h', 'i', 0}; std::uint16_t buffer[8] = {}; - Require(sceAmprWcscpyS(buffer, 8, hello) == 0); + Require(wcscpy_s_nid_postfix(buffer, 8, hello) == 0); Require(buffer[0] == 'h' && buffer[1] == 'i' && buffer[2] == 0); const std::uint16_t longText[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 0}; g_handlerCalls = 0; Require(sceAmprWcscpyS(buffer, 4, longText) == ERANGE); Require(g_handlerCalls == 1); + g_handlerCalls = 0; Require(sceAmprWcscpyS(nullptr, 4, longText) == EINVAL); + Require(g_handlerCalls == 1); + g_handlerCalls = 0; + Require(wcscpy_s_nid_postfix(nullptr, 4, longText) == EINVAL); + Require(g_handlerCalls == 1); + g_handlerCalls = 0; + Require(fopen_s_nid_postfix(nullptr, nullptr, nullptr) == EINVAL); + Require(g_handlerCalls == 1); + previous = sceAmprSetConstraintHandlerS(reinterpret_cast(&CountingHandler)); + Require(previous == reinterpret_cast(&CountingHandler)); + g_handlerCalls = 0; + Require(wcscpy_s_nid_postfix(buffer, 4, longText) == ERANGE); + Require(g_handlerCalls == 1); + sceAmprIgnoreHandlerS("msg", nullptr, 0); + ignore_handler_s_nid_postfix("msg", nullptr, 0); previous = sceAmprSetConstraintHandlerS(nullptr); Require(previous == reinterpret_cast(&CountingHandler)); - previous = sceAmprSetConstraintHandlerS(reinterpret_cast(&CountingHandler)); + previous = set_constraint_handler_s_nid_postfix(reinterpret_cast(&CountingHandler)); Require(previous != nullptr && previous != reinterpret_cast(&CountingHandler)); - sceAmprIgnoreHandlerS("msg", nullptr, 0); char text[64]; GuestTm known{}; @@ -238,4 +261,15 @@ int main() { bool randomChanged = false; for (int draw = 0; draw < 8; ++draw) randomChanged |= _ZSt14_Random_devicev_nid_postfix() != firstRandom; Require(randomChanged); + + set_constraint_handler_s_nid_postfix(nullptr); + void* const defaultHandler = set_constraint_handler_s_nid_postfix(reinterpret_cast(&CountingHandler)); + Require(defaultHandler != nullptr && defaultHandler != reinterpret_cast(&CountingHandler)); + Require(reinterpret_cast(SonyConstraintHandlerSlot()) == reinterpret_cast(&CountingHandler)); + Require(sceAmprSetConstraintHandlerS(nullptr) == reinterpret_cast(&CountingHandler)); + Require(reinterpret_cast(SonyConstraintHandlerSlot()) == defaultHandler); + Require(set_constraint_handler_s_nid_postfix(reinterpret_cast(&CountingHandler)) == defaultHandler); + Require(set_constraint_handler_s_nid_postfix(nullptr) == reinterpret_cast(&CountingHandler)); + Require(reinterpret_cast(SonyConstraintHandlerSlot()) == defaultHandler); + return 0; } diff --git a/core/libs/tests/GuestFiopen.cpp b/core/libs/tests/GuestFiopen.cpp index 044265f11c6e..98777e987420 100644 --- a/core/libs/tests/GuestFiopen.cpp +++ b/core/libs/tests/GuestFiopen.cpp @@ -11,6 +11,8 @@ extern "C" { FileStream* APS5_VABI _ZSt7_FiopenPKcNSt5_IosbIiE9_OpenmodeEi_nid_postfix(const char*, int, int); FileStream* APS5_VABI fopen_nid_postfix(const char*, const char*); int APS5_VABI fopen_s_nid_postfix(FileStream**, const char*, const char*); +void APS5_VABI ignore_handler_s_nid_postfix(const char* message, void* pointer, int error); +void* APS5_VABI set_constraint_handler_s_nid_postfix(void* handler); int APS5_VABI fclose_nid_postfix(FileStream*); std::int64_t APS5_VABI ftello_nid_postfix(FileStream*); } @@ -69,6 +71,7 @@ int main() { Write(Open(file, Out), "abc"); Require(Contents(file) == "abc"); + set_constraint_handler_s_nid_postfix(reinterpret_cast(&ignore_handler_s_nid_postfix)); FileStream* checkedStream = &_Stdout_nid_postfix; Require(fopen_s_nid_postfix(&checkedStream, nullptr, "rb") == GuestEinval); Require(checkedStream == nullptr); diff --git a/docs/dev/debt/libc.md b/docs/dev/debt/libc.md index abcc20d6f111..b366ef3428a4 100644 --- a/docs/dev/debt/libc.md +++ b/docs/dev/debt/libc.md @@ -49,8 +49,8 @@ - [snprintf_s and vsnprintf_s](../../../core/libs/prx/libc/src/Formatting.cpp) (libc) - follow C11 K.3.5.3.5 and K.3.5.3.12 with `RSIZE_MAX` assumed to be `SIZE_MAX >> 1`, as `snwprintf_s` does: a null buffer or format, `size` of 0 or above `RSIZE_MAX`, any `%n` and a null `%s`, `%ls` or `%S` argument return a negative value and set `buffer[0]` to zero when the buffer and size allow it; otherwise the output is truncated to the buffer and the full length is returned. A violation only returns: the constraint handler is not invoked, and the console's error value is unverified. Not confirmed on a PS5 title - [vsscanf_s](../../../core/libs/prx/libc/src/Formatting.cpp) (libc) - returns the count of items converted before an undersized `%s`/`%c`/`%[` destination (0 when it is the first), not EOF. Not confirmed on a PS5 title - [setlocale](../../../core/libs/prx/libc/src/StdioExtras.cpp) (libc) knows only the "C" locale, as the `libc.prx` that titles ship does: it ignores the category, returns "C" for a null, empty or "C" name and a null pointer for any other, "POSIX" included, without setting `errno`. Checked by running the copies bundled with PPSA07923 and PPSA13934 side by side for categories -40 to 79 and 2^30, not measured on a console. -- [localeconv](../../../core/libs/prx/libc/src/LocaleSupport.cpp) (libc, re-exported by libSceAmpr) - the 0x90-byte Dinkumware struct (empty monetary strings, `CHAR_MAX` for the 14 `char` fields, `decimal_point` ".", `_False`/`_True` "false"/"true") is read from the `libc.prx` that Astro's Playroom ships. It is a fixed C-locale table: whether the console changes it with `setlocale` is not known, and `_Frac_grouping`, `_Frac_sep`, `_No`, `_Yes` are empty strings as in that dump -- [set_constraint_handler_s](../../../core/libs/prx/libc/src/WideConversion.cpp) (libc, re-exported by libSceAmpr) - the default handler is assumed to abort; each module keeps its own handler state, so a handler set through one library is not seen by the other. Within a module every `_s` function calls the installed handler, with the guest's calling convention. `ctime_s` and `asctime_s` follow the Dinkumware 26-byte format with `EINVAL` for a buffer under 26 bytes, a null pointer or an out-of-range weekday/month; `ctime_s` uses the guest time zone of `localtime_s`, and the console's exact checks are not verified +- [localeconv](../../../core/libs/prx/libc/src/LocaleSupport.cpp) (libc, re-exported by libSceAmpr) - the 0x90-byte Dinkumware struct (empty monetary strings, `CHAR_MAX` for the 14 `char` fields, `decimal_point` ".", `_False`/`_True` "false"/"true") is read from the `libc.prx` that Astro's Playroom ships. It is a fixed C-locale table: whether the console changes it with `setlocale` is not known, and `_Frac_grouping`, `_Frac_sep`, `_No`, `_Yes` are empty strings as in that dump. The table is shared between libc and libSceAmpr through a single definition in libc +- [set_constraint_handler_s](../../../core/libs/prx/libc/src/WideConversion.cpp) (libc, re-exported by libSceAmpr) - the default handler is assumed to abort; the handler state is shared between libc and libSceAmpr through a single definition in libc, while the console's cross-module visibility is unverified. Within a module every `_s` function calls the installed handler, with the guest's calling convention. `ctime_s` and `asctime_s` follow the Dinkumware 26-byte format with `EINVAL` for a buffer under 26 bytes, a null pointer or an out-of-range weekday/month; `ctime_s` uses the guest time zone of `localtime_s`, and the console's exact checks are not verified - [qsort_s](../../../core/libs/prx/libc/src/FileAndHeap.cpp) (libc) sorts as `qsort` does and passes its context as the comparator's third argument, as FreeBSD 13's [`qsort_s`](https://github.com/freebsd/freebsd-src/blob/release/13.0.0/lib/libc/stdlib/qsort.c#L97-L127) (C11 Annex K) does. It returns `EINVAL` after calling the constraint handler when `n` or `es` exceeds `RSIZE_MAX`, or when `n` is not 0 and `a` or `cmp` is null; FreeBSD 11, which the rest of libc follows, has no `qsort_s`. An element size of 0 sorts nothing, where FreeBSD loops over the same address. The console's behaviour is unverified; the Quake homebrew (#6698) imports it from its `libOSMesa.so.8` - [__udivti3](../../../core/libs/prx/libc/src/RuntimeSupport.cpp) (libc, re-exported by libSceAmpr) - the hardware traps on a zero divisor; this throws instead - [__umodti3](../../../core/libs/prx/libc/src/RuntimeSupport.cpp) (libc) - the hardware traps on a zero divisor; this throws instead