Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions core/libs/prx/libSceAmpr/Export.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down
1 change: 1 addition & 0 deletions core/libs/prx/libc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
70 changes: 8 additions & 62 deletions core/libs/prx/libc/include/SonyCrt.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<typename TInteger>
inline TInteger ParseInteger(const char* text, char** end, int base, TInteger (*parse)(const char*, char**, int)) {
Expand All @@ -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<std::mutex> 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) {
Expand Down Expand Up @@ -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<std::mutex> lock(SonyConstraintMutex());
void* previous = reinterpret_cast<void*>(SonyConstraintHandlerSlot());
SonyConstraintHandlerSlot() = handler ? reinterpret_cast<SonyConstraintHandler>(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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -290,6 +238,4 @@ inline __int128 SonyModti3(__int128 dividend, __int128 divisor) {
return (__int128)(negative ? (unsigned __int128)0 - remainder : remainder);
}

}

#endif
10 changes: 8 additions & 2 deletions core/libs/prx/libc/src/FileAndHeap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
2 changes: 1 addition & 1 deletion core/libs/prx/libc/src/LocaleSupport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

}
76 changes: 76 additions & 0 deletions core/libs/prx/libc/src/SonyCrt.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#include "prx/libc/include/SonyCrt.hpp"

#include <climits>
#include <cstdlib>
#include <mutex>

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<std::mutex> lock(SonyConstraintMutex());
void* previous = reinterpret_cast<void*>(SonyConstraintHandlerSlot());
SonyConstraintHandlerSlot() = handler ? reinterpret_cast<SonyConstraintHandler>(handler) : SonyDefaultConstraintHandler;
return previous;
}

extern "C" void SonyInvokeConstraintHandler_nid_no_patch(const char* message, void* pointer, int error) {
SonyConstraintHandler handler = nullptr;
{
std::lock_guard<std::mutex> 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();
}
4 changes: 2 additions & 2 deletions core/libs/prx/libc/src/WideConversion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
#include <stdexcept>

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);
Expand Down
46 changes: 40 additions & 6 deletions core/libs/tests/GuestCrt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -66,15 +70,20 @@ 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<std::uintptr_t>(&locale->decimal_point) - reinterpret_cast<std::uintptr_t>(locale) == 0x48);
Require(reinterpret_cast<std::uintptr_t>(&amprLocale->decimal_point) - reinterpret_cast<std::uintptr_t>(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);
for (const char* text : locale->monetary) Require(text != nullptr && text[0] == '\0');
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);
Expand Down Expand Up @@ -159,23 +168,37 @@ int main() {
}
Require(trapped);

void* previous = sceAmprSetConstraintHandlerS(reinterpret_cast<void*>(&CountingHandler));
void* previous = set_constraint_handler_s_nid_postfix(reinterpret_cast<void*>(&CountingHandler));
Require(previous != nullptr);
Require(previous != reinterpret_cast<void*>(&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<void*>(&CountingHandler));
Require(previous == reinterpret_cast<void*>(&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<void*>(&CountingHandler));
previous = sceAmprSetConstraintHandlerS(reinterpret_cast<void*>(&CountingHandler));
previous = set_constraint_handler_s_nid_postfix(reinterpret_cast<void*>(&CountingHandler));
Require(previous != nullptr && previous != reinterpret_cast<void*>(&CountingHandler));
sceAmprIgnoreHandlerS("msg", nullptr, 0);

char text[64];
GuestTm known{};
Expand Down Expand Up @@ -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<void*>(&CountingHandler));
Require(defaultHandler != nullptr && defaultHandler != reinterpret_cast<void*>(&CountingHandler));
Require(reinterpret_cast<void*>(SonyConstraintHandlerSlot()) == reinterpret_cast<void*>(&CountingHandler));
Require(sceAmprSetConstraintHandlerS(nullptr) == reinterpret_cast<void*>(&CountingHandler));
Require(reinterpret_cast<void*>(SonyConstraintHandlerSlot()) == defaultHandler);
Require(set_constraint_handler_s_nid_postfix(reinterpret_cast<void*>(&CountingHandler)) == defaultHandler);
Require(set_constraint_handler_s_nid_postfix(nullptr) == reinterpret_cast<void*>(&CountingHandler));
Require(reinterpret_cast<void*>(SonyConstraintHandlerSlot()) == defaultHandler);
return 0;
}
3 changes: 3 additions & 0 deletions core/libs/tests/GuestFiopen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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*);
}
Expand Down Expand Up @@ -69,6 +71,7 @@ int main() {
Write(Open(file, Out), "abc");
Require(Contents(file) == "abc");

set_constraint_handler_s_nid_postfix(reinterpret_cast<void*>(&ignore_handler_s_nid_postfix));
FileStream* checkedStream = &_Stdout_nid_postfix;
Require(fopen_s_nid_postfix(&checkedStream, nullptr, "rb") == GuestEinval);
Require(checkedStream == nullptr);
Expand Down
4 changes: 2 additions & 2 deletions docs/dev/debt/libc.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading