mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-27 00:28:14 +00:00
fix(libc): give strerror_r and perror the strerror texts as measured on a PS5
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
cceb6c3c41
commit
b4530f1cce
6 files changed
+63
-46
No files matched your search
@@ -0,0 +1,8 @@
|
||||
#ifndef CORE_LIBS_PRX_LIBC_INCLUDE_ERRORMESSAGES_HPP
|
||||
#define CORE_LIBS_PRX_LIBC_INCLUDE_ERRORMESSAGES_HPP
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
extern "C" void FreeBsdErrorMessage_nid_no_patch(int error, char* buffer, std::size_t length);
|
||||
|
||||
#endif
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "prx/libc/include/WindowsFormatting.hpp"
|
||||
#include "prx/libc/include/Api.hpp"
|
||||
#include "prx/libc/include/ErrorMessages.hpp"
|
||||
#include "prx/libc/include/FileStream.hpp"
|
||||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include <cstdio>
|
||||
@@ -45,32 +46,37 @@ constexpr const char* messages[] = {
|
||||
static_assert(sizeof(messages) / sizeof(messages[0]) == 97);
|
||||
bool Known(int error) { return error >= 0 && error < 97; }
|
||||
bool NamedByStrerror(int error) { return error > 0 && error < 95; }
|
||||
|
||||
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 std::snprintf(text, sizeof(text), "error_code=0x%08x", static_cast<unsigned>(error));
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
void FreeBsdErrorMessage_nid_no_patch(int error, char* buffer, std::size_t length) {
|
||||
const int saved = *__error_nid_postfix();
|
||||
if (Known(error)) std::snprintf(buffer, length, "%s", messages[error]);
|
||||
else std::snprintf(buffer, length, "Unknown error: %d", error);
|
||||
*__error_nid_postfix() = saved;
|
||||
}
|
||||
|
||||
char* APS5_VABI strerror_nid_postfix(int error) {
|
||||
thread_local char buffer[128];
|
||||
if (error == 0) std::snprintf(buffer, sizeof(buffer), "No error");
|
||||
else if (NamedByStrerror(error)) std::snprintf(buffer, sizeof(buffer), "%s", messages[error]);
|
||||
else std::snprintf(buffer, sizeof(buffer), "error_code=0x%08x", static_cast<unsigned>(error));
|
||||
Describe(error, buffer);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
int APS5_VABI strerror_r_nid_postfix(int error, char* buffer, std::size_t length) {
|
||||
const int saved = *__error_nid_postfix();
|
||||
char temporary[128];
|
||||
if (Known(error)) std::snprintf(temporary, sizeof(temporary), "%s", messages[error]);
|
||||
else std::snprintf(temporary, sizeof(temporary), "Unknown error: %d", error);
|
||||
const auto required = std::strlen(temporary) + 1;
|
||||
int result = Known(error) ? 0 : 22;
|
||||
if (Known(error) && (!buffer || length < required)) result = 34;
|
||||
if (buffer && length) {
|
||||
const auto count = required <= length ? required - 1 : length - 1;
|
||||
std::memcpy(buffer, temporary, count);
|
||||
buffer[count] = '\0';
|
||||
}
|
||||
char text[128];
|
||||
Describe(error, text);
|
||||
*__error_nid_postfix() = saved;
|
||||
return result;
|
||||
const auto required = std::strlen(text) + 1;
|
||||
if (!buffer || length < required) return 34;
|
||||
std::memcpy(buffer, text, required);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void APS5_VABI perror_nid_postfix(const char* prefix) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "prx/libc/include/WindowsFormatting.hpp"
|
||||
#include "prx/libc/include/Api.hpp"
|
||||
#include "prx/libc/include/ErrorMessages.hpp"
|
||||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include "prx/libc/include/SonyCrt.hpp"
|
||||
#include <cerrno>
|
||||
@@ -10,7 +11,6 @@
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
|
||||
extern "C" void APS5_VABI ignore_handler_s_nid_postfix(const char* message, void* pointer, int error) {
|
||||
SonyIgnoreHandlerS(message, pointer, error);
|
||||
@@ -41,8 +41,7 @@ extern "C" int APS5_VABI wcscpy_s_nid_postfix(std::uint16_t* dest, std::size_t s
|
||||
namespace {
|
||||
|
||||
std::size_t ReadErrorMessage(int error, char (&message)[128]) {
|
||||
const int result = strerror_r_nid_postfix(error, message, sizeof(message));
|
||||
if (result != 0 && result != 22) throw std::runtime_error("strerror_s: error message exceeds internal buffer");
|
||||
FreeBsdErrorMessage_nid_no_patch(error, message, sizeof(message));
|
||||
return std::strlen(message);
|
||||
}
|
||||
|
||||
|
||||
@@ -58,40 +58,43 @@ int main() {
|
||||
Require(std::strcmp(parent, "Invalid argument") == 0);
|
||||
char buffer[128];
|
||||
for (int error = 0; error <= 96; ++error) {
|
||||
Require(strerror_r_nid_postfix(error, buffer, sizeof(buffer)) == 0);
|
||||
Require(std::strcmp(buffer, freebsd[error]) == 0);
|
||||
char coded[32];
|
||||
std::snprintf(coded, sizeof(coded), "error_code=0x%08x", static_cast<unsigned>(error));
|
||||
const char* expected = error == 0 ? "No error" : error < 95 ? freebsd[error] : coded;
|
||||
Require(strerror_r_nid_postfix(error, buffer, sizeof(buffer)) == 0);
|
||||
Require(std::strcmp(buffer, expected) == 0);
|
||||
Require(std::strcmp(strerror_nid_postfix(error), expected) == 0);
|
||||
Require(*__error_nid_postfix() == 13);
|
||||
}
|
||||
Require(strerror_r_nid_postfix(-1, buffer, sizeof(buffer)) == 22);
|
||||
Require(std::strcmp(buffer, "Unknown error: -1") == 0);
|
||||
Require(*__error_nid_postfix() == 13);
|
||||
struct UnknownError {
|
||||
int error;
|
||||
const char* text;
|
||||
const char* coded;
|
||||
};
|
||||
for (const UnknownError& unknown : {UnknownError{97, "Unknown error: 97", "error_code=0x00000061"},
|
||||
UnknownError{9999, "Unknown error: 9999", "error_code=0x0000270f"},
|
||||
UnknownError{INT_MAX, "Unknown error: 2147483647", "error_code=0x7fffffff"},
|
||||
UnknownError{-1, "Unknown error: -1", "error_code=0xffffffff"},
|
||||
UnknownError{INT_MIN, "Unknown error: -2147483648", "error_code=0x80000000"}}) {
|
||||
Require(strerror_r_nid_postfix(unknown.error, buffer, sizeof(buffer)) == 22);
|
||||
Require(std::strcmp(buffer, unknown.text) == 0);
|
||||
for (const int error : {97, 100, 1000, 9999, INT_MAX, -1, INT_MIN}) {
|
||||
char coded[32];
|
||||
std::snprintf(coded, sizeof(coded), "error_code=0x%08x", static_cast<unsigned>(error));
|
||||
Require(strerror_r_nid_postfix(error, buffer, sizeof(buffer)) == 0);
|
||||
Require(std::strcmp(buffer, coded) == 0);
|
||||
Require(*__error_nid_postfix() == 13);
|
||||
Require(std::strcmp(strerror_nid_postfix(unknown.error), unknown.coded) == 0);
|
||||
Require(std::strcmp(strerror_nid_postfix(error), coded) == 0);
|
||||
Require(*__error_nid_postfix() == 13);
|
||||
}
|
||||
char sentinel[] = "xyz";
|
||||
Require(strerror_r_nid_postfix(22, sentinel, 1) == 34);
|
||||
Require(sentinel[0] == 0 && sentinel[1] == 'y');
|
||||
sentinel[0] = 'x';
|
||||
Require(strerror_r_nid_postfix(22, sentinel, 0) == 34 && sentinel[0] == 'x');
|
||||
struct ShortBuffer {
|
||||
int error;
|
||||
const char* text;
|
||||
};
|
||||
for (const ShortBuffer& entry : {ShortBuffer{0, "No error"}, ShortBuffer{22, "Invalid argument"},
|
||||
ShortBuffer{95, "error_code=0x0000005f"}, ShortBuffer{-1, "error_code=0xffffffff"}}) {
|
||||
const std::size_t required = std::strlen(entry.text) + 1;
|
||||
for (const std::size_t length : {std::size_t{0}, std::size_t{1}, std::size_t{5}, required - 1}) {
|
||||
char canary[64];
|
||||
std::memset(canary, 0xcc, sizeof(canary));
|
||||
Require(strerror_r_nid_postfix(entry.error, canary, length) == 34);
|
||||
for (const char byte : canary) Require(static_cast<unsigned char>(byte) == 0xcc);
|
||||
Require(*__error_nid_postfix() == 13);
|
||||
}
|
||||
char exact[64];
|
||||
std::memset(exact, 0xcc, sizeof(exact));
|
||||
Require(strerror_r_nid_postfix(entry.error, exact, required) == 0);
|
||||
Require(std::strcmp(exact, entry.text) == 0);
|
||||
Require(static_cast<unsigned char>(exact[required]) == 0xcc);
|
||||
}
|
||||
Require(strerror_r_nid_postfix(22, nullptr, 0) == 34);
|
||||
char truncated[4];
|
||||
Require(strerror_r_nid_postfix(97, truncated, sizeof(truncated)) == 22 && std::strcmp(truncated, "Unk") == 0);
|
||||
Require(strerror_r_nid_postfix(-1, truncated, sizeof(truncated)) == 22 && std::strcmp(truncated, "Unk") == 0);
|
||||
Require(strerror_r_nid_postfix(22, nullptr, sizeof(buffer)) == 34);
|
||||
}
|
||||
@@ -60,12 +60,13 @@ void CheckMessages() {
|
||||
CheckMessage(2, "No such file or directory");
|
||||
CheckMessage(11, "Resource deadlock avoided");
|
||||
CheckMessage(35, "Resource temporarily unavailable");
|
||||
CheckMessage(95, "State not recoverable");
|
||||
CheckMessage(96, "Previous owner died");
|
||||
CheckMessage(-1, "Unknown error: -1");
|
||||
CheckMessage(97, "Unknown error: 97");
|
||||
CheckMessage(std::numeric_limits<int>::min(), "Unknown error: -2147483648");
|
||||
CheckMessage(std::numeric_limits<int>::max(), "Unknown error: 2147483647");
|
||||
for (int error = 0; error < 97; ++error) {
|
||||
for (int error = 1; error < 95; ++error) {
|
||||
char expected[128];
|
||||
Require(strerror_r_nid_postfix(error, expected, sizeof(expected)) == 0, "existing guest error table");
|
||||
CheckMessage(error, expected);
|
||||
|
||||
@@ -47,7 +47,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](../../../core/libs/prx/libc/src/Errors.cpp) (libc) - follows 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, any other number "error_code=0x%08x", and errno is left alone. The console's system libc, which homebrew without a bundled `libc.prx` uses, answers differently ("No error information" for 0 and unknown numbers, 95 and 96 named). `strerror_r` and `perror` keep "No error: 0" and "Unknown error: %d"; they are not measured.
|
||||
- [strerror, strerror_r and perror](../../../core/libs/prx/libc/src/Errors.cpp) (libc) - a null `strerror_r` buffer returns `ERANGE` (34) without writing; what the console does with one is not measured. Otherwise they 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"; `strerror_r` returns 0 for every number, and 34 without writing anything when the buffer is shorter than the text and its terminator; `perror` prints the same texts; errno is left alone.
|
||||
- [strerror_s and strerrorlen_s](../../../core/libs/prx/libc/src/WideConversion.cpp) (libc) follow [C11 K.3.7.4.2 and K.3.7.4.3](https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf), using the existing FreeBSD error messages and unknown-error formatting. Truncation writes an ellipsis when capacity exceeds 3; constraint failures leave the destination untouched and invoke the registered guest-ABI handler. The console's `RSIZE_MAX` (`SIZE_MAX >> 1` here), nonzero returns (`EINVAL` 22 for constraints, `ERANGE` 34 for truncation), handler arguments and default abort behavior are unverified. Messages remain in the C locale, and guest errno is preserved.
|
||||
- [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
|
||||
|
||||
Reference in new issue
Block a user