Files
FEX-Emu--FEX/Source/Tools/LinuxEmulation/LinuxSyscalls/x32/FD.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

969 lines
31 KiB
C++

// SPDX-License-Identifier: MIT
/*
$info$
tags: LinuxSyscalls|syscalls-x86-32
$end_info$
*/
#include "LinuxSyscalls/Syscalls.h"
#include "LinuxSyscalls/x32/IoctlEmulation.h"
#include "LinuxSyscalls/x32/Syscalls.h"
#include "LinuxSyscalls/x32/SyscallsEnum.h"
#include "LinuxSyscalls/x32/Types.h"
#include "LinuxSyscalls/x64/Syscalls.h"
#include <FEXCore/Debug/InternalThreadState.h>
#include <FEXCore/Utils/LogManager.h>
#include <FEXCore/Utils/MathUtils.h>
#include <FEXCore/fextl/vector.h>
#include <algorithm>
#include <cstdint>
#include <fcntl.h>
#include <limits>
#include <poll.h>
#include <signal.h>
#include <stddef.h>
#include <string.h>
#include <sys/select.h>
#include <sys/sendfile.h>
#include <sys/stat.h>
#include <sys/statfs.h>
#include <sys/time.h>
#include <sys/timerfd.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <syscall.h>
#include <time.h>
#include <type_traits>
#include <unistd.h>
ARG_TO_STR(FEX::HLE::x32::compat_ptr<FEX::HLE::x32::sigset_argpack32>, "%lx")
namespace FEXCore::Core {
struct CpuStateFrame;
}
namespace FEX::HLE::x32 {
// Used to ensure no bogus values are passed into readv/writev family syscalls.
// This is mainly to sanitize vector sizing. It's fine for the bogus value
// itself to pass into the syscall, since the kernel will handle it.
static constexpr int SanitizeIOCount(int count) {
return std::max(0, count);
}
#ifdef _M_X86_64
uint32_t ioctl_32(FEXCore::Core::CpuStateFrame*, int fd, uint32_t cmd, uint32_t args) {
uint32_t Result{};
__asm volatile("int $0x80;"
: "=a" (Result)
: "a" (SYSCALL_x86_ioctl)
, "b" (fd)
, "c" (cmd)
, "d" (args)
: "memory");
return Result;
}
#endif
auto fcntlHandler = [](FEXCore::Core::CpuStateFrame *Frame, int fd, int cmd, uint64_t arg) -> uint64_t {
// fcntl64 struct directly matches the 64bit fcntl op
// cmd just needs to be fixed up
// These are redefined to be their non-64bit tagged value on x86-64
constexpr int OP_GETLK64_32 = 12;
constexpr int OP_SETLK64_32 = 13;
constexpr int OP_SETLKW64_32 = 14;
void *lock_arg = (void*)arg;
struct flock tmp{};
int old_cmd = cmd;
switch (old_cmd) {
case OP_GETLK64_32: {
cmd = F_GETLK;
lock_arg = (void*)&tmp;
tmp = *reinterpret_cast<flock64_32*>(arg);
break;
}
case OP_SETLK64_32: {
cmd = F_SETLK;
lock_arg = (void*)&tmp;
tmp = *reinterpret_cast<flock64_32*>(arg);
break;
}
case OP_SETLKW64_32: {
cmd = F_SETLKW;
lock_arg = (void*)&tmp;
tmp = *reinterpret_cast<flock64_32*>(arg);
break;
}
case F_OFD_SETLK:
case F_OFD_GETLK:
case F_OFD_SETLKW: {
lock_arg = (void*)&tmp;
tmp = *reinterpret_cast<flock64_32*>(arg);
break;
}
case F_GETLK:
case F_SETLK:
case F_SETLKW: {
lock_arg = (void*)&tmp;
tmp = *reinterpret_cast<flock_32*>(arg);
break;
}
case F_SETFL:
lock_arg = reinterpret_cast<void*>(FEX::HLE::RemapFromX86Flags(arg));
break;
// Maps directly
case F_DUPFD:
case F_DUPFD_CLOEXEC:
case F_GETFD:
case F_SETFD:
case F_GETFL:
break;
default:
LOGMAN_MSG_A_FMT("Unhandled fcntl64: 0x{:x}", cmd);
break;
}
uint64_t Result = ::fcntl(fd, cmd, lock_arg);
if (Result != -1) {
switch (old_cmd) {
case OP_GETLK64_32: {
*reinterpret_cast<flock64_32*>(arg) = tmp;
break;
}
case F_OFD_GETLK: {
*reinterpret_cast<flock64_32*>(arg) = tmp;
break;
}
case F_GETLK: {
*reinterpret_cast<flock_32*>(arg) = tmp;
break;
}
break;
case F_DUPFD:
case F_DUPFD_CLOEXEC:
FEX::HLE::x32::CheckAndAddFDDuplication(fd, Result);
break;
case F_GETFL: {
Result = FEX::HLE::RemapToX86Flags(Result);
break;
}
default: break;
}
}
SYSCALL_ERRNO();
};
auto selectHandler = [](FEXCore::Core::CpuStateFrame *Frame, int nfds, fd_set32 *readfds, fd_set32 *writefds, fd_set32 *exceptfds, struct timeval32 *timeout) -> uint64_t {
struct timeval tp64{};
if (timeout) {
tp64 = *timeout;
}
fd_set Host_readfds;
fd_set Host_writefds;
fd_set Host_exceptfds;
FD_ZERO(&Host_readfds);
FD_ZERO(&Host_writefds);
FD_ZERO(&Host_exceptfds);
// Round up to the full 32bit word
uint32_t NumWords = FEXCore::AlignUp(nfds, 32) / 4;
if (readfds) {
for (int i = 0; i < NumWords; ++i) {
uint32_t FD = readfds[i];
int32_t Rem = nfds - (i * 32);
for (int j = 0; j < 32 && j < Rem; ++j) {
if ((FD >> j) & 1) {
FD_SET(i * 32 + j, &Host_readfds);
}
}
}
}
if (writefds) {
for (int i = 0; i < NumWords; ++i) {
uint32_t FD = writefds[i];
int32_t Rem = nfds - (i * 32);
for (int j = 0; j < 32 && j < Rem; ++j) {
if ((FD >> j) & 1) {
FD_SET(i * 32 + j, &Host_writefds);
}
}
}
}
if (exceptfds) {
for (int i = 0; i < NumWords; ++i) {
uint32_t FD = exceptfds[i];
int32_t Rem = nfds - (i * 32);
for (int j = 0; j < 32 && j < Rem; ++j) {
if ((FD >> j) & 1) {
FD_SET(i * 32 + j, &Host_exceptfds);
}
}
}
}
uint64_t Result = ::select(nfds,
readfds ? &Host_readfds : nullptr,
writefds ? &Host_writefds : nullptr,
exceptfds ? &Host_exceptfds : nullptr,
timeout ? &tp64 : nullptr);
if (readfds) {
for (int i = 0; i < nfds; ++i) {
if (FD_ISSET(i, &Host_readfds)) {
readfds[i/32] |= 1 << (i & 31);
} else {
readfds[i/32] &= ~(1 << (i & 31));
}
}
}
if (writefds) {
for (int i = 0; i < nfds; ++i) {
if (FD_ISSET(i, &Host_writefds)) {
writefds[i/32] |= 1 << (i & 31);
} else {
writefds[i/32] &= ~(1 << (i & 31));
}
}
}
if (exceptfds) {
for (int i = 0; i < nfds; ++i) {
if (FD_ISSET(i, &Host_exceptfds)) {
exceptfds[i/32] |= 1 << (i & 31);
} else {
exceptfds[i/32] &= ~(1 << (i & 31));
}
}
}
if (timeout) {
*timeout = tp64;
}
SYSCALL_ERRNO();
};
void RegisterFD(FEX::HLE::SyscallHandler *Handler) {
REGISTER_SYSCALL_IMPL_X32_PASS(poll, [](FEXCore::Core::CpuStateFrame *Frame, struct pollfd *fds, nfds_t nfds, int timeout) -> uint64_t {
uint64_t Result = ::poll(fds, nfds, timeout);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(ppoll, [](FEXCore::Core::CpuStateFrame *Frame, struct pollfd *fds, nfds_t nfds, timespec32 *timeout_ts, const uint64_t *sigmask, size_t sigsetsize) -> uint64_t {
// sigsetsize is unused here since it is currently a constant and not exposed through glibc
struct timespec tp64{};
struct timespec *timed_ptr{};
if (timeout_ts) {
tp64 = *timeout_ts;
timed_ptr = &tp64;
}
uint64_t Result = ::syscall(SYSCALL_DEF(ppoll),
fds,
nfds,
timed_ptr,
sigmask,
sigsetsize);
if (timeout_ts) {
*timeout_ts = tp64;
}
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(ppoll_time64, ppoll, [](FEXCore::Core::CpuStateFrame *Frame, struct pollfd *fds, nfds_t nfds, struct timespec *timeout_ts, const uint64_t *sigmask, size_t sigsetsize) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(ppoll),
fds,
nfds,
timeout_ts,
sigmask,
sigsetsize);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(_llseek, [](FEXCore::Core::CpuStateFrame *Frame, uint32_t fd, uint32_t offset_high, uint32_t offset_low, loff_t *result, uint32_t whence) -> uint64_t {
uint64_t Offset = offset_high;
Offset <<= 32;
Offset |= offset_low;
uint64_t Result = lseek(fd, Offset, whence);
if (Result != -1) {
*result = Result;
}
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(readv, [](FEXCore::Core::CpuStateFrame *Frame, int fd, const struct iovec32 *iov, int iovcnt) -> uint64_t {
fextl::vector<iovec> Host_iovec(iov, iov + SanitizeIOCount(iovcnt));
uint64_t Result = ::readv(fd, Host_iovec.data(), iovcnt);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(writev, [](FEXCore::Core::CpuStateFrame *Frame, int fd, const struct iovec32 *iov, int iovcnt) -> uint64_t {
fextl::vector<iovec> Host_iovec(iov, iov + SanitizeIOCount(iovcnt));
uint64_t Result = ::writev(fd, Host_iovec.data(), iovcnt);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(chown32, chown, [](FEXCore::Core::CpuStateFrame *Frame, const char *pathname, uid_t owner, gid_t group) -> uint64_t {
uint64_t Result = ::chown(pathname, owner, group);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(fchown32, fchown, [](FEXCore::Core::CpuStateFrame *Frame, int fd, uid_t owner, gid_t group) -> uint64_t {
uint64_t Result = ::fchown(fd, owner, group);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(lchown32, lchown, [](FEXCore::Core::CpuStateFrame *Frame, const char *pathname, uid_t owner, gid_t group) -> uint64_t {
uint64_t Result = ::lchown(pathname, owner, group);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(oldstat, [](FEXCore::Core::CpuStateFrame *Frame, const char *pathname, oldstat32 *buf) -> uint64_t {
struct stat host_stat;
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Stat(pathname, &host_stat);
if (Result != -1) {
if (host_stat.st_ino > std::numeric_limits<decltype(buf->st_ino)>::max()) {
return -EOVERFLOW;
}
if (host_stat.st_nlink > std::numeric_limits<decltype(buf->st_nlink)>::max()) {
return -EOVERFLOW;
}
*buf = host_stat;
}
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(oldfstat, [](FEXCore::Core::CpuStateFrame *Frame, int fd, oldstat32 *buf) -> uint64_t {
struct stat host_stat;
uint64_t Result = ::fstat(fd, &host_stat);
if (Result != -1) {
if (host_stat.st_ino > std::numeric_limits<decltype(buf->st_ino)>::max()) {
return -EOVERFLOW;
}
if (host_stat.st_nlink > std::numeric_limits<decltype(buf->st_nlink)>::max()) {
return -EOVERFLOW;
}
*buf = host_stat;
}
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(oldlstat, [](FEXCore::Core::CpuStateFrame *Frame, const char *path, oldstat32 *buf) -> uint64_t {
struct stat host_stat;
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Lstat(path, &host_stat);
if (Result != -1) {
if (host_stat.st_ino > std::numeric_limits<decltype(buf->st_ino)>::max()) {
return -EOVERFLOW;
}
if (host_stat.st_nlink > std::numeric_limits<decltype(buf->st_nlink)>::max()) {
return -EOVERFLOW;
}
*buf = host_stat;
}
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(stat, [](FEXCore::Core::CpuStateFrame *Frame, const char *pathname, stat32 *buf) -> uint64_t {
struct stat host_stat;
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Stat(pathname, &host_stat);
if (Result != -1) {
*buf = host_stat;
}
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(fstat, [](FEXCore::Core::CpuStateFrame *Frame, int fd, stat32 *buf) -> uint64_t {
struct stat host_stat;
uint64_t Result = ::fstat(fd, &host_stat);
if (Result != -1) {
*buf = host_stat;
}
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(lstat, [](FEXCore::Core::CpuStateFrame *Frame, const char *path, stat32 *buf) -> uint64_t {
struct stat host_stat;
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Lstat(path, &host_stat);
if (Result != -1) {
*buf = host_stat;
}
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(stat64, [](FEXCore::Core::CpuStateFrame *Frame, const char *pathname, stat64_32 *buf) -> uint64_t {
struct stat host_stat;
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Stat(pathname, &host_stat);
if (Result != -1) {
*buf = host_stat;
}
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(lstat64, [](FEXCore::Core::CpuStateFrame *Frame, const char *path, stat64_32 *buf) -> uint64_t {
struct stat host_stat;
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Lstat(path, &host_stat);
if (Result != -1) {
*buf = host_stat;
}
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(fstat64, [](FEXCore::Core::CpuStateFrame *Frame, int fd, stat64_32 *buf) -> uint64_t {
struct stat64 host_stat;
uint64_t Result = ::fstat64(fd, &host_stat);
if (Result != -1) {
*buf = host_stat;
}
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(statfs, [](FEXCore::Core::CpuStateFrame *Frame, const char *path, statfs32_32 *buf) -> uint64_t {
struct statfs host_stat;
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Statfs(path, &host_stat);
if (Result != -1) {
*buf = host_stat;
}
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(fstatfs, [](FEXCore::Core::CpuStateFrame *Frame, int fd, statfs32_32 *buf) -> uint64_t {
struct statfs host_stat;
uint64_t Result = ::fstatfs(fd, &host_stat);
if (Result != -1) {
*buf = host_stat;
}
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(fstatfs64, [](FEXCore::Core::CpuStateFrame *Frame, int fd, size_t sz, struct statfs64_32 *buf) -> uint64_t {
LOGMAN_THROW_AA_FMT(sz == sizeof(struct statfs64_32), "This needs to match");
struct statfs64 host_stat;
uint64_t Result = ::fstatfs64(fd, &host_stat);
if (Result != -1) {
*buf = host_stat;
}
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(statfs64, [](FEXCore::Core::CpuStateFrame *Frame, const char *path, size_t sz, struct statfs64_32 *buf) -> uint64_t {
LOGMAN_THROW_AA_FMT(sz == sizeof(struct statfs64_32), "This needs to match");
struct statfs host_stat;
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Statfs(path, &host_stat);
if (Result != -1) {
*buf = host_stat;
}
SYSCALL_ERRNO();
});
// x86 32-bit fcntl syscall has a historical quirk that it uses the same handler as fcntl64
// This is in direct opposition to all other 32-bit architectures that use the compat_fcntl handler
// This quirk goes back to the start of the Linux 2.6.12-rc2 git history. Seeing history before
// that point to see when this quirk happened would be difficult
//
// For more reference, the compat_fcntl handler blocks a few commands:
// - F_GETLK64
// - F_SETLK64
// - F_SETLKW64
// - F_OFD_GETLK
// - F_OFD_SETLK
// - F_OFD_SETLKW
REGISTER_SYSCALL_IMPL_X32(fcntl, fcntlHandler);
REGISTER_SYSCALL_IMPL_X32(fcntl64, fcntlHandler);
REGISTER_SYSCALL_IMPL_X32(dup, [](FEXCore::Core::CpuStateFrame *Frame, int oldfd) -> uint64_t {
uint64_t Result = ::dup(oldfd);
if (Result != -1) {
CheckAndAddFDDuplication(oldfd, Result);
}
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(dup2, [](FEXCore::Core::CpuStateFrame *Frame, int oldfd, int newfd) -> uint64_t {
uint64_t Result = ::dup2(oldfd, newfd);
if (Result != -1) {
CheckAndAddFDDuplication(oldfd, newfd);
}
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(preadv, [](FEXCore::Core::CpuStateFrame *Frame,
int fd,
const struct iovec32 *iov,
uint32_t iovcnt,
uint32_t pos_low,
uint32_t pos_high) -> uint64_t {
fextl::vector<iovec> Host_iovec(iov, iov + SanitizeIOCount(iovcnt));
uint64_t Result = ::syscall(SYSCALL_DEF(preadv), fd, Host_iovec.data(), iovcnt, pos_low, pos_high);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(pwritev, [](FEXCore::Core::CpuStateFrame *Frame,
int fd,
const struct iovec32 *iov,
uint32_t iovcnt,
uint32_t pos_low,
uint32_t pos_high) -> uint64_t {
fextl::vector<iovec> Host_iovec(iov, iov + SanitizeIOCount(iovcnt));
uint64_t Result = ::syscall(SYSCALL_DEF(pwritev), fd, Host_iovec.data(), iovcnt, pos_low, pos_high);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(process_vm_readv, [](FEXCore::Core::CpuStateFrame *Frame, pid_t pid, const struct iovec32 *local_iov, unsigned long liovcnt, const struct iovec32 *remote_iov, unsigned long riovcnt, unsigned long flags) -> uint64_t {
fextl::vector<iovec> Host_local_iovec(local_iov, local_iov + SanitizeIOCount(liovcnt));
fextl::vector<iovec> Host_remote_iovec(remote_iov, remote_iov + SanitizeIOCount(riovcnt));
uint64_t Result = ::process_vm_readv(pid, Host_local_iovec.data(), liovcnt, Host_remote_iovec.data(), riovcnt, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(process_vm_writev, [](FEXCore::Core::CpuStateFrame *Frame, pid_t pid, const struct iovec32 *local_iov, unsigned long liovcnt, const struct iovec32 *remote_iov, unsigned long riovcnt, unsigned long flags) -> uint64_t {
fextl::vector<iovec> Host_local_iovec(local_iov, local_iov + SanitizeIOCount(liovcnt));
fextl::vector<iovec> Host_remote_iovec(remote_iov, remote_iov + SanitizeIOCount(riovcnt));
uint64_t Result = ::process_vm_writev(pid, Host_local_iovec.data(), liovcnt, Host_remote_iovec.data(), riovcnt, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(preadv2, [](FEXCore::Core::CpuStateFrame *Frame,
int fd,
const struct iovec32 *iov,
uint32_t iovcnt,
uint32_t pos_low,
uint32_t pos_high,
int flags) -> uint64_t {
fextl::vector<iovec> Host_iovec(iov, iov + SanitizeIOCount(iovcnt));
uint64_t Result = ::syscall(SYSCALL_DEF(preadv2), fd, Host_iovec.data(), iovcnt, pos_low, pos_high, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(pwritev2, [](FEXCore::Core::CpuStateFrame *Frame,
int fd,
const struct iovec32 *iov,
uint32_t iovcnt,
uint32_t pos_low,
uint32_t pos_high,
int flags) -> uint64_t {
fextl::vector<iovec> Host_iovec(iov, iov + SanitizeIOCount(iovcnt));
uint64_t Result = ::syscall(SYSCALL_DEF(pwritev2), fd, Host_iovec.data(),iovcnt, pos_low, pos_high, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(fstatat_64, [](FEXCore::Core::CpuStateFrame *Frame, int dirfd, const char *pathname, stat64_32 *buf, int flag) -> uint64_t {
struct stat64 host_stat;
uint64_t Result = FEX::HLE::_SyscallHandler->FM.NewFSStatAt64(dirfd, pathname, &host_stat, flag);
if (Result != -1) {
*buf = host_stat;
}
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(ioctl, ioctl32);
REGISTER_SYSCALL_IMPL_X32(getdents, [](FEXCore::Core::CpuStateFrame *Frame, int fd, void *dirp, uint32_t count) -> uint64_t {
return GetDentsEmulation<true>(fd, reinterpret_cast<FEX::HLE::x32::linux_dirent_32*>(dirp), count);
});
REGISTER_SYSCALL_IMPL_X32(getdents64, [](FEXCore::Core::CpuStateFrame *Frame, int fd, void *dirp, uint32_t count) -> uint64_t {
uint64_t Result = ::syscall(SYSCALL_DEF(getdents64),
static_cast<uint64_t>(fd),
dirp,
static_cast<uint64_t>(count));
if (Result != -1) {
// Walk each offset
// if we are passing the full d_off to the 32bit application then it seems to break things?
for (size_t i = 0, num = 0; i < Result; ++num) {
linux_dirent_64 *Incoming = (linux_dirent_64*)(reinterpret_cast<uint64_t>(dirp) + i);
Incoming->d_off = num;
i += Incoming->d_reclen;
}
}
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(select, [](FEXCore::Core::CpuStateFrame *Frame, compat_select_args *arg) -> uint64_t {
return selectHandler(Frame, arg->nfds, arg->readfds, arg->writefds, arg->exceptfds, arg->timeout);
});
REGISTER_SYSCALL_IMPL_X32(_newselect, selectHandler);
REGISTER_SYSCALL_IMPL_X32(pselect6, [](FEXCore::Core::CpuStateFrame *Frame, int nfds, fd_set32 *readfds, fd_set32 *writefds, fd_set32 *exceptfds, timespec32 *timeout, compat_ptr<sigset_argpack32> sigmaskpack) -> uint64_t {
struct timespec tp64{};
if (timeout) {
tp64 = *timeout;
}
fd_set Host_readfds;
fd_set Host_writefds;
fd_set Host_exceptfds;
sigset_t HostSet{};
FD_ZERO(&Host_readfds);
FD_ZERO(&Host_writefds);
FD_ZERO(&Host_exceptfds);
sigemptyset(&HostSet);
// Round up to the full 32bit word
uint32_t NumWords = FEXCore::AlignUp(nfds, 32) / 4;
if (readfds) {
for (int i = 0; i < NumWords; ++i) {
uint32_t FD = readfds[i];
int32_t Rem = nfds - (i * 32);
for (int j = 0; j < 32 && j < Rem; ++j) {
if ((FD >> j) & 1) {
FD_SET(i * 32 + j, &Host_readfds);
}
}
}
}
if (writefds) {
for (int i = 0; i < NumWords; ++i) {
uint32_t FD = writefds[i];
int32_t Rem = nfds - (i * 32);
for (int j = 0; j < 32 && j < Rem; ++j) {
if ((FD >> j) & 1) {
FD_SET(i * 32 + j, &Host_writefds);
}
}
}
}
if (exceptfds) {
for (int i = 0; i < NumWords; ++i) {
uint32_t FD = exceptfds[i];
int32_t Rem = nfds - (i * 32);
for (int j = 0; j < 32 && j < Rem; ++j) {
if ((FD >> j) & 1) {
FD_SET(i * 32 + j, &Host_exceptfds);
}
}
}
}
if (sigmaskpack && sigmaskpack->sigset) {
uint64_t *sigmask = sigmaskpack->sigset;
size_t sigsetsize = sigmaskpack->size;
for (int32_t i = 0; i < (sigsetsize * 8); ++i) {
if (*sigmask & (1ULL << i)) {
sigaddset(&HostSet, i + 1);
}
}
}
uint64_t Result = ::pselect(nfds,
readfds ? &Host_readfds : nullptr,
writefds ? &Host_writefds : nullptr,
exceptfds ? &Host_exceptfds : nullptr,
timeout ? &tp64 : nullptr,
&HostSet);
if (readfds) {
for (int i = 0; i < nfds; ++i) {
if (FD_ISSET(i, &Host_readfds)) {
readfds[i/32] |= 1 << (i & 31);
} else {
readfds[i/32] &= ~(1 << (i & 31));
}
}
}
if (writefds) {
for (int i = 0; i < nfds; ++i) {
if (FD_ISSET(i, &Host_writefds)) {
writefds[i/32] |= 1 << (i & 31);
} else {
writefds[i/32] &= ~(1 << (i & 31));
}
}
}
if (exceptfds) {
for (int i = 0; i < nfds; ++i) {
if (FD_ISSET(i, &Host_exceptfds)) {
exceptfds[i/32] |= 1 << (i & 31);
} else {
exceptfds[i/32] &= ~(1 << (i & 31));
}
}
}
if (timeout) {
*timeout = tp64;
}
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(fadvise64_64, [](FEXCore::Core::CpuStateFrame *Frame, int32_t fd, uint32_t offset_low, uint32_t offset_high, uint32_t len_low, uint32_t len_high, int advice) -> uint64_t {
uint64_t Offset = offset_high;
Offset <<= 32;
Offset |= offset_low;
uint64_t Len = len_high;
Len <<= 32;
Len |= len_low;
uint64_t Result = ::posix_fadvise64(fd, Offset, Len, advice);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(timerfd_settime64, timerfd_settime, [](FEXCore::Core::CpuStateFrame *Frame, int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value) -> uint64_t {
uint64_t Result = ::timerfd_settime(fd, flags, new_value, old_value);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(timerfd_gettime64, timerfd_gettime, [](FEXCore::Core::CpuStateFrame *Frame, int fd, struct itimerspec *curr_value) -> uint64_t {
uint64_t Result = ::timerfd_gettime(fd, curr_value);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(timerfd_settime, [](FEXCore::Core::CpuStateFrame *Frame,
int fd,
int flags,
const FEX::HLE::x32::old_itimerspec32 *new_value,
FEX::HLE::x32::old_itimerspec32 *old_value) -> uint64_t {
struct itimerspec new_value_host{};
struct itimerspec old_value_host{};
struct itimerspec *old_value_host_p{};
new_value_host = *new_value;
if (old_value) {
old_value_host_p = &old_value_host;
}
// Flags don't need remapped
uint64_t Result = ::timerfd_settime(fd, flags, &new_value_host, old_value_host_p);
if (Result != -1 && old_value) {
*old_value = old_value_host;
}
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(timerfd_gettime, [](FEXCore::Core::CpuStateFrame *Frame, int fd, FEX::HLE::x32::old_itimerspec32 *curr_value) -> uint64_t {
struct itimerspec Host{};
uint64_t Result = ::timerfd_gettime(fd, &Host);
if (Result != -1) {
*curr_value = Host;
}
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(pselect6_time64, [](FEXCore::Core::CpuStateFrame *Frame, int nfds, fd_set32 *readfds, fd_set32 *writefds, fd_set32 *exceptfds, struct timespec *timeout, compat_ptr<sigset_argpack32> sigmaskpack) -> uint64_t {
fd_set Host_readfds;
fd_set Host_writefds;
fd_set Host_exceptfds;
sigset_t HostSet{};
FD_ZERO(&Host_readfds);
FD_ZERO(&Host_writefds);
FD_ZERO(&Host_exceptfds);
sigemptyset(&HostSet);
// Round up to the full 32bit word
uint32_t NumWords = FEXCore::AlignUp(nfds, 32) / 4;
if (readfds) {
for (int i = 0; i < NumWords; ++i) {
uint32_t FD = readfds[i];
int32_t Rem = nfds - (i * 32);
for (int j = 0; j < 32 && j < Rem; ++j) {
if ((FD >> j) & 1) {
FD_SET(i * 32 + j, &Host_readfds);
}
}
}
}
if (writefds) {
for (int i = 0; i < NumWords; ++i) {
uint32_t FD = writefds[i];
int32_t Rem = nfds - (i * 32);
for (int j = 0; j < 32 && j < Rem; ++j) {
if ((FD >> j) & 1) {
FD_SET(i * 32 + j, &Host_writefds);
}
}
}
}
if (exceptfds) {
for (int i = 0; i < NumWords; ++i) {
uint32_t FD = exceptfds[i];
int32_t Rem = nfds - (i * 32);
for (int j = 0; j < 32 && j < Rem; ++j) {
if ((FD >> j) & 1) {
FD_SET(i * 32 + j, &Host_exceptfds);
}
}
}
}
if (sigmaskpack && sigmaskpack->sigset) {
uint64_t *sigmask = sigmaskpack->sigset;
size_t sigsetsize = sigmaskpack->size;
for (int32_t i = 0; i < (sigsetsize * 8); ++i) {
if (*sigmask & (1ULL << i)) {
sigaddset(&HostSet, i + 1);
}
}
}
uint64_t Result = ::pselect(nfds,
readfds ? &Host_readfds : nullptr,
writefds ? &Host_writefds : nullptr,
exceptfds ? &Host_exceptfds : nullptr,
timeout,
&HostSet);
if (readfds) {
for (int i = 0; i < nfds; ++i) {
if (FD_ISSET(i, &Host_readfds)) {
readfds[i/32] |= 1 << (i & 31);
} else {
readfds[i/32] &= ~(1 << (i & 31));
}
}
}
if (writefds) {
for (int i = 0; i < nfds; ++i) {
if (FD_ISSET(i, &Host_writefds)) {
writefds[i/32] |= 1 << (i & 31);
} else {
writefds[i/32] &= ~(1 << (i & 31));
}
}
}
if (exceptfds) {
for (int i = 0; i < nfds; ++i) {
if (FD_ISSET(i, &Host_exceptfds)) {
exceptfds[i/32] |= 1 << (i & 31);
} else {
exceptfds[i/32] &= ~(1 << (i & 31));
}
}
}
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(sendfile, [](FEXCore::Core::CpuStateFrame *Frame, int out_fd, int in_fd, compat_off_t *offset, size_t count) -> uint64_t {
off_t Local{};
off_t *Local_p{};
if (offset) {
Local_p = &Local;
Local = *offset;
}
uint64_t Result = ::sendfile(out_fd, in_fd, Local_p, count);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32_PASS_MANUAL(sendfile64, sendfile, [](FEXCore::Core::CpuStateFrame *Frame, int out_fd, int in_fd, off_t *offset, compat_size_t count) -> uint64_t {
// Linux definition for this is a bit confusing
// Defines offset as compat_loff_t* but loads loff_t worth of data
// count is defined as compat_size_t still
uint64_t Result = ::sendfile(out_fd, in_fd, offset, count);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(pread_64, [](FEXCore::Core::CpuStateFrame *Frame, int fd, void *buf, uint32_t count, uint32_t offset_low, uint32_t offset_high) -> uint64_t {
uint64_t Offset = offset_high;
Offset <<= 32;
Offset |= offset_low;
uint64_t Result = ::pread64(fd, buf, count, Offset);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(pwrite_64, [](FEXCore::Core::CpuStateFrame *Frame, int fd, void *buf, uint32_t count, uint32_t offset_low, uint32_t offset_high) -> uint64_t {
uint64_t Offset = offset_high;
Offset <<= 32;
Offset |= offset_low;
uint64_t Result = ::pwrite64(fd, buf, count, Offset);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(readahead, [](FEXCore::Core::CpuStateFrame *Frame, int fd, uint32_t offset_low, uint64_t offset_high, size_t count) -> uint64_t {
uint64_t Offset = offset_high;
Offset <<= 32;
Offset |= offset_low;
uint64_t Result = ::readahead(fd, Offset, count);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(sync_file_range, [](FEXCore::Core::CpuStateFrame *Frame,
int fd,
uint32_t offset_low,
uint32_t offset_high,
uint32_t len_low,
uint32_t len_high,
unsigned int flags) -> uint64_t {
// Flags don't need remapped
uint64_t Offset = offset_high;
Offset <<= 32;
Offset |= offset_low;
uint64_t Len = len_high;
Len <<= 32;
Len |= len_low;
uint64_t Result = ::syscall(SYSCALL_DEF(sync_file_range), fd, Offset, Len, flags);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(fallocate, [](FEXCore::Core::CpuStateFrame *Frame,
int fd,
int mode,
uint32_t offset_low,
uint32_t offset_high,
uint32_t len_low,
uint32_t len_high) -> uint64_t {
uint64_t Offset = offset_high;
Offset <<= 32;
Offset |= offset_low;
uint64_t Len = len_high;
Len <<= 32;
Len |= len_low;
uint64_t Result = ::fallocate(fd, mode, Offset, Len);
SYSCALL_ERRNO();
});
REGISTER_SYSCALL_IMPL_X32(vmsplice, [](FEXCore::Core::CpuStateFrame *Frame, int fd, const struct iovec32 *iov, unsigned long nr_segs, unsigned int flags) -> uint64_t {
fextl::vector<iovec> Host_iovec(iov, iov + nr_segs);
uint64_t Result = ::vmsplice(fd, Host_iovec.data(), nr_segs, flags);
SYSCALL_ERRNO();
});
}
}