Skip to content
Closed
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
13 changes: 10 additions & 3 deletions core/libs/prx/libc/include/WindowsFormatting.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -306,14 +306,19 @@ inline int FormatWindows(char* buffer, size_t size, const char* format, const vo
}
}
std::string length;
if (*format && std::strchr("hljztL", *format)) {
if (*format && std::strchr("hljztLq", *format)) {
length += *format++;
if ((length == "h" && *format == 'h') || (length == "l" && *format == 'l'))
length += *format++;
}
const char conversion = *format;
if (!conversion) throw std::invalid_argument("Incomplete format conversion");
if (length == "q") length = "ll";
char conversion = *format;
if (!conversion) break;
++format;
if (conversion == 'D' || conversion == 'O' || conversion == 'U') {
if (length != "j" && length != "z" && length != "t" && length != "ll") length = "l";
conversion = static_cast<char>(conversion + 'a' - 'A');
}
const bool integerLength = length.empty() || length == "h" || length == "hh" ||
length == "l" || length == "ll" || length == "j" || length == "z" || length == "t";
if (conversion == 'd' || conversion == 'i') {
Expand Down Expand Up @@ -391,6 +396,8 @@ inline int FormatWindows(char* buffer, size_t size, const char* format, const vo
else if (length == "h") *static_cast<short*>(pointer) = static_cast<short>(count);
else if (length.empty()) *static_cast<int*>(pointer) = count;
else *static_cast<long long*>(pointer) = count;
} else if (!std::strchr("diouxXaAeEfFgGcCsSpn", conversion)) {
output.Value(spec + 'c', static_cast<int>(static_cast<unsigned char>(conversion)));
} else {
throw std::invalid_argument("Unsupported format conversion '" + spec + length + conversion + "' in \"" + std::string(formatStart).substr(0, 160) + "\"");
}
Expand Down
10 changes: 10 additions & 0 deletions core/libs/tests/Formatting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,16 @@ __attribute__((noinline)) static void APS5_VABI RunChecks() {
address(0x1a), 6, address(0xf), -6, address(0xf)) == 34 &&
std::strcmp(pointers, "[0x1a][0x1a][0x1a][ 0xf][0xf ]") == 0);
Require(snprintf_nid_postfix(pointers, 6, "%p", address(0xabcdef)) == 8 && std::strcmp(pointers, "0xabc") == 0);
char freebsd[64];
Require(snprintf_nid_postfix(freebsd, sizeof(freebsd), "%qd|%qu|%qx|%qo", -5000000000LL, 5000000000ULL, 255ULL, 8ULL) == 28 &&
std::strcmp(freebsd, "-5000000000|5000000000|ff|10") == 0);
Require(snprintf_nid_postfix(freebsd, sizeof(freebsd), "%D|%hD|%O|%U|%jD", -5000000000L, 5000000000L, 8L, 5000000000UL, -6000000000LL) == 48 &&
std::strcmp(freebsd, "-5000000000|5000000000|10|5000000000|-6000000000") == 0);
Require(snprintf_nid_postfix(freebsd, sizeof(freebsd), "[%5%][%-5%][%y][%5y][%-3y]") == 29 &&
std::strcmp(freebsd, "[ %][% ][y][ y][y ]") == 0);
Require(snprintf_nid_postfix(freebsd, sizeof(freebsd), "100%") == 3 && std::strcmp(freebsd, "100") == 0);
Require(snprintf_nid_postfix(freebsd, sizeof(freebsd), "%d done 5% ", 7) == 8 && std::strcmp(freebsd, "7 done 5") == 0);
Require(snprintf_nid_postfix(freebsd, 4, "ab%dcd%", 12) == 6 && std::strcmp(freebsd, "ab1") == 0);
#endif
snprintf_nid_postfix(buffer, sizeof(buffer), "%.*s", -1, "unlimited");
Require(std::strcmp(buffer, "unlimited") == 0);
Expand Down
1 change: 1 addition & 0 deletions docs/dev/debt/libc.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,4 @@
- [flock](../../../core/libs/prx/libc/src/FileLock.cpp) (libkernel) on Windows shares one lock state between duplicated descriptors, following FreeBSD 11.4's [flock(2)](https://github.com/freebsd/freebsd-src/blob/releng/11.4/lib/libc/sys/flock.2). Independent opens have separate states; closing a descriptor or reopening a stream drops only its reference. Lock transitions on one open file description are serialized. The host's [LockFileEx](https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-lockfileex) locks also restrict ordinary reads and writes through other handles, unlike FreeBSD's advisory locks, and do not restrict mapped views. Console behavior, signal interruption and concurrent closure of a descriptor while its lock call is pending are unverified.
- [std::_Throw_Cpp_error](../../../core/libs/prx/libc/src/exception/StandardExceptions.cpp) (libSceAmpr) follows Dinkumware's `thread0.cpp` as shipped with [Visual C++ 2013](https://github.com/ganboing/crt.vc12/blob/f1d47126bf13d1c9dccaa6c53b72217fd0474968/src/thread0.cpp) and in [microsoft/STL](https://github.com/microsoft/STL/blob/da52dc0fcadbd83690cfd1ed32408d2847439493/stl/src/thread0.cpp), without console verification. `_Cpp_error` codes 0 to 6 throw a guest `std::system_error` whose `error_code` holds the FreeBSD errno of the `errc` value (EBUSY 16, EINVAL 22, ESRCH 3, ENOMEM 12, EPERM 1, EDEADLK 11, EAGAIN 35) and the generic category, and whose `what()` is the table message, `": "` and the generic category's message for that errno, selected by the first matching errno entry in VC12's [`syserror.cpp`](https://github.com/ganboing/crt.vc12/blob/f1d47126bf13d1c9dccaa6c53b72217fd0474968/src/syserror.cpp); another code throws `std::out_of_range` on the host instead of reading past the table. The guest `system_error` is taken to be [VC12's](https://github.com/ganboing/crt.vc12/blob/f1d47126bf13d1c9dccaa6c53b72217fd0474968/src/system_error): the 16-byte `runtime_error` followed by `error_code {int, const error_category*}`, 32 bytes, so its copy constructor and assignment copy the code too. The generic category is a static libc object with VC12's `error_category` virtual order (destructors, `name`, `message`, `default_error_condition`, the two `equivalent`), not the console's `_Error_objects<int>::_Generic_object`, and the new `generic_category()` export returns that object; a title's inline category object in a bundled libc can still have a different identity. Its `message` and direct message export construct the existing [GuestLocale::String](../../../core/libs/prx/libc/include/GuestLocale.hpp) model: a reserved eight-byte word, 16-byte inline buffer, length and capacity, with guest `operator new` storage above 15 bytes. This 40-byte string layout and virtual return ABI have not been verified against a console library. The deleting destructor still throws. `_Syserror_map` covers the 72 distinct Dinkumware `errc` values present in FreeBSD 11.4 [errno.h](https://github.com/freebsd/freebsd-src/blob/releng/11.4/sys/sys/errno.h), returning null for other values; category messages use `unknown error` for those. First-match aliases are `EAGAIN`/`EWOULDBLOCK` -> `operation would block` and `ENOTSUP`/`EOPNOTSUPP` -> `not supported`. STREAMS-only errno assignments absent from that header (`ENODATA`, `ENOSR`, `ENOSTR`, `ETIME`) are not inferred
- [Call tracing](../../../core/libs/prx/libc/src/specifics/windows/CallTrace.cpp) (`--trace-imports`, `APS5_TRACE_CALLS`) exists on Windows only and records the entry of a call, not its arguments or its result. One process-wide ring keeps the last 65536 calls, so a thread that calls often pushes the others out. Calls are also counted per traced target since start-up; an import bound to a target already traced under another name is counted under the first name. The thunk saves the integer argument registers and `xmm0` to `xmm7`; arguments in wider vector registers pass only as long as the recording code stays free of AVX instructions, which is not tested
- [printf family conversions](../../../core/libs/prx/libc/include/WindowsFormatting.hpp) (libc) - on Windows the `q` length modifier, the `%D`, `%O` and `%U` conversions, an unknown conversion character (printed as itself with the width and `-` flag, as `%c` would) and a `%` that ends the format (output stops there) follow FreeBSD 11.4 [vfprintf.c](https://github.com/freebsd/freebsd-src/blob/release/11.4.0/lib/libc/stdio/vfprintf.c). The console's `libc.prx` is assumed to take them from the same code, and has not been checked on a title. The `0` flag on an unknown conversion is handled by the host `%c`, which pads with blanks where FreeBSD pads with zeroes. Linux formats these through glibc, which prints `%D`, `%O`, `%U` and unknown conversions as written.
Loading