Files
FEX-Emu--FEX/Source/Tests/LinuxSyscalls/x32/Socket.cpp
T
Azkali 75988b2ae5 Improve compatibility with older uapi kernel headers
Following up the work previously done in 2079f6b3c7.
Adding more defines for older Linux uapi headers missing some defines.
2022-02-24 09:49:56 +01:00

821 lines
27 KiB
C++

/*
$info$
tags: LinuxSyscalls|syscalls-x86-32
$end_info$
*/
#include "Tests/LinuxSyscalls/Syscalls.h"
#include "Tests/LinuxSyscalls/x32/Syscalls.h"
#include "Tests/LinuxSyscalls/x32/Types.h"
#include "Tests/LinuxSyscalls/x64/Syscalls.h"
#include <FEXCore/Utils/LogManager.h>
#include <alloca.h>
#include <cstdint>
#include <cstring>
#include <memory>
#include <stddef.h>
#include <sys/socket.h>
#include <unistd.h>
#include <vector>
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{};
std::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{};
std::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(std::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) {
std::vector<iovec> Host_iovec;
std::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 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() {
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;
}
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 {
std::vector<iovec> Host_iovec;
std::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);
}
}
std::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();
});
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);
});
}
}