// SPDX-License-Identifier: MIT /* $info$ tags: LinuxSyscalls|syscalls-x86-32 $end_info$ */ #include "LinuxSyscalls/Syscalls.h" #include "LinuxSyscalls/x32/Syscalls.h" #include "LinuxSyscalls/x32/Types.h" #include "LinuxSyscalls/x64/Syscalls.h" #include #include #include #include #include #include #include #include #include ARG_TO_STR(FEX::HLE::x32::compat_ptr, "%lx") ARG_TO_STR(FEX::HLE::x32::compat_ptr, "%lx") ARG_TO_STR(FEX::HLE::x32::compat_ptr, "%lx") namespace FEXCore::Core { struct CpuStateFrame; } namespace FEX::HLE::x32 { // Some sockopt defines for older build environments #ifndef SO_RCVTIMEO_OLD #define SO_RCVTIMEO_OLD 20 #endif #ifndef SO_SNDTIMEO_OLD #define SO_SNDTIMEO_OLD 21 #endif #ifndef SO_TIMESTAMP_OLD #define SO_TIMESTAMP_OLD 29 #endif #ifndef SO_TIMESTAMPNS_OLD #define SO_TIMESTAMPNS_OLD 35 #endif #ifndef SO_TIMESTAMPING_OLD #define SO_TIMESTAMPING_OLD 37 #endif #ifndef SO_MEMINFO #define SO_MEMINFO 55 #endif #ifndef SO_INCOMING_NAPI_ID #define SO_INCOMING_NAPI_ID 56 #endif #ifndef SO_PEERGROUPS #define SO_PEERGROUPS 59 #endif #ifndef SO_ZEROCOPY #define SO_ZEROCOPY 60 #endif #ifndef SO_TXTIME #define SO_TXTIME 61 #endif #ifndef SO_BINDTOIFINDEX #define SO_BINDTOIFINDEX 62 #endif #ifndef SO_TIMESTAMP_NEW #define SO_TIMESTAMP_NEW 63 #endif #ifndef SO_TIMESTAMPNS_NEW #define SO_TIMESTAMPNS_NEW 64 #endif #ifndef SO_TIMESTAMPING_NEW #define SO_TIMESTAMPING_NEW 65 #endif #ifndef SO_RCVTIMEO_NEW #define SO_RCVTIMEO_NEW 66 #endif #ifndef SO_SNDTIMEO_NEW #define SO_SNDTIMEO_NEW 67 #endif #ifndef SO_DETACH_REUSEPORT_BPF #define SO_DETACH_REUSEPORT_BPF 68 #endif #ifndef SO_PREFER_BUSY_POLL #define SO_PREFER_BUSY_POLL 69 #endif #ifndef SO_BUSY_POLL_BUDGET #define SO_BUSY_POLL_BUDGET 70 #endif #ifndef SO_NETNS_COOKIE #define SO_NETNS_COOKIE 71 #endif #ifndef SO_BUF_LOCK #define SO_BUF_LOCK 72 #endif #ifndef SO_RESERVE_MEM #define SO_RESERVE_MEM 73 #endif enum SockOp { OP_SOCKET = 1, OP_BIND = 2, OP_CONNECT = 3, OP_LISTEN = 4, OP_ACCEPT = 5, OP_GETSOCKNAME = 6, OP_GETPEERNAME = 7, OP_SOCKETPAIR = 8, OP_SEND = 9, OP_RECV = 10, OP_SENDTO = 11, OP_RECVFROM = 12, OP_SHUTDOWN = 13, OP_SETSOCKOPT = 14, OP_GETSOCKOPT = 15, OP_SENDMSG = 16, OP_RECVMSG = 17, OP_ACCEPT4 = 18, OP_RECVMMSG = 19, OP_SENDMMSG = 20, }; static uint64_t SendMsg(int sockfd, const struct msghdr32 *msg, int flags) { struct msghdr HostHeader{}; fextl::vector Host_iovec(msg->msg_iovlen); for (size_t i = 0; i < msg->msg_iovlen; ++i) { Host_iovec[i] = msg->msg_iov[i]; } HostHeader.msg_name = msg->msg_name; HostHeader.msg_namelen = msg->msg_namelen; HostHeader.msg_iov = Host_iovec.data(); HostHeader.msg_iovlen = msg->msg_iovlen; HostHeader.msg_control = alloca(msg->msg_controllen * 2); HostHeader.msg_controllen = msg->msg_controllen; HostHeader.msg_flags = msg->msg_flags; if (HostHeader.msg_controllen) { void *CurrentGuestPtr = msg->msg_control; struct cmsghdr *CurrentHost = reinterpret_cast(HostHeader.msg_control); for (cmsghdr32 *msghdr_guest = reinterpret_cast(CurrentGuestPtr); CurrentGuestPtr != 0; msghdr_guest = reinterpret_cast(CurrentGuestPtr)) { CurrentHost->cmsg_level = msghdr_guest->cmsg_level; CurrentHost->cmsg_type = msghdr_guest->cmsg_type; if (msghdr_guest->cmsg_len) { size_t SizeIncrease = (CMSG_LEN(0) - sizeof(cmsghdr32)); CurrentHost->cmsg_len = msghdr_guest->cmsg_len + SizeIncrease; HostHeader.msg_controllen += SizeIncrease; memcpy(CMSG_DATA(CurrentHost), msghdr_guest->cmsg_data, msghdr_guest->cmsg_len - sizeof(cmsghdr32)); } // Go to next host CurrentHost = CMSG_NXTHDR(&HostHeader, CurrentHost); // Go to next msg if (msghdr_guest->cmsg_len < sizeof(cmsghdr32)) { CurrentGuestPtr = nullptr; } else { CurrentGuestPtr = reinterpret_cast(reinterpret_cast(CurrentGuestPtr) + msghdr_guest->cmsg_len); CurrentGuestPtr = reinterpret_cast((reinterpret_cast(CurrentGuestPtr) + 3) & ~3ULL); if (CurrentGuestPtr >= reinterpret_cast(reinterpret_cast(static_cast(msg->msg_control)) + msg->msg_controllen)) { CurrentGuestPtr = nullptr; } } } } uint64_t Result = ::sendmsg(sockfd, &HostHeader, flags); SYSCALL_ERRNO(); } static uint64_t RecvMsg(int sockfd, struct msghdr32 *msg, int flags) { struct msghdr HostHeader{}; fextl::vector Host_iovec(msg->msg_iovlen); for (size_t i = 0; i < msg->msg_iovlen; ++i) { Host_iovec[i] = msg->msg_iov[i]; } HostHeader.msg_name = msg->msg_name; HostHeader.msg_namelen = msg->msg_namelen; HostHeader.msg_iov = Host_iovec.data(); HostHeader.msg_iovlen = msg->msg_iovlen; HostHeader.msg_control = alloca(msg->msg_controllen*2); HostHeader.msg_controllen = msg->msg_controllen*2; HostHeader.msg_flags = msg->msg_flags; uint64_t Result = ::recvmsg(sockfd, &HostHeader, flags); if (Result != -1) { for (size_t i = 0; i < msg->msg_iovlen; ++i) { msg->msg_iov[i] = Host_iovec[i]; } msg->msg_namelen = HostHeader.msg_namelen; msg->msg_controllen = HostHeader.msg_controllen; msg->msg_flags = HostHeader.msg_flags; if (HostHeader.msg_controllen) { // Host and guest cmsg data structures aren't compatible. // Copy them over now void *CurrentGuestPtr = msg->msg_control; for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(&HostHeader); cmsg != nullptr; cmsg = CMSG_NXTHDR(&HostHeader, cmsg)) { cmsghdr32 *CurrentGuest = reinterpret_cast(CurrentGuestPtr); // Copy over the header first // cmsg_len needs to be adjusted by the size of the header between host and guest // Host is 16 bytes, guest is 12 bytes CurrentGuest->cmsg_level = cmsg->cmsg_level; CurrentGuest->cmsg_type = cmsg->cmsg_type; // Now copy over the data if (cmsg->cmsg_len) { size_t SizeIncrease = (CMSG_LEN(0) - sizeof(cmsghdr32)); CurrentGuest->cmsg_len = cmsg->cmsg_len - SizeIncrease; // Controllen size also changes msg->msg_controllen -= SizeIncrease; memcpy(CurrentGuest->cmsg_data, CMSG_DATA(cmsg), cmsg->cmsg_len - sizeof(struct cmsghdr)); CurrentGuestPtr = reinterpret_cast(reinterpret_cast(CurrentGuestPtr) + CurrentGuest->cmsg_len); CurrentGuestPtr = reinterpret_cast((reinterpret_cast(CurrentGuestPtr) + 3) & ~3ULL); } } } } SYSCALL_ERRNO(); } void ConvertHeaderToHost(fextl::vector &iovec, struct msghdr *Host, const struct msghdr32 *Guest) { size_t CurrentIOVecSize = iovec.size(); iovec.resize(CurrentIOVecSize + Guest->msg_iovlen); for (size_t i = 0; i < Guest->msg_iovlen; ++i) { iovec[CurrentIOVecSize + i] = Guest->msg_iov[i]; } Host->msg_name = Guest->msg_name; Host->msg_namelen = Guest->msg_namelen; Host->msg_iov = &iovec[CurrentIOVecSize]; Host->msg_iovlen = Guest->msg_iovlen; // XXX: This could result in a stack overflow Host->msg_control = alloca(Guest->msg_controllen*2); Host->msg_controllen = Guest->msg_controllen*2; Host->msg_flags = Guest->msg_flags; } void ConvertHeaderToGuest(struct msghdr32 *Guest, struct msghdr *Host) { for (size_t i = 0; i < Guest->msg_iovlen; ++i) { Guest->msg_iov[i] = Host->msg_iov[i]; } Guest->msg_namelen = Host->msg_namelen; Guest->msg_controllen = Host->msg_controllen; Guest->msg_flags = Host->msg_flags; if (Host->msg_controllen) { // Host and guest cmsg data structures aren't compatible. // Copy them over now void *CurrentGuestPtr = Guest->msg_control; for (struct cmsghdr *cmsg = CMSG_FIRSTHDR(Host); cmsg != nullptr; cmsg = CMSG_NXTHDR(Host, cmsg)) { cmsghdr32 *CurrentGuest = reinterpret_cast(CurrentGuestPtr); // Copy over the header first // cmsg_len needs to be adjusted by the size of the header between host and guest // Host is 16 bytes, guest is 12 bytes CurrentGuest->cmsg_level = cmsg->cmsg_level; CurrentGuest->cmsg_type = cmsg->cmsg_type; // Now copy over the data if (cmsg->cmsg_len) { size_t SizeIncrease = (CMSG_LEN(0) - sizeof(cmsghdr32)); CurrentGuest->cmsg_len = cmsg->cmsg_len - SizeIncrease; // Controllen size also changes Guest->msg_controllen -= SizeIncrease; memcpy(CurrentGuest->cmsg_data, CMSG_DATA(cmsg), cmsg->cmsg_len - sizeof(struct cmsghdr)); CurrentGuestPtr = reinterpret_cast(reinterpret_cast(CurrentGuestPtr) + CurrentGuest->cmsg_len); CurrentGuestPtr = reinterpret_cast((reinterpret_cast(CurrentGuestPtr) + 3) & ~3ULL); } } } } static uint64_t RecvMMsg(int sockfd, compat_ptr msgvec, uint32_t vlen, int flags, struct timespec *timeout_ts) { fextl::vector Host_iovec; fextl::vector HostMHeader(vlen); for (size_t i = 0; i < vlen; ++i) { ConvertHeaderToHost(Host_iovec, &HostMHeader[i].msg_hdr, &msgvec[i].msg_hdr); HostMHeader[i].msg_len = msgvec[i].msg_len; } uint64_t Result = ::recvmmsg(sockfd, HostMHeader.data(), vlen, flags, timeout_ts); if (Result != -1) { for (size_t i = 0; i < Result; ++i) { ConvertHeaderToGuest(&msgvec[i].msg_hdr, &HostMHeader[i].msg_hdr); msgvec[i].msg_len = HostMHeader[i].msg_len; } } SYSCALL_ERRNO(); } static uint64_t SendMMsg(int sockfd, compat_ptr msgvec, uint32_t vlen, int flags) { fextl::vector Host_iovec; fextl::vector HostMmsg(vlen); // Walk the iovec and convert them // Calculate controllen at the same time size_t Controllen_size{}; for (size_t i = 0; i < vlen; ++i) { msghdr32 &guest = msgvec[i].msg_hdr; Controllen_size += guest.msg_controllen * 2; for (size_t j = 0; j < guest.msg_iovlen; ++j) { iovec guest_iov = guest.msg_iov[j]; Host_iovec.emplace_back(guest_iov); } } fextl::vector Controllen(Controllen_size); size_t current_iov{}; size_t current_controllen_offset{}; for (size_t i = 0; i < vlen; ++i) { msghdr32 &guest = msgvec[i].msg_hdr; struct msghdr &msg = HostMmsg[i].msg_hdr; msg.msg_name = guest.msg_name; msg.msg_namelen = guest.msg_namelen; msg.msg_iov = &Host_iovec.at(current_iov); msg.msg_iovlen = guest.msg_iovlen; current_iov += msg.msg_iovlen; if (guest.msg_controllen) { msg.msg_control = &Controllen.at(current_controllen_offset); current_controllen_offset += guest.msg_controllen * 2; } msg.msg_controllen = guest.msg_controllen; msg.msg_flags = guest.msg_flags; if (msg.msg_controllen) { void *CurrentGuestPtr = guest.msg_control; struct cmsghdr *CurrentHost = reinterpret_cast(msg.msg_control); for (cmsghdr32 *msghdr_guest = reinterpret_cast(CurrentGuestPtr); CurrentGuestPtr != 0; msghdr_guest = reinterpret_cast(CurrentGuestPtr)) { CurrentHost->cmsg_level = msghdr_guest->cmsg_level; CurrentHost->cmsg_type = msghdr_guest->cmsg_type; if (msghdr_guest->cmsg_len) { size_t SizeIncrease = (CMSG_LEN(0) - sizeof(cmsghdr32)); CurrentHost->cmsg_len = msghdr_guest->cmsg_len + SizeIncrease; msg.msg_controllen += SizeIncrease; memcpy(CMSG_DATA(CurrentHost), msghdr_guest->cmsg_data, msghdr_guest->cmsg_len - sizeof(cmsghdr32)); } // Go to next host CurrentHost = CMSG_NXTHDR(&msg, CurrentHost); // Go to next msg if (msghdr_guest->cmsg_len < sizeof(cmsghdr32)) { CurrentGuestPtr = nullptr; } else { CurrentGuestPtr = reinterpret_cast(reinterpret_cast(CurrentGuestPtr) + msghdr_guest->cmsg_len); CurrentGuestPtr = reinterpret_cast((reinterpret_cast(CurrentGuestPtr) + 3) & ~3ULL); if (CurrentGuestPtr >= reinterpret_cast(reinterpret_cast(static_cast(guest.msg_control)) + guest.msg_controllen)) { CurrentGuestPtr = nullptr; } } } } HostMmsg[i].msg_len = msgvec[i].msg_len; } uint64_t Result = ::sendmmsg(sockfd, HostMmsg.data(), vlen, flags); if (Result != -1) { // Update guest msglen for (size_t i = 0; i < Result; ++i) { msgvec[i].msg_len = HostMmsg[i].msg_len; } } SYSCALL_ERRNO(); } static uint64_t SetSockOpt(int sockfd, int level, int optname, compat_ptr optval, int optlen) { uint64_t Result{}; if (level == SOL_SOCKET) { switch (optname) { case SO_ATTACH_FILTER: case SO_ATTACH_REUSEPORT_CBPF: { struct sock_fprog32 { uint16_t len; uint32_t filter; }; struct sock_fprog64 { uint16_t len; uint64_t filter; }; if (optlen != sizeof(sock_fprog32)) { return -EINVAL; } sock_fprog32 *prog = reinterpret_cast(optval.Ptr); sock_fprog64 prog64{}; prog64.len = prog->len; prog64.filter = prog->filter; Result = ::syscall(SYSCALL_DEF(setsockopt), sockfd, level, optname, &prog64, sizeof(sock_fprog64) ); break; } case SO_RCVTIMEO_OLD: { // _OLD uses old_timeval32. Needs to be converted struct timeval tv64 = *reinterpret_cast(optval.Ptr); Result = ::syscall(SYSCALL_DEF(setsockopt), sockfd, level, SO_RCVTIMEO_NEW, &tv64, sizeof(tv64) ); break; } case SO_SNDTIMEO_OLD: { // _OLD uses old_timeval32. Needs to be converted struct timeval tv64 = *reinterpret_cast(optval.Ptr); Result = ::syscall(SYSCALL_DEF(setsockopt), sockfd, level, SO_SNDTIMEO_NEW, &tv64, sizeof(tv64) ); break; } // Each optname as a reminder which setting has been manually checked case SO_DEBUG: case SO_REUSEADDR: case SO_TYPE: case SO_ERROR: case SO_DONTROUTE: case SO_BROADCAST: case SO_SNDBUF: case SO_RCVBUF: case SO_SNDBUFFORCE: case SO_RCVBUFFORCE: case SO_KEEPALIVE: case SO_OOBINLINE: case SO_NO_CHECK: case SO_PRIORITY: case SO_LINGER: case SO_BSDCOMPAT: case SO_REUSEPORT: /** * @name These end up differing between {x86,arm} and {powerpc, alpha, sparc, mips, parisc} * @{ */ case SO_PASSCRED: case SO_PEERCRED: case SO_RCVLOWAT: case SO_SNDLOWAT: /** @} */ case SO_SECURITY_AUTHENTICATION: case SO_SECURITY_ENCRYPTION_TRANSPORT: case SO_SECURITY_ENCRYPTION_NETWORK: case SO_DETACH_FILTER: case SO_PEERNAME: case SO_TIMESTAMP_OLD: // Returns int32_t boolean case SO_ACCEPTCONN: case SO_PEERSEC: // Gap 32, 33 case SO_PASSSEC: case SO_TIMESTAMPNS_OLD: // Returns int32_t boolean case SO_MARK: case SO_TIMESTAMPING_OLD: // Returns so_timestamping case SO_PROTOCOL: case SO_DOMAIN: case SO_RXQ_OVFL: case SO_WIFI_STATUS: case SO_PEEK_OFF: case SO_NOFCS: case SO_LOCK_FILTER: case SO_SELECT_ERR_QUEUE: case SO_BUSY_POLL: case SO_MAX_PACING_RATE: case SO_BPF_EXTENSIONS: case SO_INCOMING_CPU: case SO_ATTACH_BPF: case SO_ATTACH_REUSEPORT_EBPF: case SO_CNX_ADVICE: // Gap 54 (SCM_TIMESTAMPING_OPT_STATS) case SO_MEMINFO: case SO_INCOMING_NAPI_ID: case SO_COOKIE: // Cookie always returns 64-bit even on 32-bit // Gap 58 (SCM_TIMESTAMPING_PKTINFO) case SO_PEERGROUPS: case SO_ZEROCOPY: case SO_TXTIME: case SO_BINDTOIFINDEX: case SO_TIMESTAMP_NEW: case SO_TIMESTAMPNS_NEW: case SO_TIMESTAMPING_NEW: case SO_RCVTIMEO_NEW: case SO_SNDTIMEO_NEW: case SO_DETACH_REUSEPORT_BPF: case SO_PREFER_BUSY_POLL: case SO_BUSY_POLL_BUDGET: case SO_NETNS_COOKIE: // Cookie always returns 64-bit even on 32-bit case SO_BUF_LOCK: case SO_RESERVE_MEM: default: Result = ::syscall(SYSCALL_DEF(setsockopt), sockfd, level, optname, reinterpret_cast(optval.Ptr), optlen ); break; } } else { Result = ::syscall(SYSCALL_DEF(setsockopt), sockfd, level, optname, reinterpret_cast(optval.Ptr), optlen ); } SYSCALL_ERRNO(); } static uint64_t GetSockOpt(int sockfd, int level, int optname, compat_ptr optval, compat_ptr optlen) { uint64_t Result{}; if (level == SOL_SOCKET) { switch (optname) { case SO_RCVTIMEO_OLD: { // _OLD uses old_timeval32. Needs to be converted struct timeval tv64{}; Result = ::syscall(SYSCALL_DEF(getsockopt), sockfd, level, SO_RCVTIMEO_NEW, &tv64, sizeof(tv64) ); *reinterpret_cast(optval.Ptr) = tv64; break; } case SO_SNDTIMEO_OLD: { // _OLD uses old_timeval32. Needs to be converted struct timeval tv64{}; Result = ::syscall(SYSCALL_DEF(getsockopt), sockfd, level, SO_SNDTIMEO_NEW, &tv64, sizeof(tv64) ); *reinterpret_cast(optval.Ptr) = tv64; break; } // Each optname as a reminder which setting has been manually checked case SO_DEBUG: case SO_REUSEADDR: case SO_TYPE: case SO_ERROR: case SO_DONTROUTE: case SO_BROADCAST: case SO_SNDBUF: case SO_RCVBUF: case SO_SNDBUFFORCE: case SO_RCVBUFFORCE: case SO_KEEPALIVE: case SO_OOBINLINE: case SO_NO_CHECK: case SO_PRIORITY: case SO_LINGER: case SO_BSDCOMPAT: case SO_REUSEPORT: /** * @name These end up differing between {x86,arm} and {powerpc, alpha, sparc, mips, parisc} * @{ */ case SO_PASSCRED: case SO_PEERCRED: case SO_RCVLOWAT: case SO_SNDLOWAT: /** @} */ case SO_SECURITY_AUTHENTICATION: case SO_SECURITY_ENCRYPTION_TRANSPORT: case SO_SECURITY_ENCRYPTION_NETWORK: case SO_ATTACH_FILTER: // Renamed to SO_GET_FILTER on get. Same between 32-bit and 64-bit case SO_DETACH_FILTER: case SO_PEERNAME: case SO_TIMESTAMP_OLD: // Returns int32_t boolean case SO_ACCEPTCONN: case SO_PEERSEC: // Gap 32, 33 case SO_PASSSEC: case SO_TIMESTAMPNS_OLD: // Returns int32_t boolean case SO_MARK: case SO_TIMESTAMPING_OLD: // Returns so_timestamping case SO_PROTOCOL: case SO_DOMAIN: case SO_RXQ_OVFL: case SO_WIFI_STATUS: case SO_PEEK_OFF: case SO_NOFCS: case SO_LOCK_FILTER: case SO_SELECT_ERR_QUEUE: case SO_BUSY_POLL: case SO_MAX_PACING_RATE: case SO_BPF_EXTENSIONS: case SO_INCOMING_CPU: case SO_ATTACH_BPF: case SO_ATTACH_REUSEPORT_CBPF: // Doesn't do anything in get case SO_ATTACH_REUSEPORT_EBPF: case SO_CNX_ADVICE: // Gap 54 (SCM_TIMESTAMPING_OPT_STATS) case SO_MEMINFO: case SO_INCOMING_NAPI_ID: case SO_COOKIE: // Cookie always returns 64-bit even on 32-bit // Gap 58 (SCM_TIMESTAMPING_PKTINFO) case SO_PEERGROUPS: case SO_ZEROCOPY: case SO_TXTIME: case SO_BINDTOIFINDEX: case SO_TIMESTAMP_NEW: case SO_TIMESTAMPNS_NEW: case SO_TIMESTAMPING_NEW: case SO_RCVTIMEO_NEW: case SO_SNDTIMEO_NEW: case SO_DETACH_REUSEPORT_BPF: case SO_PREFER_BUSY_POLL: case SO_BUSY_POLL_BUDGET: case SO_NETNS_COOKIE: // Cookie always returns 64-bit even on 32-bit case SO_BUF_LOCK: case SO_RESERVE_MEM: default: Result = ::syscall(SYSCALL_DEF(getsockopt), sockfd, level, optname, optval, optlen); break; } } else { Result = ::syscall(SYSCALL_DEF(getsockopt), sockfd, level, optname, optval, optlen); } SYSCALL_ERRNO(); } void RegisterSocket(FEX::HLE::SyscallHandler *Handler) { REGISTER_SYSCALL_IMPL_X32(socketcall, [](FEXCore::Core::CpuStateFrame *Frame, uint32_t call, uint32_t *Arguments) -> uint64_t { uint64_t Result{}; switch (call) { case OP_SOCKET: { Result = ::socket(Arguments[0], Arguments[1], Arguments[2]); break; } case OP_BIND: { Result = ::bind(Arguments[0], reinterpret_cast(Arguments[1]), Arguments[2]); break; } case OP_CONNECT: { Result = ::connect(Arguments[0], reinterpret_cast(Arguments[1]), Arguments[2]); break; } case OP_LISTEN: { Result = ::listen(Arguments[0], Arguments[1]); break; } case OP_ACCEPT: { Result = ::accept(Arguments[0], reinterpret_cast(Arguments[1]), reinterpret_cast(Arguments[2])); break; } case OP_GETSOCKNAME: { Result = ::getsockname(Arguments[0], reinterpret_cast(Arguments[1]), reinterpret_cast(Arguments[2])); break; } case OP_GETPEERNAME: { Result = ::getpeername(Arguments[0], reinterpret_cast(Arguments[1]), reinterpret_cast(Arguments[2])); break; } case OP_SOCKETPAIR: { Result = ::socketpair(Arguments[0], Arguments[1], Arguments[2], reinterpret_cast(Arguments[3])); break; } case OP_SEND: { Result = ::send(Arguments[0], reinterpret_cast(Arguments[1]), Arguments[2], Arguments[3]); break; } case OP_RECV: { Result = ::recv(Arguments[0], reinterpret_cast(Arguments[1]), Arguments[2], Arguments[3]); break; } case OP_SENDTO: { Result = ::sendto( Arguments[0], reinterpret_cast(Arguments[1]), Arguments[2], Arguments[3], reinterpret_cast(Arguments[4]), reinterpret_cast(Arguments[5]) ); break; } case OP_RECVFROM: { Result = ::recvfrom( Arguments[0], reinterpret_cast(Arguments[1]), Arguments[2], Arguments[3], reinterpret_cast(Arguments[4]), reinterpret_cast(Arguments[5]) ); break; } case OP_SHUTDOWN: { Result = ::shutdown(Arguments[0], Arguments[1]); break; } case OP_SETSOCKOPT: { return SetSockOpt( Arguments[0], Arguments[1], Arguments[2], Arguments[3], reinterpret_cast(Arguments[4]) ); break; } case OP_GETSOCKOPT: { return GetSockOpt( Arguments[0], Arguments[1], Arguments[2], reinterpret_cast(Arguments[3]), reinterpret_cast(Arguments[4]) ); break; } case OP_SENDMSG: { return SendMsg(Arguments[0], reinterpret_cast(Arguments[1]), Arguments[2]); break; } case OP_RECVMSG: { return RecvMsg(Arguments[0], reinterpret_cast(Arguments[1]), Arguments[2]); break; } case OP_ACCEPT4: { return ::accept4(Arguments[0], reinterpret_cast(Arguments[1]), reinterpret_cast(Arguments[2]), Arguments[3]); break; } case OP_RECVMMSG: { timespec32 *timeout_ts = reinterpret_cast(Arguments[4]); struct timespec tp64{}; struct timespec *timed_ptr{}; if (timeout_ts) { tp64 = *timeout_ts; timed_ptr = &tp64; } uint64_t Result = RecvMMsg(Arguments[0], Arguments[1], Arguments[2], Arguments[3], timed_ptr); if (timeout_ts) { *timeout_ts = tp64; } return Result; break; } case OP_SENDMMSG: { return SendMMsg(Arguments[0], reinterpret_cast(Arguments[1]), Arguments[2], Arguments[3]); break; } default: LOGMAN_MSG_A_FMT("Unsupported socketcall op: {}", call); break; } SYSCALL_ERRNO(); }); REGISTER_SYSCALL_IMPL_X32(sendmsg, [](FEXCore::Core::CpuStateFrame *Frame, int sockfd, const struct msghdr32 *msg, int flags) -> uint64_t { return SendMsg(sockfd, msg, flags); }); REGISTER_SYSCALL_IMPL_X32(sendmmsg, [](FEXCore::Core::CpuStateFrame *Frame, int sockfd, compat_ptr msgvec, uint32_t vlen, int flags) -> uint64_t { return SendMMsg(sockfd, msgvec, vlen, flags); }); REGISTER_SYSCALL_IMPL_X32(recvmmsg, [](FEXCore::Core::CpuStateFrame *Frame, int sockfd, compat_ptr msgvec, uint32_t vlen, int flags, timespec32 *timeout_ts) -> uint64_t { struct timespec tp64{}; struct timespec *timed_ptr{}; if (timeout_ts) { tp64 = *timeout_ts; timed_ptr = &tp64; } uint64_t Result = RecvMMsg(sockfd, msgvec, vlen, flags, timed_ptr); if (timeout_ts) { *timeout_ts = tp64; } return Result; }); REGISTER_SYSCALL_IMPL_X32(recvmmsg_time64, [](FEXCore::Core::CpuStateFrame *Frame, int sockfd, compat_ptr msgvec, uint32_t vlen, int flags, struct timespec *timeout_ts) -> uint64_t { return RecvMMsg(sockfd, msgvec, vlen, flags, timeout_ts); }); REGISTER_SYSCALL_IMPL_X32(recvmsg, [](FEXCore::Core::CpuStateFrame *Frame, int sockfd, struct msghdr32 *msg, int flags) -> uint64_t { return RecvMsg(sockfd, msg, flags); }); REGISTER_SYSCALL_IMPL_X32(setsockopt, [](FEXCore::Core::CpuStateFrame *Frame, int sockfd, int level, int optname, compat_ptr optval, socklen_t optlen) -> uint64_t { return SetSockOpt(sockfd, level, optname, optval, optlen); }); REGISTER_SYSCALL_IMPL_X32(getsockopt, [](FEXCore::Core::CpuStateFrame *Frame, int sockfd, int level, int optname, compat_ptr optval, compat_ptr optlen) -> uint64_t { return GetSockOpt(sockfd, level, optname, optval, optlen); }); } }