feat(libc): implement swscanf

swscanf (HNnWdT43ues) runs the swscanf_s scanner without the buffer
size arguments after %s, %c and %[, as FreeBSD's swscanf.c passes its
arguments to vswscanf. Both entry points share one engine with a flag
for the sizes; swscanf_s behaves as before.

Title: PS5CEMU-HAR PPSA99360 imports swscanf from libSceLibcInternal.prx
(import audit with --trap-sdk-syscall-stubs, registry index 292, right
after pthread_setaffinity_np at 276). Predicted stop line once
pthread_setaffinity_np resolves, without this change:
FAIL: unresolved ELF import HNnWdT43ues (swscanf) from libSceLibcInternal.prx

NID from tools/nid_names.py, confirmed in shadPS4 aerolib.inl:51950
(e0a466753a1a). Prototype in ps5-payload-dev include/freebsd/wchar.h:133,
export in sce_stubs/libSceLibcInternal.c:8582 (f5d40f02770e).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
screamm 2026-10-12 14:18:58 +02:00 committed by Dean Galvin
parent bf8dc610e6
commit 8aa0d42824
3 changed files with 113 additions and 42 deletions

View File

@ -13,6 +13,14 @@
#include "prx/libc/include/General.hpp"
#include "prx/libc/include/WindowsFormatting.hpp"
#ifdef _WIN32
#define APS5_VA_BEGIN(last) __builtin_sysv_va_list args; __builtin_sysv_va_start(args, last)
#define APS5_VA_END() __builtin_sysv_va_end(args)
#else
#define APS5_VA_BEGIN(last) std::va_list args; va_start(args, last)
#define APS5_VA_END() va_end(args)
#endif
extern "C" int APS5_VABI iswspace_nid_postfix(std::uint32_t c);
extern "C" std::size_t APS5_VABI wcrtomb_nid_postfix(char* destination, std::uint16_t value, void* state);
@ -122,15 +130,9 @@ Fit FloatFit(std::string_view text) {
return Fit::Valid;
}
std::size_t IntegerSize(const std::string& length) {
if (length.empty()) return sizeof(std::int32_t);
if (length == "hh") return sizeof(std::int8_t);
if (length == "h") return sizeof(std::int16_t);
if (length == "l" || length == "ll" || length == "j" || length == "z" || length == "t") return sizeof(std::int64_t);
throw std::invalid_argument("swscanf_s: invalid integer length modifier " + length);
}
class WideScanner {
const char* const name;
const bool sized;
const char16_t* const start;
const char16_t* input;
const char16_t* format;
@ -139,8 +141,8 @@ class WideScanner {
bool converted = false;
public:
WideScanner(const char16_t* buffer, const char16_t* formatText, const void* source)
: start(buffer), input(buffer), format(formatText), args(source) {}
WideScanner(const char* function, bool withSizes, const char16_t* buffer, const char16_t* formatText, const void* source)
: name(function), sized(withSizes), start(buffer), input(buffer), format(formatText), args(source) {}
int Run() {
while (*format != 0) {
@ -162,6 +164,18 @@ public:
}
private:
[[noreturn]] void Fail(const std::string& message) const {
throw std::invalid_argument(std::string(name) + ": " + message);
}
std::size_t IntegerSize(const std::string& length) const {
if (length.empty()) return sizeof(std::int32_t);
if (length == "hh") return sizeof(std::int8_t);
if (length == "h") return sizeof(std::int16_t);
if (length == "l" || length == "ll" || length == "j" || length == "z" || length == "t") return sizeof(std::int64_t);
Fail("invalid integer length modifier " + length);
}
void SkipSpace() {
while (*input != 0 && IsSpace(*input)) ++input;
}
@ -185,7 +199,7 @@ private:
const std::size_t limit = (std::numeric_limits<std::size_t>::max() - digit) / 10;
directive.width = directive.width > limit ? std::numeric_limits<std::size_t>::max() : directive.width * 10 + digit;
}
if (format != digits && directive.width == 0) throw std::invalid_argument("swscanf_s: zero field width");
if (format != digits && directive.width == 0) Fail("zero field width");
if (*format == u'h' || *format == u'l') {
directive.length += static_cast<char>(*format++);
if (*format == directive.length[0]) directive.length += static_cast<char>(*format++);
@ -193,7 +207,7 @@ private:
directive.length += static_cast<char>(*format++);
}
directive.conversion = *format;
if (directive.conversion == 0) throw std::invalid_argument("swscanf_s: incomplete conversion specification");
if (directive.conversion == 0) Fail("incomplete conversion specification");
++format;
if (directive.conversion == u'[') {
directive.setExcluded = *format == u'^';
@ -201,10 +215,10 @@ private:
directive.setBegin = format;
if (*format == u']') ++format;
while (*format != 0 && *format != u']') ++format;
if (*format == 0) throw std::invalid_argument("swscanf_s: unterminated scanset");
if (*format == 0) Fail("unterminated scanset");
directive.setEnd = format++;
for (const char16_t* member = directive.setBegin + 1; member + 1 < directive.setEnd; ++member) {
if (*member == u'-') throw std::invalid_argument("swscanf_s: scanset range is implementation-defined");
if (*member == u'-') Fail("scanset range is implementation-defined");
}
}
return directive;
@ -230,7 +244,7 @@ private:
case u'c':
case u'[': return Text(directive);
case u'n': return Count(directive);
default: throw std::invalid_argument("swscanf_s: unsupported conversion " + std::to_string(directive.conversion));
default: Fail("unsupported conversion " + std::to_string(directive.conversion));
}
}
@ -271,7 +285,7 @@ private:
Outcome Float(const Directive& directive) {
static_assert(sizeof(long double) == 16 && std::numeric_limits<long double>::digits == 64);
if (!directive.length.empty() && directive.length != "l" && directive.length != "L")
throw std::invalid_argument("swscanf_s: invalid floating length modifier " + directive.length);
Fail("invalid floating length modifier " + directive.length);
void* target = nullptr;
if (!directive.suppress && (target = args.Next<void*>()) == nullptr) return Outcome::Violation;
SkipSpace();
@ -312,12 +326,12 @@ private:
Outcome Text(const Directive& directive) {
if (!directive.length.empty() && directive.length != "l")
throw std::invalid_argument("swscanf_s: invalid string length modifier " + directive.length);
Fail("invalid string length modifier " + directive.length);
void* target = nullptr;
std::size_t capacity = 0;
if (!directive.suppress) {
target = args.Next<void*>();
capacity = args.Next<unsigned int>();
if (sized) capacity = args.Next<unsigned int>();
if (target == nullptr) return Outcome::Violation;
}
if (directive.conversion == u's') SkipSpace();
@ -343,7 +357,7 @@ private:
}
const std::size_t terminator = directive.conversion == u'c' ? 0 : 1;
if (directive.length == "l") {
if (item.size() + terminator > capacity) return Truncated(target, capacity, true);
if (sized && item.size() + terminator > capacity) return Truncated(target, capacity, true);
std::memcpy(target, item.data(), item.size() * sizeof(char16_t));
if (terminator != 0) static_cast<char16_t*>(target)[item.size()] = 0;
} else {
@ -351,10 +365,10 @@ private:
for (const char16_t unit : item) {
char byte[8];
const std::size_t written = wcrtomb_nid_postfix(byte, unit, nullptr);
if (written == static_cast<std::size_t>(-1)) throw std::invalid_argument("swscanf_s: wide character has no single-byte form");
if (written == static_cast<std::size_t>(-1)) Fail("wide character has no single-byte form");
bytes.append(byte, written);
}
if (bytes.size() + terminator > capacity) return Truncated(target, capacity, false);
if (sized && bytes.size() + terminator > capacity) return Truncated(target, capacity, false);
std::memcpy(target, bytes.data(), bytes.size());
if (terminator != 0) static_cast<char*>(target)[bytes.size()] = 0;
}
@ -364,7 +378,7 @@ private:
}
Outcome Count(const Directive& directive) {
if (directive.suppress || directive.width != 0) throw std::invalid_argument("swscanf_s: %n with suppression or width");
if (directive.suppress || directive.width != 0) Fail("%n with suppression or width");
const std::size_t size = IntegerSize(directive.length);
void* target = args.Next<void*>();
if (target == nullptr) return Outcome::Violation;
@ -378,29 +392,28 @@ private:
extern "C" {
int APS5_VABI swscanf_s_nid_postfix(const char16_t* buffer, const char16_t* format, ...) {
int APS5_VABI swscanf_nid_postfix(const char16_t* buffer, const char16_t* format, ...) {
if (buffer == nullptr || format == nullptr) return EOF;
#ifdef _WIN32
__builtin_sysv_va_list args;
__builtin_sysv_va_start(args, format);
#else
std::va_list args;
va_start(args, format);
#endif
APS5_VA_BEGIN(format);
try {
const int result = WideScanner(buffer, format, args).Run();
#ifdef _WIN32
__builtin_sysv_va_end(args);
#else
va_end(args);
#endif
const int result = WideScanner("swscanf", false, buffer, format, args).Run();
APS5_VA_END();
return result;
} catch (...) {
#ifdef _WIN32
__builtin_sysv_va_end(args);
#else
va_end(args);
#endif
APS5_VA_END();
throw;
}
}
int APS5_VABI swscanf_s_nid_postfix(const char16_t* buffer, const char16_t* format, ...) {
if (buffer == nullptr || format == nullptr) return EOF;
APS5_VA_BEGIN(format);
try {
const int result = WideScanner("swscanf_s", true, buffer, format, args).Run();
APS5_VA_END();
return result;
} catch (...) {
APS5_VA_END();
throw;
}
}

View File

@ -11,6 +11,7 @@
extern "C" {
std::uint64_t APS5_VABI _WStoul_nid_postfix(const char16_t*, char16_t**, int);
int APS5_VABI _Iswctype_nid_postfix(std::uint32_t, std::uint64_t);
int APS5_VABI swscanf_nid_postfix(const char16_t*, const char16_t*, ...);
int APS5_VABI swscanf_s_nid_postfix(const char16_t*, const char16_t*, ...);
int APS5_VABI iswalnum_nid_postfix(std::uint32_t);
int APS5_VABI iswalpha_nid_postfix(std::uint32_t);
@ -155,9 +156,66 @@ static void CheckScan() {
Require(Throws([&] { swscanf_s_nid_postfix(u"Ā", u"%s", narrow, std::size_t{4}); }), "unconvertible narrow %s throws");
}
static void CheckUnsizedScan() {
int first = 0;
int second = 0;
Require(swscanf_nid_postfix(u" 42 -7", u"%d%d", &first, &second) == 2 && first == 42 && second == -7, "swscanf %d");
Require(swscanf_nid_postfix(u"x=0x1f", u"x=%i", &first) == 1 && first == 31, "swscanf %i with a hex prefix");
Require(swscanf_nid_postfix(u"1 2", u"%*d%d", &first) == 1 && first == 2, "swscanf suppressed %d");
Require(swscanf_nid_postfix(u"12345", u"%3d%d", &first, &second) == 2 && first == 123 && second == 45, "swscanf %d width");
std::int64_t wide = 0;
Require(swscanf_nid_postfix(u"5000000000", u"%ld", &wide) == 1 && wide == 5000000000LL, "swscanf %ld is 64-bit");
short half = 0;
Require(swscanf_nid_postfix(u"-2", u"%hd", &half) == 1 && half == -2, "swscanf %hd");
unsigned value = 0;
Require(swscanf_nid_postfix(u"0xg", u"%x", &value) == 0, "swscanf %x prefix without digits is a matching failure");
char16_t text[16] = {u'?'};
Require(swscanf_nid_postfix(u"hello world", u"%ls", text) == 1 && std::u16string(text) == u"hello", "swscanf %ls");
Require(swscanf_nid_postfix(u"7 a-long-word", u"%d%ls", &first, text) == 2 && first == 7 && std::u16string(text) == u"a-long-word", "swscanf %ls takes no size");
Require(swscanf_nid_postfix(u"hello world", u"%3ls", text) == 1 && std::u16string(text) == u"hel", "swscanf %ls width");
char narrow[8] = {};
Require(swscanf_nid_postfix(u"abc def", u"%s%d", narrow, &first) == 1 && std::strcmp(narrow, "abc") == 0, "swscanf %s");
char16_t pair[3] = {u'?', u'?', u'?'};
Require(swscanf_nid_postfix(u"xyz", u"%2lc", pair) == 1 && pair[0] == u'x' && pair[1] == u'y' && pair[2] == u'?', "swscanf %2lc");
Require(swscanf_nid_postfix(u"x", u"%2lc", pair) == 0, "swscanf %2lc short input");
char letter = 0;
Require(swscanf_nid_postfix(u" q7", u"%c%d", &letter, &first) == 1 && letter == ' ', "swscanf %c does not skip white space");
Require(swscanf_nid_postfix(u"cab12", u"%l[abc]%d", text, &first) == 2 && std::u16string(text) == u"cab" && first == 12, "swscanf %l[");
Require(swscanf_nid_postfix(u"été!", u"%l[^!]", text) == 1 && std::u16string(text) == u"été", "swscanf %l[^");
Require(swscanf_nid_postfix(u"key=val", u"%[^=]=%s", narrow, narrow + 4) == 2 && std::strcmp(narrow, "key") == 0 && std::strcmp(narrow + 4, "val") == 0, "swscanf %[^");
std::int64_t count = -1;
Require(swscanf_nid_postfix(u" 12 ab", u"%d %ln", &first, &count) == 1 && count == 4, "swscanf %ln");
int shortCount = -1;
Require(swscanf_nid_postfix(u"abc", u"%*ls%n", &shortCount) == 0 && shortCount == 3, "swscanf %n after a suppressed conversion");
Require(swscanf_nid_postfix(u"50% off", u"%d%% off", &first) == 1 && first == 50, "swscanf %%");
Require(swscanf_nid_postfix(u"50 off", u"%d%%", &first) == 1, "swscanf %% mismatch is a matching failure");
double real = 0;
Require(swscanf_nid_postfix(u"1.5e3", u"%lf", &real) == 1 && real == 1500.0, "swscanf %lf");
Require(swscanf_nid_postfix(u"1e+x", u"%lf", &real) == 0, "swscanf %lf incomplete exponent is a matching failure");
long double extended = 0;
Require(swscanf_nid_postfix(u"2.25", u"%Lf", &extended) == 1 && extended == 2.25L, "swscanf %Lf");
float single = 0;
Require(swscanf_nid_postfix(u"-inf", u"%f", &single) == 1 && single == -std::numeric_limits<float>::infinity(), "swscanf %f infinity");
Require(swscanf_nid_postfix(u"a", u"b%d", &first) == 0, "swscanf literal mismatch is a matching failure");
Require(swscanf_nid_postfix(u"3 x", u"%d%d", &first, &second) == 1 && first == 3, "swscanf partial match");
Require(swscanf_nid_postfix(u"", u"%d", &first) == -1, "swscanf empty input is an input failure");
Require(swscanf_nid_postfix(u"", u"%ls", text) == -1, "swscanf empty input before %ls");
Require(swscanf_nid_postfix(u" ", u"%ls", text) == -1, "swscanf white space only is an input failure");
Require(swscanf_nid_postfix(u"5", u"%d%d", &first, &second) == 1, "swscanf input failure after a conversion");
Require(swscanf_nid_postfix(nullptr, u"%d", &first) == -1, "swscanf null buffer");
Require(swscanf_nid_postfix(u"5", nullptr) == -1, "swscanf null format");
Require(Throws([&] { swscanf_nid_postfix(u"abc", u"%l[a-c]", text); }), "swscanf scanset range throws");
Require(Throws([&] { swscanf_nid_postfix(u"5", u"%Ld", &wide); }), "swscanf %Ld throws");
Require(Throws([&] { swscanf_nid_postfix(u"Ā", u"%s", narrow); }), "swscanf unconvertible narrow %s throws");
}
int main() {
CheckStoul();
CheckClassification();
CheckScan();
CheckUnsizedScan();
return failures == 0 ? 0 : 1;
}

View File

@ -62,7 +62,7 @@
- [syslog](../../../core/libs/prx/libc/src/Syslog.cpp) (libc) writes each message to `stderr` as `[syslog:<priority>] <message>`, with a newline added when missing, instead of sending it to the console's system log. Priority and facility handling follow FreeBSD's `vsyslog`: `%m` expands to the guest errno's message, unknown bits are reported (as priority 0x23, since FreeBSD passes `LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID` as the priority) and masked, a missing facility becomes `LOG_USER` or the facility set by `openlog`, and errno is preserved. `openlog` applies its identity and `LOG_PID`; a null identity retains the previous one, and a valid nonzero facility replaces the default. `closelog` clears the identity but retains the options and facility, as in FreeBSD 11; when no identity is set, FreeBSD falls back to the program name, which this backend does not emit. `LOG_PERROR` has no extra effect because stderr is the emulated log sink; `LOG_NDELAY`/`LOG_ODELAY`, `LOG_NOWAIT` and `LOG_CONS` have no daemon, child or console backend to control. `setlogmask` follows [FreeBSD 11](https://github.com/freebsd/freebsd-src/blob/8d2708de5d6112a492744d367cfdfb8fd219227a/lib/libc/gen/syslog.c): the process-wide mask defaults to 0xff, zero queries it, and nonzero values replace it; masked messages are skipped before formatting, including the unknown-priority diagnostic. The console's mask behaviour is unverified
- [daemon](../../../core/libs/prx/libc/src/Process.cpp) (libc) throws: forking into the background is not supported. PPSA85300 imports it and fails to load without it. VLC 3.0 calls it only when its `daemon` option is set ([libvlc.c](https://github.com/videolan/vlc/blob/6de05adcbaf2e8b85fe86aad4169393098628119/src/libvlc.c#L190-L197)), and the title starts libVLC with `--ignore-config` and without `--daemon` ([player.cc](https://github.com/ZiZc3/VLC/blob/f3253020a64368153233ede29183014c0b6c8979/src/player.cc#L960-L981))
- [Call tracing](../../../core/libs/prx/libc/src/specifics/windows/CallTrace.cpp) (`APS5_TRACE_CALLS`) exists on Windows only and records the entry of a call, not its arguments or its result. Nothing passes imports through `TraceImport_nid_no_patch` yet, so no title is traced. 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
- [_WStoul](../../../core/libs/prx/libc/src/Strings.cpp), [_Iswctype](../../../core/libs/prx/libc/src/RuneLocale.cpp) and [swscanf_s](../../../core/libs/prx/libc/src/ScanningWide.cpp) (libc) - `_WStoul` is `wcstoul` and throws for a base other than 0 or 2 to 36. `_Iswctype` takes the class numbers of Dinkumware's [xwcc.h](https://github.com/superna9999/ti-cgt6x/blob/cgt6x_7_3_4/include/xwcc.h) (1 `alnum` to 11 `xdigit`, 12 `blank`) and throws for any other value, including the FreeBSD masks `wctype` returns; the numbering is not confirmed on the console's `libc.prx`. `swscanf_s` follows C11 7.29.2.2 and K.3.9.1.5, but reads the size argument as `unsigned int` like `sscanf_s` where K.3.9.1.5 specifies `rsize_t`; the console's width is unverified. A runtime-constraint violation (null buffer, format or receiving pointer) only returns `EOF`, and a too small array gets a null first element as in `sscanf_s`. A literal mismatch is a matching failure and a field that is only a prefix, such as `0x` or `1e`, does not convert, as C11 requires; FreeBSD's `vfwscanf` reports an input failure for the first and converts the second. Integers out of range are truncated from `strtoll`/`strtoull`. It throws for `%p`, `%S`, `%C`, unknown length modifiers, scanset ranges, `%n` with suppression or width, and narrow `%s`, `%c` or `%[` input that the C-locale `wcrtomb` cannot convert
- [_WStoul](../../../core/libs/prx/libc/src/Strings.cpp), [_Iswctype](../../../core/libs/prx/libc/src/RuneLocale.cpp), [swscanf_s and swscanf](../../../core/libs/prx/libc/src/ScanningWide.cpp) (libc) - `_WStoul` is `wcstoul` and throws for a base other than 0 or 2 to 36. `_Iswctype` takes the class numbers of Dinkumware's [xwcc.h](https://github.com/superna9999/ti-cgt6x/blob/cgt6x_7_3_4/include/xwcc.h) (1 `alnum` to 11 `xdigit`, 12 `blank`) and throws for any other value, including the FreeBSD masks `wctype` returns; the numbering is not confirmed on the console's `libc.prx`. `swscanf_s` follows C11 7.29.2.2 and K.3.9.1.5, but reads the size argument as `unsigned int` like `sscanf_s` where K.3.9.1.5 specifies `rsize_t`; the console's width is unverified. A runtime-constraint violation (null buffer, format or receiving pointer) only returns `EOF`, and a too small array gets a null first element as in `sscanf_s`. A literal mismatch is a matching failure and a field that is only a prefix, such as `0x` or `1e`, does not convert, as C11 requires; FreeBSD's `vfwscanf` reports an input failure for the first and converts the second. Integers out of range are truncated from `strtoll`/`strtoull`. It throws for `%p`, `%S`, `%C`, unknown length modifiers, scanset ranges, `%n` with suppression or width, and narrow `%s`, `%c` or `%[` input that the C-locale `wcrtomb` cannot convert. `swscanf` runs the same scanner without the size arguments of `%s`, `%c` and `%[`, as FreeBSD's [swscanf.c](https://github.com/freebsd/freebsd-src/blob/d106002967eff712894ab8b5337bbbff106ce5e0/lib/libc/stdio/swscanf.c) passes its arguments to `vswscanf`, so everything above except the sizes applies to it; it also keeps the null buffer, format and receiving pointer checks returning `EOF`, where C leaves those undefined. Neither variant is confirmed on a PS5 title
- [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