From 433bf1ebc2debf27cee3ba0f56a1c531cdb4cb52 Mon Sep 17 00:00:00 2001 From: Yunus Shaikh <75296019+yunusshaikh7@users.noreply.github.com> Date: Sat, 10 Oct 2026 01:07:23 -0700 Subject: [PATCH] fix(libc): map _Towctrans descriptors 1 and 2 to lower and upper case _Towctrans passed the guest's descriptor to the host towctrans. Dinkumware's (xwcc.h) defines towlower(wc) as _Towctrans(wc, 1) and towupper(wc) as _Towctrans(wc, 2), the values FreeBSD's wctrans.c also uses for "tolower" and "toupper". UCRT reads 1 as toupper and other nonzero values as tolower, so on Windows towlower(L'A') returned L'A' and towupper(L'a') returned L'a', while towlower(L'a') returned L'A'. The host wint_t is 16 bits there, which also turned WEOF into 0xffff. glibc's wctrans_t is a pointer to a table that towctrans reads, so on Linux descriptor 1 or 2 is dereferenced as an address. _Towctrans now takes the guest's 32-bit wint_t and size_t descriptor. 1 and 2 map 0..255 through the C locale tables behind _Getptolower and _Getptoupper and return other values unchanged. Any other descriptor sets errno to EINVAL and returns wc unchanged, as FreeBSD's towctrans_l does. --- core/libs/prx/libc/src/LocaleSupport.cpp | 13 +++++++++--- core/libs/tests/GuestLocale.cpp | 26 ++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 3 deletions(-) diff --git a/core/libs/prx/libc/src/LocaleSupport.cpp b/core/libs/prx/libc/src/LocaleSupport.cpp index d0a70841a..a21ce8227 100644 --- a/core/libs/prx/libc/src/LocaleSupport.cpp +++ b/core/libs/prx/libc/src/LocaleSupport.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include #include #include @@ -323,8 +323,15 @@ mbstate_t* APS5_VABI _Getpwcstate_nid_postfix() { return &state; } -wint_t APS5_VABI _Towctrans_nid_postfix(wint_t c, wctrans_t desc) { - return std::towctrans(c, desc); +std::uint32_t APS5_VABI _Towctrans_nid_postfix(std::uint32_t c, std::uint64_t desc) { + constexpr std::uint64_t ToLower = 1; + constexpr std::uint64_t ToUpper = 2; + if (desc != ToLower && desc != ToUpper) { + errno = 22; + return c; + } + if (c > UCHAR_MAX) return c; + return static_cast((desc == ToLower ? g_lowerTable : g_upperTable)[c + 1]); } // Locale ids and stream objects the title imports besides the ones above; the streams are zeroed diff --git a/core/libs/tests/GuestLocale.cpp b/core/libs/tests/GuestLocale.cpp index 632670181..93cf43ff1 100644 --- a/core/libs/tests/GuestLocale.cpp +++ b/core/libs/tests/GuestLocale.cpp @@ -1,5 +1,6 @@ #include "prx/libc/include/GuestLocale.hpp" #include +#include #include #include #include @@ -23,6 +24,8 @@ int APS5_VABI _Mbtowcx_nid_postfix(std::uint16_t* dst, const char* src, std::siz int APS5_VABI _Wctombx_nid_postfix(char* dst, std::uint16_t src, std::mbstate_t* st); void APS5_VABI _Locksyslock_nid_postfix(); void APS5_VABI _Unlocksyslock_nid_postfix(); +std::uint32_t APS5_VABI _Towctrans_nid_postfix(std::uint32_t wc, std::uint64_t desc); +int* APS5_VABI __error_nid_postfix(); } @@ -144,12 +147,35 @@ static void CheckStreamDestruction() { Require(object.stream.locale == &_ZSt21_sceLibcClassicLocale_nid_postfix); } +static void CheckWideCaseTranslation() { + const auto* lower = _Getptolower_nid_postfix(); + const auto* upper = _Getptoupper_nid_postfix(); + Require(_Towctrans_nid_postfix(u'A', 1) == u'a' && _Towctrans_nid_postfix(u'a', 2) == u'A'); + *__error_nid_postfix() = 0; + for (std::uint32_t value = 0; value < 256; ++value) { + Require(_Towctrans_nid_postfix(value, 1) == static_cast(lower[value])); + Require(_Towctrans_nid_postfix(value, 2) == static_cast(upper[value])); + } + const std::uint32_t uncased[] = {0x100u, 0x391u, 0x3b1u, 0xffffu, 0xffffffffu}; + for (const std::uint32_t value : uncased) { + Require(_Towctrans_nid_postfix(value, 1) == value && _Towctrans_nid_postfix(value, 2) == value); + } + Require(*__error_nid_postfix() == 0); + const std::uint64_t invalidDescriptors[] = {0, 3}; + for (const std::uint64_t descriptor : invalidDescriptors) { + *__error_nid_postfix() = 0; + Require(_Towctrans_nid_postfix(u'A', descriptor) == u'A' && _Towctrans_nid_postfix(u'7', descriptor) == u'7'); + Require(*__error_nid_postfix() == 22); + } +} + int main() { CheckGuestCalls(); CheckLocinfoAlignment(); CheckCharacterTables(); CheckCharacterConversions(); CheckStreamDestruction(); + CheckWideCaseTranslation(); _Locksyslock_nid_postfix(); _Locksyslock_nid_postfix(); _Unlocksyslock_nid_postfix();