Files
FEX-Emu--FEX/Source/Tools/LinuxEmulation/LinuxSyscalls/x32/Socket.cpp
T
Ryan Houdek f2bcc14cda Tools: Moves IRLoader to independent folder
Which requires moving LinuxEmulation to its own independent folder as
well. Since both IRLoader and FEXLoader rely on it.

No functional change, just moves the the code around.
2023-12-18 12:04:28 -08:00

852 lines
28 KiB
C++

// 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 <FEXCore/Utils/LogManager.h>
#include <FEXCore/fextl/vector.h>
#include <alloca.h>
#include <cstdint>
#include <cstring>
#include <memory>
#include <stddef.h>
#include <sys/socket.h>
#include <unistd.h>
ARG_TO_STR(FEX::HLE::x32::compat_ptr<FEX::HLE::x32::mmsghdr_32>, "%lx")
ARG_TO_STR(FEX::HLE::x32::compat_ptr<void>, "%lx")
ARG_TO_STR(FEX::HLE::x32::compat_ptr<uint32_t>, "%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<iovec> 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<struct cmsghdr*>(HostHeader.msg_control);
for (cmsghdr32 *msghdr_guest = reinterpret_cast<cmsghdr32*>(CurrentGuestPtr);
CurrentGuestPtr != 0;
msghdr_guest = reinterpret_cast<cmsghdr32*>(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<void*>(reinterpret_cast<uintptr_t>(CurrentGuestPtr) + msghdr_guest->cmsg_len);
CurrentGuestPtr = reinterpret_cast<void*>((reinterpret_cast<uintptr_t>(CurrentGuestPtr) + 3) & ~3ULL);
if (CurrentGuestPtr >= reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(static_cast<void*>(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<iovec> 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<cmsghdr32*>(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<void*>(reinterpret_cast<uintptr_t>(CurrentGuestPtr) + CurrentGuest->cmsg_len);
CurrentGuestPtr = reinterpret_cast<void*>((reinterpret_cast<uintptr_t>(CurrentGuestPtr) + 3) & ~3ULL);
}
}
}
}
SYSCALL_ERRNO();
}
void ConvertHeaderToHost(fextl::vector<iovec> &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<cmsghdr32*>(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<void*>(reinterpret_cast<uintptr_t>(CurrentGuestPtr) + CurrentGuest->cmsg_len);
CurrentGuestPtr = reinterpret_cast<void*>((reinterpret_cast<uintptr_t>(CurrentGuestPtr) + 3) & ~3ULL);
}
}
}
}
static uint64_t RecvMMsg(int sockfd, compat_ptr<mmsghdr_32> msgvec, uint32_t vlen, int flags, struct timespec *timeout_ts) {
fextl::vector<iovec> Host_iovec;
fextl::vector<struct mmsghdr> 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<mmsghdr_32> msgvec, uint32_t vlen, int flags) {
fextl::vector<iovec> Host_iovec;
fextl::vector<struct mmsghdr> 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<uint8_t> 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<struct cmsghdr*>(msg.msg_control);
for (cmsghdr32 *msghdr_guest = reinterpret_cast<cmsghdr32*>(CurrentGuestPtr);
CurrentGuestPtr != 0;
msghdr_guest = reinterpret_cast<cmsghdr32*>(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<void*>(reinterpret_cast<uintptr_t>(CurrentGuestPtr) + msghdr_guest->cmsg_len);
CurrentGuestPtr = reinterpret_cast<void*>((reinterpret_cast<uintptr_t>(CurrentGuestPtr) + 3) & ~3ULL);
if (CurrentGuestPtr >= reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(static_cast<void*>(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<void> 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<sock_fprog32*>(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<timeval32*>(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<timeval32*>(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<const void*>(optval.Ptr),
optlen
);
break;
}
}
else {
Result = ::syscall(SYSCALL_DEF(setsockopt),
sockfd,
level,
optname,
reinterpret_cast<const void*>(optval.Ptr),
optlen
);
}
SYSCALL_ERRNO();
}
static uint64_t GetSockOpt(int sockfd, int level, int optname, compat_ptr<void> optval, compat_ptr<socklen_t> 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<timeval32*>(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<timeval32*>(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<const struct sockaddr *>(Arguments[1]), Arguments[2]);
break;
}
case OP_CONNECT: {
Result = ::connect(Arguments[0], reinterpret_cast<const struct sockaddr *>(Arguments[1]), Arguments[2]);
break;
}
case OP_LISTEN: {
Result = ::listen(Arguments[0], Arguments[1]);
break;
}
case OP_ACCEPT: {
Result = ::accept(Arguments[0], reinterpret_cast<struct sockaddr *>(Arguments[1]), reinterpret_cast<socklen_t*>(Arguments[2]));
break;
}
case OP_GETSOCKNAME: {
Result = ::getsockname(Arguments[0], reinterpret_cast<struct sockaddr *>(Arguments[1]), reinterpret_cast<socklen_t*>(Arguments[2]));
break;
}
case OP_GETPEERNAME: {
Result = ::getpeername(Arguments[0], reinterpret_cast<struct sockaddr *>(Arguments[1]), reinterpret_cast<socklen_t*>(Arguments[2]));
break;
}
case OP_SOCKETPAIR: {
Result = ::socketpair(Arguments[0], Arguments[1], Arguments[2], reinterpret_cast<int32_t*>(Arguments[3]));
break;
}
case OP_SEND: {
Result = ::send(Arguments[0], reinterpret_cast<const void*>(Arguments[1]), Arguments[2], Arguments[3]);
break;
}
case OP_RECV: {
Result = ::recv(Arguments[0], reinterpret_cast<void*>(Arguments[1]), Arguments[2], Arguments[3]);
break;
}
case OP_SENDTO: {
Result = ::sendto(
Arguments[0],
reinterpret_cast<const void*>(Arguments[1]),
Arguments[2],
Arguments[3],
reinterpret_cast<struct sockaddr *>(Arguments[4]), reinterpret_cast<socklen_t>(Arguments[5])
);
break;
}
case OP_RECVFROM: {
Result = ::recvfrom(
Arguments[0],
reinterpret_cast<void*>(Arguments[1]),
Arguments[2],
Arguments[3],
reinterpret_cast<struct sockaddr *>(Arguments[4]), reinterpret_cast<socklen_t*>(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<socklen_t>(Arguments[4])
);
break;
}
case OP_GETSOCKOPT: {
return GetSockOpt(
Arguments[0],
Arguments[1],
Arguments[2],
reinterpret_cast<void*>(Arguments[3]),
reinterpret_cast<socklen_t*>(Arguments[4])
);
break;
}
case OP_SENDMSG: {
return SendMsg(Arguments[0], reinterpret_cast<const struct msghdr32*>(Arguments[1]), Arguments[2]);
break;
}
case OP_RECVMSG: {
return RecvMsg(Arguments[0], reinterpret_cast<struct msghdr32*>(Arguments[1]), Arguments[2]);
break;
}
case OP_ACCEPT4: {
return ::accept4(Arguments[0], reinterpret_cast<struct sockaddr *>(Arguments[1]), reinterpret_cast<socklen_t*>(Arguments[2]), Arguments[3]);
break;
}
case OP_RECVMMSG: {
timespec32 *timeout_ts = reinterpret_cast<timespec32 *>(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<mmsghdr_32*>(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<mmsghdr_32> 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<mmsghdr_32> 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<mmsghdr_32> 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<void> 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<void> optval, compat_ptr<socklen_t> optlen) -> uint64_t {
return GetSockOpt(sockfd, level, optname, optval, optlen);
});
}
}