diff --git a/core/libs/prx/libc/src/Errors.cpp b/core/libs/prx/libc/src/Errors.cpp index b52a211209..f934c4de09 100644 --- a/core/libs/prx/libc/src/Errors.cpp +++ b/core/libs/prx/libc/src/Errors.cpp @@ -46,9 +46,25 @@ constexpr const char* messages[] = { static_assert(sizeof(messages) / sizeof(messages[0]) == 97); bool NamedByStrerror(int error) { return error > 0 && error < 95; } +constexpr struct { + int error; + const char* text; +} extendedMessages[] = { + {107, "State not recoverable"}, {108, "Owner dead"}, {152, "File positioning error"}, + {1040, "No message available"}, {1050, "No stream resources"}, {1051, "Not a stream"}, + {1062, "Other"}, {1074, "Stream timeout"}, +}; + +const char* ExtendedMessage(int error) { + for (const auto& entry : extendedMessages) + if (entry.error == error) return entry.text; + return nullptr; +} + void Describe(int error, char (&text)[128]) { if (error == 0) std::snprintf(text, sizeof(text), "No error"); else if (NamedByStrerror(error)) std::snprintf(text, sizeof(text), "%s", messages[error]); + else if (const char* extended = ExtendedMessage(error)) std::snprintf(text, sizeof(text), "%s", extended); else std::snprintf(text, sizeof(text), "error_code=0x%08x", static_cast(error)); } } diff --git a/core/libs/tests/GuestErrors.cpp b/core/libs/tests/GuestErrors.cpp index 25ac0bb23a..2ea161da65 100644 --- a/core/libs/tests/GuestErrors.cpp +++ b/core/libs/tests/GuestErrors.cpp @@ -68,7 +68,21 @@ int main() { Require(std::strcmp(strerror_nid_postfix(error), expected) == 0); Require(*__error_nid_postfix() == 13); } - for (const int error : {97, 100, 1000, 9999, INT_MAX, -1, INT_MIN}) { + const struct { + int error; + const char* text; + } extended[] = { + {107, "State not recoverable"}, {108, "Owner dead"}, {152, "File positioning error"}, + {1040, "No message available"}, {1050, "No stream resources"}, {1051, "Not a stream"}, + {1062, "Other"}, {1074, "Stream timeout"}, + }; + for (const auto& entry : extended) { + Require(strerror_r_nid_postfix(entry.error, buffer, sizeof(buffer)) == 0); + Require(std::strcmp(buffer, entry.text) == 0); + Require(std::strcmp(strerror_nid_postfix(entry.error), entry.text) == 0); + Require(*__error_nid_postfix() == 13); + } + for (const int error : {97, 100, 106, 109, 151, 153, 1000, 1039, 1041, 1075, 9999, INT_MAX, -1, INT_MIN}) { char coded[32]; std::snprintf(coded, sizeof(coded), "error_code=0x%08x", static_cast(error)); Require(strerror_r_nid_postfix(error, buffer, sizeof(buffer)) == 0); diff --git a/docs/dev/debt/libc.md b/docs/dev/debt/libc.md index 0fe5630112..f29dc649fd 100644 --- a/docs/dev/debt/libc.md +++ b/docs/dev/debt/libc.md @@ -69,7 +69,7 @@ - [wcsncat_s and wcscat_s](../../../core/libs/prx/libc/src/Strings.cpp) (libc) refuse a copy as overlapping only when the written range `[dest + len1, dest + len1 + len2)` meets the read range `[src, src + len2)`. The rule comes from a static reading of the console's `wcsncat_s` in `libc.prx` (not run on a console); `wcscat_s` gets it through `wcsncat_s`, and its own console code was not read. The narrow `strncat_s` keeps a wider rule, `[dest, dest + used + length + 1)` against `[src, src + length + 1)`, whose console code was not read either, so it refuses some copies whose wide equivalent proceeds. - [snwprintf_s](../../../core/libs/prx/libc/src/FormattingWide.cpp) (libc) - follows C11 K.3.9.1.3 with `RSIZE_MAX` assumed to be `SIZE_MAX >> 1`. A runtime-constraint violation only returns a negative value: the handler installed with `set_constraint_handler_s` is not invoked, and the console's default handler is unknown. An invalid multibyte `%s` argument is copied byte by byte instead of being reported as an encoding error, as in `vswprintf` - [swprintf_s and vswprintf_s](../../../core/libs/prx/libc/src/FormattingWide.cpp) (libc) - follow C11 K.3.9.1.4 with `RSIZE_MAX` assumed to be `(SIZE_MAX >> 1) / sizeof(char16_t)`: the complete output, including the terminating null, must fit in the buffer, otherwise it is a runtime-constraint violation that sets `buffer[0]` to zero and returns a negative value, and the buffer is not partially written. Argument and encoding checks match `snwprintf_s` -- [strerror, strerror_r and perror](../../../core/libs/prx/libc/src/Errors.cpp) (libc) - match the system `libSceLibcInternal` of PS5 firmware 13.60 and the `libc.prx` that CUSA04865 and CUSA12435 ship, loaded on a PS4 Pro (firmware 10.71): 0 gives "No error", 1 to 94 the FreeBSD texts and any other number "error_code=0x%08x"; `perror` prints the same texts; errno is left alone. `strerror_r` follows the `libc.prx` that PPSA31872 ships, read statically: a buffer under 23 bytes returns `ERANGE` (34) without writing, otherwise it `strncpy`s length - 1 bytes, terminates the last byte and returns 0, so a long text is cut. Firmware 13.60 agreed at 0, 1, 5, 16 and 256 bytes; 17 to 255 bytes and the cut were not run on a console. A null buffer of 23 bytes or more throws, as the console writes through it. +- [strerror, strerror_r and perror](../../../core/libs/prx/libc/src/Errors.cpp) (libc) - follow the `libc.prx` that PPSA05916, PPSA07923 and PPSA13934 ship, run natively over -2000 to 2000, `INT_MAX` and `INT_MIN`: 0 gives "No error", 1 to 94 the FreeBSD texts, 107, 108, 152, 1040, 1050, 1051, 1062 and 1074 their own texts, and any other number "error_code=0x%08x"; `perror` prints the same texts and errno is left alone. Firmware 13.60's `libSceLibcInternal` and the PS4 `libc.prx` of CUSA04865 and CUSA12435 agreed at the numbers measured there (0 to 96, 97, 100, 1000, -1, `INT_MAX`, `INT_MIN`); the eight extra names were not run on a console. `strerror_r` follows PPSA31872's `libc.prx`, read statically: under 23 bytes it returns `ERANGE` (34) without writing, otherwise it `strncpy`s length - 1 bytes, terminates the last byte and returns 0. Firmware 13.60 agreed at 0, 1, 5, 16 and 256 bytes. A null buffer of 23 bytes or more throws, as the console writes through it. - [strerror_s and strerrorlen_s](../../../core/libs/prx/libc/src/WideConversion.cpp) (libc) - a null destination, a capacity of 0 or one above `RSIZE_MAX` (`SIZE_MAX >> 1` here) calls the registered guest-ABI constraint handler with `EINVAL` and leaves the destination untouched, following [C11 K.3.7.4.2](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf); what the console does then, its `RSIZE_MAX` and its default handler are not measured. Otherwise they match the system `libSceLibcInternal` of PS5 firmware 13.60: the `strerror` texts, a short buffer truncated with "..." when it holds more than 3 bytes and a return of 0 either way, and `errno` left alone - [getw and putw](../../../core/libs/prx/libc/src/BinaryWordIo.cpp) (libc) follow FreeBSD 11.4 [getw.c](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libc/stdio/getw.c) and [putw.c](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libc/stdio/putw.c): one 32-bit little-endian word, `getw` returns -1 for EOF or an error, and `putw` returns 0 on success or -1 on error while holding the stream lock. They use the existing host-backed streams, so buffering and error timing follow the host CRT. Null streams throw and closed streams return -1 with `EBADF`, as existing byte I/O does. Console behavior is unverified. A payload process on a PS5 (firmware 13.60) gave the same results: two words written little-endian, -1 with end of file after them and for a 2-byte file, and -1 with the error flag for `putw` on a read-only stream - [vsnwprintf_s](../../../core/libs/prx/libc/src/FormattingWide.cpp) (libc) - `va_list` counterpart of `snwprintf_s` (C11 K.3.9.1.3): truncates to the buffer and returns the full length, with the same constraints and argument checks