Merge pull request #3592 from MarcZX100/codex/socket-errno-errors-3573
fix(libkernel): map host socket errors to guest errno
This commit is contained in:
commit
af0b52ebb4
|
|
@ -838,6 +838,10 @@ if(BUILD_TESTING)
|
|||
set_tests_properties(guest_system_command PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
|
||||
endif()
|
||||
add_test_executable(guest_socket_error_tests ${LIBS_INCLUDE_DIR}/tests/GuestSocketError.cpp)
|
||||
target_include_directories(guest_socket_error_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
configure_windows_unwind(guest_socket_error_tests)
|
||||
add_test(NAME guest_socket_error COMMAND guest_socket_error_tests)
|
||||
add_test_executable(guest_udp_tests ${LIBS_INCLUDE_DIR}/tests/GuestUdp.cpp)
|
||||
target_include_directories(guest_udp_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_udp_tests PRIVATE libkernel libc)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,99 @@
|
|||
#pragma once
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#else
|
||||
#include <cerrno>
|
||||
#endif
|
||||
|
||||
namespace GuestSockets {
|
||||
|
||||
constexpr int GuestErrorIo = 5;
|
||||
|
||||
constexpr int GuestErrorFromHost(int hostError) {
|
||||
switch (hostError) {
|
||||
#ifdef _WIN32
|
||||
case WSAEINTR: return 4;
|
||||
case WSAEBADF: return 9;
|
||||
case WSAEACCES: return 13;
|
||||
case WSAEFAULT: return 14;
|
||||
case WSAEINVAL: return 22;
|
||||
case WSAEMFILE: return 24;
|
||||
case WSAEWOULDBLOCK: return 35;
|
||||
case WSAEINPROGRESS: return 36;
|
||||
case WSAEALREADY: return 37;
|
||||
case WSAENOTSOCK: return 38;
|
||||
case WSAEDESTADDRREQ: return 39;
|
||||
case WSAEMSGSIZE: return 40;
|
||||
case WSAEPROTOTYPE: return 41;
|
||||
case WSAENOPROTOOPT: return 42;
|
||||
case WSAEPROTONOSUPPORT: return 43;
|
||||
case WSAESOCKTNOSUPPORT: return 44;
|
||||
case WSAEOPNOTSUPP: return 45;
|
||||
case WSAEPFNOSUPPORT: return 46;
|
||||
case WSAEAFNOSUPPORT: return 47;
|
||||
case WSAEADDRINUSE: return 48;
|
||||
case WSAEADDRNOTAVAIL: return 49;
|
||||
case WSAENETDOWN: return 50;
|
||||
case WSAENETUNREACH: return 51;
|
||||
case WSAENETRESET: return 52;
|
||||
case WSAECONNABORTED: return 53;
|
||||
case WSAECONNRESET: return 54;
|
||||
case WSAENOBUFS: return 55;
|
||||
case WSAEISCONN: return 56;
|
||||
case WSAENOTCONN: return 57;
|
||||
case WSAESHUTDOWN: return 32;
|
||||
case WSAETOOMANYREFS: return 59;
|
||||
case WSAETIMEDOUT: return 60;
|
||||
case WSAECONNREFUSED: return 61;
|
||||
case WSAEHOSTDOWN: return 64;
|
||||
case WSAEHOSTUNREACH: return 65;
|
||||
#else
|
||||
case EPERM: return 1;
|
||||
case EINTR: return 4;
|
||||
case EBADF: return 9;
|
||||
case ENOMEM: return 12;
|
||||
case EACCES: return 13;
|
||||
case EFAULT: return 14;
|
||||
case EINVAL: return 22;
|
||||
case ENFILE: return 23;
|
||||
case EMFILE: return 24;
|
||||
case EPIPE: return 32;
|
||||
case EAGAIN: return 35;
|
||||
#if EWOULDBLOCK != EAGAIN
|
||||
case EWOULDBLOCK: return 35;
|
||||
#endif
|
||||
case EINPROGRESS: return 36;
|
||||
case EALREADY: return 37;
|
||||
case ENOTSOCK: return 38;
|
||||
case EDESTADDRREQ: return 39;
|
||||
case EMSGSIZE: return 40;
|
||||
case EPROTOTYPE: return 41;
|
||||
case ENOPROTOOPT: return 42;
|
||||
case EPROTONOSUPPORT: return 43;
|
||||
case ESOCKTNOSUPPORT: return 44;
|
||||
case EOPNOTSUPP: return 45;
|
||||
case EPFNOSUPPORT: return 46;
|
||||
case EAFNOSUPPORT: return 47;
|
||||
case EADDRINUSE: return 48;
|
||||
case EADDRNOTAVAIL: return 49;
|
||||
case ENETDOWN: return 50;
|
||||
case ENETUNREACH: return 51;
|
||||
case ENETRESET: return 52;
|
||||
case ECONNABORTED: return 53;
|
||||
case ECONNRESET: return 54;
|
||||
case ENOBUFS: return 55;
|
||||
case EISCONN: return 56;
|
||||
case ENOTCONN: return 57;
|
||||
case ESHUTDOWN: return 58;
|
||||
case ETOOMANYREFS: return 59;
|
||||
case ETIMEDOUT: return 60;
|
||||
case ECONNREFUSED: return 61;
|
||||
case EHOSTDOWN: return 64;
|
||||
case EHOSTUNREACH: return 65;
|
||||
case EPROTO: return 92;
|
||||
#endif
|
||||
default: return GuestErrorIo;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@
|
|||
#endif
|
||||
#include "prx/libc/include/GuestArena.hpp"
|
||||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include "prx/libkernel/Socket/include/SocketError.hpp"
|
||||
#include "prx/libkernel/Socket/include/SocketRuntime.hpp"
|
||||
#include "prx/libkernel/Equeue/Equeue.hpp"
|
||||
#include "prx/libkernel/Time/include/TimedWait.hpp"
|
||||
|
|
@ -54,47 +55,9 @@ int NativeSendFlags(int flags) {
|
|||
}
|
||||
int NativeError() {
|
||||
#ifdef _WIN32
|
||||
switch (WSAGetLastError()) {
|
||||
case WSAEWOULDBLOCK: return 35;
|
||||
case WSAEADDRINUSE: return 48;
|
||||
case WSAEADDRNOTAVAIL: return 49;
|
||||
case WSAEACCES: return 13;
|
||||
case WSAEMSGSIZE: return 40;
|
||||
case WSAENETUNREACH: return 51;
|
||||
case WSAEHOSTUNREACH: return 65;
|
||||
case WSAECONNRESET: return 54;
|
||||
case WSAECONNABORTED: return 53;
|
||||
case WSAEISCONN: return 56;
|
||||
case WSAENOTCONN: return 57;
|
||||
case WSAENOBUFS: return 55;
|
||||
case WSAETIMEDOUT: return 60;
|
||||
case WSAECONNREFUSED: return 61;
|
||||
case WSAEINTR: return 4;
|
||||
case WSAEINVAL: return 22;
|
||||
case WSAESHUTDOWN: return 32;
|
||||
default: return 5;
|
||||
}
|
||||
return GuestSockets::GuestErrorFromHost(WSAGetLastError());
|
||||
#else
|
||||
switch (errno) {
|
||||
case EAGAIN: return 35;
|
||||
case EADDRINUSE: return 48;
|
||||
case EADDRNOTAVAIL: return 49;
|
||||
case EACCES: return 13;
|
||||
case EMSGSIZE: return 40;
|
||||
case ENETUNREACH: return 51;
|
||||
case EHOSTUNREACH: return 65;
|
||||
case ECONNREFUSED: return 61;
|
||||
case ECONNRESET: return 54;
|
||||
case ECONNABORTED: return 53;
|
||||
case EISCONN: return 56;
|
||||
case ENOTCONN: return 57;
|
||||
case ENOBUFS: return 55;
|
||||
case ETIMEDOUT: return 60;
|
||||
case EINTR: return 4;
|
||||
case EINVAL: return 22;
|
||||
case EPIPE: return 32;
|
||||
default: return 5;
|
||||
}
|
||||
return GuestSockets::GuestErrorFromHost(errno);
|
||||
#endif
|
||||
}
|
||||
struct Socket {
|
||||
|
|
@ -354,21 +317,6 @@ namespace {
|
|||
constexpr int GuestUnix = 1;
|
||||
constexpr int GuestCloseOnExec = 0x10000000;
|
||||
constexpr int GuestNonBlocking = 0x20000000;
|
||||
int PairError() {
|
||||
#ifdef _WIN32
|
||||
switch (WSAGetLastError()) {
|
||||
case WSAEMFILE: return 24;
|
||||
default: return NativeError();
|
||||
}
|
||||
#else
|
||||
switch (errno) {
|
||||
case EMFILE: return 24;
|
||||
case ENFILE: return 23;
|
||||
case ENOMEM: return 12;
|
||||
default: return NativeError();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
int SetNonBlocking(Socket& socket) {
|
||||
#ifdef _WIN32
|
||||
unsigned long value = 1;
|
||||
|
|
@ -394,23 +342,23 @@ int NativePair(int type, NativeSocket (&pair)[2]) {
|
|||
loopback.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||
const int nativeType = type == 1 ? SOCK_STREAM : SOCK_DGRAM;
|
||||
Socket first(::socket(AF_INET, nativeType, 0), 2, type);
|
||||
if (first.value == Invalid) return PairError();
|
||||
if (first.value == Invalid) return NativeError();
|
||||
const int exclusive = 1;
|
||||
setsockopt(first.value, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, reinterpret_cast<const char*>(&exclusive), sizeof(exclusive));
|
||||
if (bind(first.value, reinterpret_cast<sockaddr*>(&loopback), sizeof(loopback))) return PairError();
|
||||
if (bind(first.value, reinterpret_cast<sockaddr*>(&loopback), sizeof(loopback))) return NativeError();
|
||||
sockaddr_in firstName{};
|
||||
if (!LoopbackName(first.value, firstName)) return PairError();
|
||||
if (!LoopbackName(first.value, firstName)) return NativeError();
|
||||
Socket second(::socket(AF_INET, nativeType, 0), 2, type);
|
||||
if (second.value == Invalid) return PairError();
|
||||
if (second.value == Invalid) return NativeError();
|
||||
if (type == 1) {
|
||||
if (listen(first.value, 1)) return PairError();
|
||||
if (connect(second.value, reinterpret_cast<sockaddr*>(&firstName), sizeof(firstName))) return PairError();
|
||||
if (listen(first.value, 1)) return NativeError();
|
||||
if (connect(second.value, reinterpret_cast<sockaddr*>(&firstName), sizeof(firstName))) return NativeError();
|
||||
sockaddr_in peer{};
|
||||
int peerSize = sizeof(peer);
|
||||
Socket accepted(accept(first.value, reinterpret_cast<sockaddr*>(&peer), &peerSize), 2, type);
|
||||
if (accepted.value == Invalid) return PairError();
|
||||
if (accepted.value == Invalid) return NativeError();
|
||||
sockaddr_in secondName{};
|
||||
if (!LoopbackName(second.value, secondName)) return PairError();
|
||||
if (!LoopbackName(second.value, secondName)) return NativeError();
|
||||
if (peer.sin_port != secondName.sin_port) return 53;
|
||||
const int noDelay = 1;
|
||||
setsockopt(second.value, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<const char*>(&noDelay), sizeof(noDelay));
|
||||
|
|
@ -421,11 +369,11 @@ int NativePair(int type, NativeSocket (&pair)[2]) {
|
|||
accepted.value = Invalid;
|
||||
return 0;
|
||||
}
|
||||
if (bind(second.value, reinterpret_cast<sockaddr*>(&loopback), sizeof(loopback))) return PairError();
|
||||
if (bind(second.value, reinterpret_cast<sockaddr*>(&loopback), sizeof(loopback))) return NativeError();
|
||||
sockaddr_in secondName{};
|
||||
if (!LoopbackName(second.value, secondName)) return PairError();
|
||||
if (connect(first.value, reinterpret_cast<sockaddr*>(&secondName), sizeof(secondName))) return PairError();
|
||||
if (connect(second.value, reinterpret_cast<sockaddr*>(&firstName), sizeof(firstName))) return PairError();
|
||||
if (!LoopbackName(second.value, secondName)) return NativeError();
|
||||
if (connect(first.value, reinterpret_cast<sockaddr*>(&secondName), sizeof(secondName))) return NativeError();
|
||||
if (connect(second.value, reinterpret_cast<sockaddr*>(&firstName), sizeof(firstName))) return NativeError();
|
||||
pair[0] = first.value;
|
||||
pair[1] = second.value;
|
||||
first.value = Invalid;
|
||||
|
|
@ -436,7 +384,7 @@ int NativePair(int type, NativeSocket (&pair)[2]) {
|
|||
int NativePair(int type, NativeSocket (&pair)[2]) {
|
||||
const int nativeType = type == 1 ? SOCK_STREAM : type == 2 ? SOCK_DGRAM : SOCK_SEQPACKET;
|
||||
int native[2];
|
||||
if (::socketpair(AF_UNIX, nativeType, 0, native)) return PairError();
|
||||
if (::socketpair(AF_UNIX, nativeType, 0, native)) return NativeError();
|
||||
pair[0] = native[0];
|
||||
pair[1] = native[1];
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,126 @@
|
|||
#include "prx/libkernel/Socket/include/SocketError.hpp"
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <initializer_list>
|
||||
|
||||
struct ErrorCase {
|
||||
const char* name;
|
||||
int host;
|
||||
int guest;
|
||||
};
|
||||
|
||||
#define HostError(prefix, name, guest) {#prefix #name, prefix##name, guest}
|
||||
|
||||
#ifdef _WIN32
|
||||
static constexpr ErrorCase Cases[] = {
|
||||
HostError(WSA, EINTR, 4),
|
||||
HostError(WSA, EBADF, 9),
|
||||
HostError(WSA, EACCES, 13),
|
||||
HostError(WSA, EFAULT, 14),
|
||||
HostError(WSA, EINVAL, 22),
|
||||
HostError(WSA, EMFILE, 24),
|
||||
HostError(WSA, EWOULDBLOCK, 35),
|
||||
HostError(WSA, EINPROGRESS, 36),
|
||||
HostError(WSA, EALREADY, 37),
|
||||
HostError(WSA, ENOTSOCK, 38),
|
||||
HostError(WSA, EDESTADDRREQ, 39),
|
||||
HostError(WSA, EMSGSIZE, 40),
|
||||
HostError(WSA, EPROTOTYPE, 41),
|
||||
HostError(WSA, ENOPROTOOPT, 42),
|
||||
HostError(WSA, EPROTONOSUPPORT, 43),
|
||||
HostError(WSA, ESOCKTNOSUPPORT, 44),
|
||||
HostError(WSA, EOPNOTSUPP, 45),
|
||||
HostError(WSA, EPFNOSUPPORT, 46),
|
||||
HostError(WSA, EAFNOSUPPORT, 47),
|
||||
HostError(WSA, EADDRINUSE, 48),
|
||||
HostError(WSA, EADDRNOTAVAIL, 49),
|
||||
HostError(WSA, ENETDOWN, 50),
|
||||
HostError(WSA, ENETUNREACH, 51),
|
||||
HostError(WSA, ENETRESET, 52),
|
||||
HostError(WSA, ECONNABORTED, 53),
|
||||
HostError(WSA, ECONNRESET, 54),
|
||||
HostError(WSA, ENOBUFS, 55),
|
||||
HostError(WSA, EISCONN, 56),
|
||||
HostError(WSA, ENOTCONN, 57),
|
||||
HostError(WSA, ETOOMANYREFS, 59),
|
||||
HostError(WSA, ETIMEDOUT, 60),
|
||||
HostError(WSA, ECONNREFUSED, 61),
|
||||
HostError(WSA, EHOSTDOWN, 64),
|
||||
HostError(WSA, EHOSTUNREACH, 65),
|
||||
};
|
||||
static constexpr int UnmappedHost = WSAEDISCON;
|
||||
static constexpr int ShutdownHost = WSAESHUTDOWN;
|
||||
#else
|
||||
static constexpr ErrorCase Cases[] = {
|
||||
HostError(, EPERM, 1),
|
||||
HostError(, EINTR, 4),
|
||||
HostError(, EBADF, 9),
|
||||
HostError(, ENOMEM, 12),
|
||||
HostError(, EACCES, 13),
|
||||
HostError(, EFAULT, 14),
|
||||
HostError(, EINVAL, 22),
|
||||
HostError(, ENFILE, 23),
|
||||
HostError(, EMFILE, 24),
|
||||
HostError(, EPIPE, 32),
|
||||
HostError(, EAGAIN, 35),
|
||||
HostError(, EWOULDBLOCK, 35),
|
||||
HostError(, EINPROGRESS, 36),
|
||||
HostError(, EALREADY, 37),
|
||||
HostError(, ENOTSOCK, 38),
|
||||
HostError(, EDESTADDRREQ, 39),
|
||||
HostError(, EMSGSIZE, 40),
|
||||
HostError(, EPROTOTYPE, 41),
|
||||
HostError(, ENOPROTOOPT, 42),
|
||||
HostError(, EPROTONOSUPPORT, 43),
|
||||
HostError(, ESOCKTNOSUPPORT, 44),
|
||||
HostError(, EOPNOTSUPP, 45),
|
||||
HostError(, EPFNOSUPPORT, 46),
|
||||
HostError(, EAFNOSUPPORT, 47),
|
||||
HostError(, EADDRINUSE, 48),
|
||||
HostError(, EADDRNOTAVAIL, 49),
|
||||
HostError(, ENETDOWN, 50),
|
||||
HostError(, ENETUNREACH, 51),
|
||||
HostError(, ENETRESET, 52),
|
||||
HostError(, ECONNABORTED, 53),
|
||||
HostError(, ECONNRESET, 54),
|
||||
HostError(, ENOBUFS, 55),
|
||||
HostError(, EISCONN, 56),
|
||||
HostError(, ENOTCONN, 57),
|
||||
HostError(, ESHUTDOWN, 58),
|
||||
HostError(, ETOOMANYREFS, 59),
|
||||
HostError(, ETIMEDOUT, 60),
|
||||
HostError(, ECONNREFUSED, 61),
|
||||
HostError(, EHOSTDOWN, 64),
|
||||
HostError(, EHOSTUNREACH, 65),
|
||||
HostError(, EPROTO, 92),
|
||||
};
|
||||
static constexpr int UnmappedHost = ENOENT;
|
||||
#endif
|
||||
|
||||
int main() {
|
||||
int failures = 0;
|
||||
for (const auto& entry : Cases) {
|
||||
const int guest = GuestSockets::GuestErrorFromHost(entry.host);
|
||||
if (guest != entry.guest) {
|
||||
std::fprintf(stderr, "%s (%d): guest %d, FreeBSD %d\n", entry.name, entry.host, guest, entry.guest);
|
||||
++failures;
|
||||
}
|
||||
}
|
||||
if (GuestSockets::GuestErrorFromHost(UnmappedHost) != 5) {
|
||||
std::fprintf(stderr, "a host error with no FreeBSD socket counterpart is not EIO\n");
|
||||
++failures;
|
||||
}
|
||||
for (const int host : {0, -1}) {
|
||||
if (GuestSockets::GuestErrorFromHost(host) != 5) {
|
||||
std::fprintf(stderr, "host error %d is not EIO\n", host);
|
||||
++failures;
|
||||
}
|
||||
}
|
||||
#ifdef _WIN32
|
||||
if (GuestSockets::GuestErrorFromHost(ShutdownHost) != 32) {
|
||||
std::fprintf(stderr, "WSAESHUTDOWN is not EPIPE\n");
|
||||
++failures;
|
||||
}
|
||||
#endif
|
||||
return failures == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
|
|
@ -68,6 +68,13 @@ int main() {
|
|||
Require(getsockopt_nid_postfix(sender, 0xffff, 0x20, &option, &optionSize) == 0 && option != 0);
|
||||
Require(setsockopt_nid_postfix(sender, 0xffff, 12345, &enabled, sizeof(enabled)) == -1);
|
||||
Require(*__error_nid_postfix() == 42);
|
||||
#ifndef _WIN32
|
||||
const int unconnected = socket_nid_postfix(2, 2, 0);
|
||||
Require(unconnected >= 0);
|
||||
Require(sendto_nid_postfix(unconnected, "x", 1, 0, nullptr, 0) == -1);
|
||||
Require(*__error_nid_postfix() == 39);
|
||||
Require(close_nid_postfix(unconnected) == 0);
|
||||
#endif
|
||||
const int unsized = socket_nid_postfix(2, 2, 0);
|
||||
std::array<unsigned char, 16> unsizedAddress{0, 2, 0, 0, 127, 0, 0, 1};
|
||||
Require(unsized >= 0 && bind_nid_postfix(unsized, unsizedAddress.data(), unsizedAddress.size()) == 0);
|
||||
|
|
|
|||
|
|
@ -594,6 +594,7 @@ Throughout the project, every function at every stage either **does exactly what
|
|||
- [UDP recv/recvfrom](../../core/libs/prx/libkernel/Socket/src/Udp.cpp) and [sceNetRecv/sceNetRecvfrom](../../core/libs/prx/libSceNet/Export.cpp) translate Windows WSAEMSGSIZE into the copied byte count for datagram sockets, including MSG_PEEK, and retain the sender address. This follows FreeBSD 11.4 recv(2) and Winsock's documented truncated-datagram behavior; console behavior and existing title impact are unverified.
|
||||
- [read, write and vector I/O](../../core/libs/prx/libkernel/File/src/Stdio.cpp) (libkernel) on a guest socket use `recv` and `send` without flags; `sceKernelReadv` and `sceKernelWritev` gather or scatter through one socket operation, preserving stream short reads and datagram boundaries. The positioned `pread`, `pwrite`, `preadv` and `pwritev` forms, and their `sceKernel` forms, return `ESPIPE` for an open guest socket and `EBADF` for a closed one. Guest socket descriptors start at `0x10000000` and are allocated upward, so the descriptor range selects the guest socket table or the host descriptor table. `EPIPE` from a write raises the host's `SIGPIPE` on Linux, which the guest can neither ignore nor suppress: `signal(SIGPIPE, SIG_IGN)` and `sigaction` record the disposition, as they do for every number from 1 to 128, but only the six signals with host equivalents (2, 4, 6, 8, 11 and 15) take effect on the host or are delivered, so the host `SIGPIPE` is not stopped; `SO_NOSIGPIPE` is not an option here, and raises nothing on Windows; a `write` longer than `INT_MAX` bytes is `EMSGSIZE` where the console sends it. AIO on a guest socket still goes to the host, which has no such descriptor
|
||||
- [poll](../../core/libs/prx/libkernel/Socket/src/Udp.cpp) (libkernel) waits only on guest sockets, through `WSAPoll` on Windows and `poll` on Linux; the `pollfd` layout and flag values are FreeBSD's. A descriptor below the socket range (files, pipes, stdio) throws, as do event bits other than `POLLIN`, `POLLPRI`, `POLLOUT`, `POLLRDNORM`, `POLLRDBAND` and `POLLWRBAND` (the `POLLERR`, `POLLHUP` and `POLLNVAL` bits are ignored in `events`), `POLLPRI` on Windows, which `WSAPoll` rejects, and an infinite timeout with no socket to wait on. A closed socket descriptor reports `POLLNVAL`; the console's limit on the descriptor count is unknown and not checked
|
||||
- [Socket error numbers](../../core/libs/prx/libkernel/Socket/include/SocketError.hpp) (libkernel) - a host socket error becomes the FreeBSD 11 `errno.h` number of the same name; the numbers are not checked on the console. Winsock `WSAESHUTDOWN` becomes `EPIPE`, as a send after `shutdown` does on FreeBSD, and a host error with no FreeBSD socket counterpart becomes `EIO`. A nonblocking `connect` in progress reports `EINPROGRESS` on Linux but `EAGAIN` on Windows (`WSAEWOULDBLOCK`), where FreeBSD reports `EINPROGRESS`. On Windows a refused nonblocking `connect` is never reported (`SO_ERROR` is unsupported), so a repeated one keeps reporting `EALREADY` where FreeBSD reports `ECONNREFUSED`. A receive after `shutdown(SHUT_RD)` reports `EPIPE` on Windows where FreeBSD returns 0
|
||||
- [msync](../../core/libs/prx/libkernel/DirectMemory/PosixMemory.cpp) (libkernel) follows FreeBSD 11's `kern_msync` and `vm_map_sync`, not measured on the console: the address is rounded down to the page, a range that wraps is `EINVAL`, as is `MS_ASYNC` with `MS_INVALIDATE`, and a range whose start or any page is not mapped (as `sceKernelVirtualQuery` reports it, reservations included) is `ENOMEM`; a page unmapped from inside a larger mapping still counts as mapped there, since `sceKernelVirtualQuery` keeps reporting the whole mapping. The guest has no file mappings, so a successful call writes nothing back. `MS_INVALIDATE` on a mapped range throws: FreeBSD returns `EBUSY` when the range holds `mlock`ed pages, and locked pages are not tracked
|
||||
- [Thread exit destructors](../../core/libs/prx/libSceLibcInternal/Export.cpp) (libSceLibcInternal) run when a libkernel thread finishes; on the main or a host thread only `_sceLibcInternalThreadDtors` runs them, as `exit` does, else they are dropped
|
||||
- [sceFontOpenFontSet](../../core/libs/prx/libSceFont/src/SystemFont.cpp) (libSceFont) opens only the primary file of a system font set. Characters the console takes from the set's other files (Hangul, Thai and Arabic in Japanese and Chinese sets, Arabic in European sets) are reported as unsupported glyphs. The console file names are those of the PS4 firmware; the names of the PS5-only JG2 sets (`0x1A......`) are unknown, so they use the SSTJpPro files. When a console file is missing, an openly licensed substitute (Noto Sans, Noto Sans Mono, Noto Sans Thai, Noto Sans CJK) is loaded instead. It is not the system font: its metrics differ, so text width, line height and wrapping differ from the console. System font faces are loaded by FreeType in host memory, not in the font memory the title passes to the library: FreeType needs about 1 MiB per CJK face, which the title's font memory is not sized for.
|
||||
|
|
|
|||
Loading…
Reference in New Issue