From 2d342d4662a22e363f4d092b47825f7cf5885a6f Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Thu, 27 May 2021 17:21:55 -0700 Subject: [PATCH 01/13] Handle user provided siginfo_t with user signal If the guest has sent a signal and the si_code is SI_USER then we need to pass that siginfo_t through without touching it. User could be sticking whatever they want in to that struct --- .../Interface/Core/Dispatcher/Dispatcher.cpp | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/External/FEXCore/Source/Interface/Core/Dispatcher/Dispatcher.cpp b/External/FEXCore/Source/Interface/Core/Dispatcher/Dispatcher.cpp index cdb9aa005..492114fc0 100644 --- a/External/FEXCore/Source/Interface/Core/Dispatcher/Dispatcher.cpp +++ b/External/FEXCore/Source/Interface/Core/Dispatcher/Dispatcher.cpp @@ -175,16 +175,23 @@ bool Dispatcher::HandleGuestSignal(int Signal, void *info, void *ucontext, Guest // siginfo_t siginfo_t *HostSigInfo = reinterpret_cast(info); - guest_siginfo->si_signo = Signal; - switch (Signal) { - case SIGSEGV: - case SIGBUS: - guest_siginfo->si_code = HostSigInfo->si_code; - guest_siginfo->si_errno = HostSigInfo->si_errno; - // Macro expansion to get the si_addr - guest_siginfo->si_addr = HostSigInfo->si_addr; - break; - default: LogMan::Msg::D("Unhandled siginfo_t signal: %d", Signal); break; + if (HostSigInfo->si_code == SI_USER) { + // If the signal was a user signal then we need to pass this struct through unaltered + // Guest might be doing something with it + *guest_siginfo = *HostSigInfo; + } + else { + guest_siginfo->si_signo = Signal; + switch (Signal) { + case SIGSEGV: + case SIGBUS: + guest_siginfo->si_code = HostSigInfo->si_code; + guest_siginfo->si_errno = HostSigInfo->si_errno; + // Macro expansion to get the si_addr + guest_siginfo->si_addr = HostSigInfo->si_addr; + break; + default: LogMan::Msg::D("Unhandled siginfo_t signal: %d", Signal); break; + } } Frame->State.gregs[X86State::REG_RSI] = SigInfoLocation; From 7a64bba8c45e20a77a99328a48b6163764e8deba Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Thu, 27 May 2021 17:23:29 -0700 Subject: [PATCH 02/13] Removes check for ThreadState being standard layout Latest clang and libstdc++ makes unique_ptr not be standard layout. Results in a compile error --- External/FEXCore/include/FEXCore/Debug/InternalThreadState.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/External/FEXCore/include/FEXCore/Debug/InternalThreadState.h b/External/FEXCore/include/FEXCore/Debug/InternalThreadState.h index ffc0ebdb9..67d3f3843 100644 --- a/External/FEXCore/include/FEXCore/Debug/InternalThreadState.h +++ b/External/FEXCore/include/FEXCore/Debug/InternalThreadState.h @@ -107,7 +107,7 @@ namespace FEXCore::Core { alignas(16) FEXCore::Core::CpuStateFrame BaseFrameState{}; }; - static_assert(std::is_standard_layout::value, "This needs to be standard layout"); + // static_assert(std::is_standard_layout::value, "This needs to be standard layout"); } From cc477b6964b3ac926a8ff743cfbfe3474d9170a5 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Thu, 27 May 2021 17:26:29 -0700 Subject: [PATCH 03/13] Implements close_range syscall --- Source/Tests/LinuxSyscalls/FileManagement.cpp | 20 +++++++++++++++++++ Source/Tests/LinuxSyscalls/FileManagement.h | 1 + Source/Tests/LinuxSyscalls/Syscalls/FD.cpp | 10 ++++++++++ 3 files changed, 31 insertions(+) diff --git a/Source/Tests/LinuxSyscalls/FileManagement.cpp b/Source/Tests/LinuxSyscalls/FileManagement.cpp index 3c3e039fd..3e19f20af 100644 --- a/Source/Tests/LinuxSyscalls/FileManagement.cpp +++ b/Source/Tests/LinuxSyscalls/FileManagement.cpp @@ -184,6 +184,26 @@ uint64_t FileManager::Close(int fd) { return ::close(fd); } +uint64_t FileManager::CloseRange(unsigned int first, unsigned int last, unsigned int flags) { +#ifndef SYS_close_range +#define SYS_close_range 436 +#endif +#ifndef CLOSE_RANGE_CLOEXEC +#define CLOSE_RANGE_CLOEXEC (1U << 2) +#endif + + if (!(flags & CLOSE_RANGE_CLOEXEC)) { + // If the flag was set then it doesn't actually close the FDs + // Just sets the flag on a range + std::lock_guard lk(FDLock); + for (unsigned int i = first; i <= last; ++i) { + // We remove from first to last inclusive + FDToNameMap.erase(i); + } + } + return ::syscall(SYS_close_range, first, last, flags); +} + uint64_t FileManager::Stat(const char *pathname, void *buf) { auto NewPath = GetSelf(pathname); const char *SelfPath = NewPath ? NewPath->c_str() : nullptr; diff --git a/Source/Tests/LinuxSyscalls/FileManagement.h b/Source/Tests/LinuxSyscalls/FileManagement.h index c7ac5eee3..8ee9139df 100644 --- a/Source/Tests/LinuxSyscalls/FileManagement.h +++ b/Source/Tests/LinuxSyscalls/FileManagement.h @@ -32,6 +32,7 @@ public: ~FileManager(); uint64_t Open(const char *pathname, int flags, uint32_t mode); uint64_t Close(int fd); + uint64_t CloseRange(unsigned int first, unsigned int last, unsigned int flags); uint64_t Stat(const char *pathname, void *buf); uint64_t Lstat(const char *path, void *buf); uint64_t Access(const char *pathname, int mode); diff --git a/Source/Tests/LinuxSyscalls/Syscalls/FD.cpp b/Source/Tests/LinuxSyscalls/Syscalls/FD.cpp index 0302b642d..4490ac546 100644 --- a/Source/Tests/LinuxSyscalls/Syscalls/FD.cpp +++ b/Source/Tests/LinuxSyscalls/Syscalls/FD.cpp @@ -319,5 +319,15 @@ namespace FEX::HLE { uint64_t Result = ::copy_file_range(fd_in, off_in, fd_out, off_out, len, flags); SYSCALL_ERRNO(); }); + + if (Handler->GetHostKernelVersion() >= FEX::HLE::SyscallHandler::KernelVersion(5, 9, 0)) { + REGISTER_SYSCALL_IMPL(close_range, [](FEXCore::Core::CpuStateFrame *Frame, unsigned int first, unsigned int last, unsigned int flags) -> uint64_t { + uint64_t Result = FEX::HLE::_SyscallHandler->FM.CloseRange(first, last, flags); + SYSCALL_ERRNO(); + }); + } + else { + REGISTER_SYSCALL_IMPL(close_range, UnimplementedSyscallSafe); + } } } From 8b3bbd0ea11383a1d08dced87d62375e26d79d51 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Thu, 27 May 2021 17:28:06 -0700 Subject: [PATCH 04/13] Implements openat2 --- Source/Tests/LinuxSyscalls/FileManagement.cpp | 31 +++++++++++++++++++ Source/Tests/LinuxSyscalls/FileManagement.h | 3 ++ Source/Tests/LinuxSyscalls/Syscalls.h | 6 ++++ Source/Tests/LinuxSyscalls/Syscalls/FD.cpp | 11 +++++++ 4 files changed, 51 insertions(+) diff --git a/Source/Tests/LinuxSyscalls/FileManagement.cpp b/Source/Tests/LinuxSyscalls/FileManagement.cpp index 3e19f20af..07a312728 100644 --- a/Source/Tests/LinuxSyscalls/FileManagement.cpp +++ b/Source/Tests/LinuxSyscalls/FileManagement.cpp @@ -6,6 +6,7 @@ $end_info$ */ #include "Tests/LinuxSyscalls/FileManagement.h" +#include "Tests/LinuxSyscalls/Syscalls.h" #include #include @@ -355,6 +356,36 @@ uint64_t FileManager::Openat([[maybe_unused]] int dirfs, const char *pathname, i return fd; } +uint64_t FileManager::Openat2(int dirfs, const char *pathname, FEX::HLE::open_how *how, size_t usize) { +#ifndef SYS_openat2 +#define SYS_openat2 437 +#endif + + auto NewPath = GetSelf(pathname); + const char *SelfPath = NewPath ? NewPath->c_str() : nullptr; + + int32_t fd = -1; + + fd = EmuFD.OpenAt(dirfs, SelfPath, how->flags, how->mode); + if (fd == -1) { + auto Path = GetEmulatedPath(SelfPath, true); + if (!Path.empty()) { + fd = ::syscall(SYS_openat2, dirfs, Path.c_str(), how, usize); + } + + if (fd == -1) + fd = ::syscall(SYS_openat2, dirfs, SelfPath, how, usize); + } + + if (fd != -1) { + std::lock_guard lk(FDLock); + FDToNameMap[fd] = SelfPath; + } + + return fd; + +} + uint64_t FileManager::Statx(int dirfd, const char *pathname, int flags, uint32_t mask, struct statx *statxbuf) { auto NewPath = GetSelf(pathname); const char *SelfPath = NewPath ? NewPath->c_str() : nullptr; diff --git a/Source/Tests/LinuxSyscalls/FileManagement.h b/Source/Tests/LinuxSyscalls/FileManagement.h index 8ee9139df..fbd3a629d 100644 --- a/Source/Tests/LinuxSyscalls/FileManagement.h +++ b/Source/Tests/LinuxSyscalls/FileManagement.h @@ -23,6 +23,8 @@ struct Context; namespace FEX::HLE { +struct open_how; + class FileManager final { public: FileManager() = delete; @@ -42,6 +44,7 @@ public: uint64_t Chmod(const char *pathname, mode_t mode); uint64_t Readlinkat(int dirfd, const char *pathname, char *buf, size_t bufsiz); uint64_t Openat(int dirfs, const char *pathname, int flags, uint32_t mode); + uint64_t Openat2(int dirfs, const char *pathname, FEX::HLE::open_how *how, size_t usize); uint64_t Statx(int dirfd, const char *pathname, int flags, uint32_t mask, struct statx *statxbuf); uint64_t Mknod(const char *pathname, mode_t mode, dev_t dev); uint64_t NewFSStatAt(int dirfd, const char *pathname, struct stat *buf, int flag); diff --git a/Source/Tests/LinuxSyscalls/Syscalls.h b/Source/Tests/LinuxSyscalls/Syscalls.h index ee98353c2..5c735adaa 100644 --- a/Source/Tests/LinuxSyscalls/Syscalls.h +++ b/Source/Tests/LinuxSyscalls/Syscalls.h @@ -247,6 +247,12 @@ struct __attribute__((packed)) epoll_event_x86 { static_assert(std::is_trivial::value, "Needs to be trivial"); static_assert(sizeof(epoll_event_x86) == 12, "Incorrect size"); +struct open_how { + uint64_t flags; + uint64_t mode; + uint64_t resolve; +}; + inline static int RemapFromX86Flags(int flags) { #ifdef _M_X86_64 // Nothing to change here diff --git a/Source/Tests/LinuxSyscalls/Syscalls/FD.cpp b/Source/Tests/LinuxSyscalls/Syscalls/FD.cpp index 4490ac546..e5dca8841 100644 --- a/Source/Tests/LinuxSyscalls/Syscalls/FD.cpp +++ b/Source/Tests/LinuxSyscalls/Syscalls/FD.cpp @@ -220,10 +220,21 @@ namespace FEX::HLE { uint64_t Result = ::syscall(SYS_pidfd_getfd, pidfd, fd, flags); SYSCALL_ERRNO(); }); + + REGISTER_SYSCALL_IMPL(openat2, [](FEXCore::Core::CpuStateFrame *Frame, int dirfs, const char *pathname, struct open_how *how, size_t usize) -> uint64_t { + open_how HostHow{}; + size_t HostSize = std::min(sizeof(open_how), usize); + memcpy(&HostHow, how, HostSize); + + HostHow.flags = FEX::HLE::RemapFromX86Flags(HostHow.flags); + uint64_t Result = FEX::HLE::_SyscallHandler->FM.Openat2(dirfs, pathname, &HostHow, HostSize); + SYSCALL_ERRNO(); + }); } else { REGISTER_SYSCALL_IMPL(faccessat2, UnimplementedSyscallSafe); REGISTER_SYSCALL_IMPL(pidfd_getfd, UnimplementedSyscallSafe); + REGISTER_SYSCALL_IMPL(openat2, UnimplementedSyscallSafe); } REGISTER_SYSCALL_IMPL(splice, [](FEXCore::Core::CpuStateFrame *Frame, int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags) -> uint64_t { From 9d5fa64f68cc740d4990a7f926632aaa74cfc0fc Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Thu, 27 May 2021 17:29:36 -0700 Subject: [PATCH 05/13] Implements pidfd_open syscall --- Source/Tests/LinuxSyscalls/Syscalls/FD.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Source/Tests/LinuxSyscalls/Syscalls/FD.cpp b/Source/Tests/LinuxSyscalls/Syscalls/FD.cpp index e5dca8841..eb44970ac 100644 --- a/Source/Tests/LinuxSyscalls/Syscalls/FD.cpp +++ b/Source/Tests/LinuxSyscalls/Syscalls/FD.cpp @@ -331,6 +331,16 @@ namespace FEX::HLE { SYSCALL_ERRNO(); }); + if (Handler->GetHostKernelVersion() >= FEX::HLE::SyscallHandler::KernelVersion(5, 3, 0)) { + REGISTER_SYSCALL_IMPL(pidfd_open, [](FEXCore::Core::CpuStateFrame *Frame, pid_t pid, unsigned int flags) -> uint64_t { + uint64_t Result = ::syscall(SYS_pidfd_open, pid, flags); + SYSCALL_ERRNO(); + }); + } + else { + REGISTER_SYSCALL_IMPL(pidfd_open, UnimplementedSyscallSafe); + } + if (Handler->GetHostKernelVersion() >= FEX::HLE::SyscallHandler::KernelVersion(5, 9, 0)) { REGISTER_SYSCALL_IMPL(close_range, [](FEXCore::Core::CpuStateFrame *Frame, unsigned int first, unsigned int last, unsigned int flags) -> uint64_t { uint64_t Result = FEX::HLE::_SyscallHandler->FM.CloseRange(first, last, flags); From 6b3b10f8aeb81d63e70f33c0048d4d26d0b7521c Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Thu, 27 May 2021 17:31:24 -0700 Subject: [PATCH 06/13] Implements pidfd_send_signal syscall Now that we know when to forward a siginfo_t to the guest, we can allow this syscall --- Source/Tests/LinuxSyscalls/Syscalls.h | 2 +- Source/Tests/LinuxSyscalls/Syscalls/Signals.cpp | 12 +++++++++++- Source/Tests/LinuxSyscalls/x32/Syscalls.cpp | 2 +- Source/Tests/LinuxSyscalls/x64/Syscalls.cpp | 2 +- 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/Source/Tests/LinuxSyscalls/Syscalls.h b/Source/Tests/LinuxSyscalls/Syscalls.h index 5c735adaa..223d2e9b8 100644 --- a/Source/Tests/LinuxSyscalls/Syscalls.h +++ b/Source/Tests/LinuxSyscalls/Syscalls.h @@ -42,7 +42,7 @@ class SyscallHandler; void RegisterSched(); void RegisterSemaphore(); void RegisterSHM(); - void RegisterSignals(); + void RegisterSignals(FEX::HLE::SyscallHandler *const Handler); void RegisterSocket(); void RegisterThread(); void RegisterTime(); diff --git a/Source/Tests/LinuxSyscalls/Syscalls/Signals.cpp b/Source/Tests/LinuxSyscalls/Syscalls/Signals.cpp index dad801eed..6c77c4ec3 100644 --- a/Source/Tests/LinuxSyscalls/Syscalls/Signals.cpp +++ b/Source/Tests/LinuxSyscalls/Syscalls/Signals.cpp @@ -21,7 +21,7 @@ namespace SignalDelegator { } namespace FEX::HLE { - void RegisterSignals() { + void RegisterSignals(FEX::HLE::SyscallHandler *const Handler) { REGISTER_SYSCALL_IMPL(rt_sigprocmask, [](FEXCore::Core::CpuStateFrame *Frame, int how, const uint64_t *set, uint64_t *oldset) -> uint64_t { return FEX::HLE::_SyscallHandler->GetSignalDelegator()->GuestSigProcMask(how, set, oldset); }); @@ -41,5 +41,15 @@ namespace FEX::HLE { uint64_t Result = ::syscall(SYS_userfaultfd, flags); SYSCALL_ERRNO(); }); + + if (Handler->GetHostKernelVersion() >= FEX::HLE::SyscallHandler::KernelVersion(5, 1, 0)) { + REGISTER_SYSCALL_IMPL(pidfd_send_signal, [](FEXCore::Core::CpuStateFrame *Frame, int pidfd, int sig, siginfo_t *info, unsigned int flags) -> uint64_t { + uint64_t Result = ::syscall(SYS_pidfd_send_signal); + SYSCALL_ERRNO(); + }); + } + else { + REGISTER_SYSCALL_IMPL(pidfd_send_signal, UnimplementedSyscallSafe); + } } } diff --git a/Source/Tests/LinuxSyscalls/x32/Syscalls.cpp b/Source/Tests/LinuxSyscalls/x32/Syscalls.cpp index 2536d93ee..4f3c45d1e 100644 --- a/Source/Tests/LinuxSyscalls/x32/Syscalls.cpp +++ b/Source/Tests/LinuxSyscalls/x32/Syscalls.cpp @@ -668,7 +668,7 @@ public: FEX::HLE::RegisterSched(); FEX::HLE::RegisterSemaphore(); FEX::HLE::RegisterSHM(); - FEX::HLE::RegisterSignals(); + FEX::HLE::RegisterSignals(this); FEX::HLE::RegisterSocket(); FEX::HLE::RegisterThread(); FEX::HLE::RegisterTime(); diff --git a/Source/Tests/LinuxSyscalls/x64/Syscalls.cpp b/Source/Tests/LinuxSyscalls/x64/Syscalls.cpp index d5fd9332f..0194fd891 100644 --- a/Source/Tests/LinuxSyscalls/x64/Syscalls.cpp +++ b/Source/Tests/LinuxSyscalls/x64/Syscalls.cpp @@ -109,7 +109,7 @@ namespace FEX::HLE::x64 { FEX::HLE::RegisterSched(); FEX::HLE::RegisterSemaphore(); FEX::HLE::RegisterSHM(); - FEX::HLE::RegisterSignals(); + FEX::HLE::RegisterSignals(this); FEX::HLE::RegisterSocket(); FEX::HLE::RegisterThread(); FEX::HLE::RegisterTime(); From a7858f4d374c5fc17bd349c919ec9fe228d9dd7d Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Thu, 27 May 2021 17:33:54 -0700 Subject: [PATCH 07/13] Calculates a guest kernel version for FEX instead Takes the host kernel version and makes sure it fits in our supported kernel range Minimum kernel version FEX reports to the guest is 5.0 Maximum kernel version FEX reports to the guest is currently 5.12 --- Source/Tests/LinuxSyscalls/Syscalls.cpp | 7 +++++++ Source/Tests/LinuxSyscalls/Syscalls.h | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/Source/Tests/LinuxSyscalls/Syscalls.cpp b/Source/Tests/LinuxSyscalls/Syscalls.cpp index 142bfcbcb..70a75f375 100644 --- a/Source/Tests/LinuxSyscalls/Syscalls.cpp +++ b/Source/Tests/LinuxSyscalls/Syscalls.cpp @@ -253,6 +253,8 @@ SyscallHandler::SyscallHandler(FEXCore::Context::Context *ctx, FEX::HLE::SignalD , SignalDelegation {_SignalDelegation} { FEX::HLE::_SyscallHandler = this; HostKernelVersion = CalculateHostKernelVersion(); + GuestKernelVersion = CalculateGuestKernelVersion(); + } SyscallHandler::~SyscallHandler() { @@ -278,6 +280,11 @@ uint32_t SyscallHandler::CalculateHostKernelVersion() { return (Major << 24) | (Minor << 16) | Patch; } +uint32_t SyscallHandler::CalculateGuestKernelVersion() { + // We currently only emulate a kernel between the ranges of Kernel 5.0.0 and 5.12.0 + return std::max(KernelVersion(5, 0), std::min(KernelVersion(5, 12), GetHostKernelVersion())); +} + uint64_t SyscallHandler::HandleSyscall(FEXCore::Core::CpuStateFrame *Frame, FEXCore::HLE::SyscallArguments *Args) { if (Args->Argument[0] >= Definitions.size()) { return -ENOSYS; diff --git a/Source/Tests/LinuxSyscalls/Syscalls.h b/Source/Tests/LinuxSyscalls/Syscalls.h index 223d2e9b8..1d209471a 100644 --- a/Source/Tests/LinuxSyscalls/Syscalls.h +++ b/Source/Tests/LinuxSyscalls/Syscalls.h @@ -114,12 +114,19 @@ public: FEX_CONFIG_OPT(Is64BitMode, IS64BIT_MODE); uint32_t GetHostKernelVersion() const { return HostKernelVersion; } + uint32_t GetGuestKernelVersion() const { return GuestKernelVersion; } static uint32_t CalculateHostKernelVersion(); + uint32_t CalculateGuestKernelVersion(); + static uint32_t KernelVersion(uint32_t Major, uint32_t Minor = 0, uint32_t Patch = 0) { return (Major << 24) | (Minor << 16) | Patch; } + static uint32_t KernelMajor(uint32_t Version) { return Version >> 24; } + static uint32_t KernelMinor(uint32_t Version) { return (Version >> 16) & 0xFF; } + static uint32_t KernelPatch(uint32_t Version) { return Version & 0xFFFF; } + protected: std::vector Definitions{}; std::mutex MMapMutex; @@ -132,6 +139,7 @@ protected: // (Major << 24) | (Minor << 16) | Patch uint32_t HostKernelVersion{}; + uint32_t GuestKernelVersion{}; private: From 9d44dfdca6bd642ad9c5e56e15bcfa2f24798198 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Thu, 27 May 2021 17:35:56 -0700 Subject: [PATCH 08/13] Actually updated the hardcoded guest kernel locations Just a couple of files and uname syscall --- .../EmulatedFiles/EmulatedFiles.cpp | 17 +++++++++++++---- Source/Tests/LinuxSyscalls/Syscalls/Info.cpp | 7 ++++++- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/Source/Tests/LinuxSyscalls/EmulatedFiles/EmulatedFiles.cpp b/Source/Tests/LinuxSyscalls/EmulatedFiles/EmulatedFiles.cpp index a2344d953..ac9f15aec 100644 --- a/Source/Tests/LinuxSyscalls/EmulatedFiles/EmulatedFiles.cpp +++ b/Source/Tests/LinuxSyscalls/EmulatedFiles/EmulatedFiles.cpp @@ -600,8 +600,12 @@ namespace FEX::EmulatedFile { FDReadCreators["/proc/sys/kernel/osrelease"] = [&](FEXCore::Context::Context *ctx, int32_t fd, const char *pathname, int32_t flags, mode_t mode) -> int32_t { FILE *fp = tmpfile(); - const char kernel_version[] = "5.0.0\0"; - fwrite(kernel_version, sizeof(uint8_t), strlen(kernel_version) + 1, fp); + uint32_t GuestVersion = FEX::HLE::_SyscallHandler->GetGuestKernelVersion(); + fprintf(fp, "%d.%d.%d\n", + FEX::HLE::SyscallHandler::KernelMajor(GuestVersion), + FEX::HLE::SyscallHandler::KernelMinor(GuestVersion), + FEX::HLE::SyscallHandler::KernelPatch(GuestVersion)); + fputc('\0', fp); fseek(fp, 0, SEEK_SET); int32_t f = fileno(fp); return f; @@ -611,8 +615,13 @@ namespace FEX::EmulatedFile { FILE *fp = tmpfile(); // UTS version NEEDS to be in a format that can pass to `date -d` // Format of this is Linux version (@) () # {SMP, PREEMPT, PREEMPT_RT} \n" - const char kernel_version[] = "Linux version 5.0.0 (FEX@FEX) (clang) #" GIT_DESCRIBE_STRING " SMP " __DATE__ " " __TIME__ "\n\0"; - fwrite(kernel_version, sizeof(uint8_t), strlen(kernel_version) + 1, fp); + const char kernel_version[] = "Linux version %d.%d.%d (FEX@FEX) (clang) #" GIT_DESCRIBE_STRING " SMP " __DATE__ " " __TIME__ "\n"; + uint32_t GuestVersion = FEX::HLE::_SyscallHandler->GetGuestKernelVersion(); + fprintf(fp, kernel_version, + FEX::HLE::SyscallHandler::KernelMajor(GuestVersion), + FEX::HLE::SyscallHandler::KernelMinor(GuestVersion), + FEX::HLE::SyscallHandler::KernelPatch(GuestVersion)); + fputc('\0', fp); fseek(fp, 0, SEEK_SET); int32_t f = fileno(fp); return f; diff --git a/Source/Tests/LinuxSyscalls/Syscalls/Info.cpp b/Source/Tests/LinuxSyscalls/Syscalls/Info.cpp index f9723d5ac..495f46b97 100644 --- a/Source/Tests/LinuxSyscalls/Syscalls/Info.cpp +++ b/Source/Tests/LinuxSyscalls/Syscalls/Info.cpp @@ -42,7 +42,12 @@ namespace FEX::HLE { LogMan::Msg::E("Couldn't determine host nodename. Defaulting to '%s'", buf->nodename); } strcpy(buf->sysname, "Linux"); - strcpy(buf->release, "5.0.0"); + uint32_t GuestVersion = FEX::HLE::_SyscallHandler->GetGuestKernelVersion(); + snprintf(buf->release, sizeof(buf->release), "%d.%d.%d", + FEX::HLE::SyscallHandler::KernelMajor(GuestVersion), + FEX::HLE::SyscallHandler::KernelMinor(GuestVersion), + FEX::HLE::SyscallHandler::KernelPatch(GuestVersion)); + const char version[] = "#" GIT_DESCRIBE_STRING " SMP " __DATE__ " " __TIME__; strcpy(buf->version, version); static_assert(sizeof(version) <= sizeof(buf->version), "uname version define became too large!"); From 7e551e5f88f6548a1244fcb9ebd7f3d97983aa7c Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Thu, 27 May 2021 17:38:17 -0700 Subject: [PATCH 09/13] Implements epoll_pwait2 syscall --- Source/Tests/LinuxSyscalls/x32/EPoll.cpp | 39 ++++++++++++++++++++- Source/Tests/LinuxSyscalls/x32/Syscalls.cpp | 4 +-- Source/Tests/LinuxSyscalls/x64/EPoll.cpp | 31 +++++++++++++++- Source/Tests/LinuxSyscalls/x64/Syscalls.cpp | 4 +-- 4 files changed, 72 insertions(+), 6 deletions(-) diff --git a/Source/Tests/LinuxSyscalls/x32/EPoll.cpp b/Source/Tests/LinuxSyscalls/x32/EPoll.cpp index 7a97931ca..0d74aaef6 100644 --- a/Source/Tests/LinuxSyscalls/x32/EPoll.cpp +++ b/Source/Tests/LinuxSyscalls/x32/EPoll.cpp @@ -21,7 +21,7 @@ $end_info$ ARG_TO_STR(FEX::HLE::x32::compat_ptr, "%lx") namespace FEX::HLE::x32 { - void RegisterEpoll() { + void RegisterEpoll(FEX::HLE::SyscallHandler *const Handler) { REGISTER_SYSCALL_IMPL_X32(epoll_wait, [](FEXCore::Core::CpuStateFrame *Frame, int epfd, compat_ptr events, int maxevents, int timeout) -> uint64_t { std::vector Events; Events.resize(maxevents); @@ -64,5 +64,42 @@ namespace FEX::HLE::x32 { SYSCALL_ERRNO(); }); + + if (Handler->GetHostKernelVersion() >= FEX::HLE::SyscallHandler::KernelVersion(5, 11, 0)) { +#ifndef SYS_epoll_pwait2 +#define SYS_epoll_pwait2 354 +#endif + REGISTER_SYSCALL_IMPL_X32(epoll_pwait2, [](FEXCore::Core::CpuStateFrame *Frame, int epfd, compat_ptr events, int maxevent, compat_ptr timeout, const uint64_t* sigmask, size_t sigsetsize) -> uint64_t { + std::vector Events; + Events.resize(maxevent); + + struct timespec tp64{}; + struct timespec *timed_ptr{}; + if (timeout) { + tp64 = *timeout; + timed_ptr = &tp64; + } + + uint64_t Result = ::syscall(SYS_epoll_pwait2, + epfd, + &Events.at(0), + maxevent, + timed_ptr, + sigmask, + sigsetsize); + + if (Result != -1) { + for (size_t i = 0; i < Result; ++i) { + events[i] = Events[i]; + } + } + + SYSCALL_ERRNO(); + }); + } + else { + REGISTER_SYSCALL_IMPL_X32(epoll_pwait2, UnimplementedSyscallSafe); + } + } } diff --git a/Source/Tests/LinuxSyscalls/x32/Syscalls.cpp b/Source/Tests/LinuxSyscalls/x32/Syscalls.cpp index 4f3c45d1e..a8ac86675 100644 --- a/Source/Tests/LinuxSyscalls/x32/Syscalls.cpp +++ b/Source/Tests/LinuxSyscalls/x32/Syscalls.cpp @@ -580,7 +580,7 @@ public: } }; - void RegisterEpoll(); + void RegisterEpoll(FEX::HLE::SyscallHandler *const Handler); void RegisterFD(); void RegisterFS(); void RegisterInfo(); @@ -677,7 +677,7 @@ public: FEX::HLE::RegisterStubs(); // 32bit specific - FEX::HLE::x32::RegisterEpoll(); + FEX::HLE::x32::RegisterEpoll(this); FEX::HLE::x32::RegisterFD(); FEX::HLE::x32::RegisterFS(); FEX::HLE::x32::RegisterInfo(); diff --git a/Source/Tests/LinuxSyscalls/x64/EPoll.cpp b/Source/Tests/LinuxSyscalls/x64/EPoll.cpp index 941d5cdf1..729d7921f 100644 --- a/Source/Tests/LinuxSyscalls/x64/EPoll.cpp +++ b/Source/Tests/LinuxSyscalls/x64/EPoll.cpp @@ -19,7 +19,7 @@ $end_info$ #include namespace FEX::HLE::x64 { - void RegisterEpoll() { + void RegisterEpoll(FEX::HLE::SyscallHandler *const Handler) { REGISTER_SYSCALL_IMPL_X64(epoll_wait, [](FEXCore::Core::CpuStateFrame *Frame, int epfd, epoll_event_x86 *events, int maxevents, int timeout) -> uint64_t { std::vector Events; Events.resize(maxevents); @@ -67,5 +67,34 @@ namespace FEX::HLE::x64 { SYSCALL_ERRNO(); }); + + if (Handler->GetHostKernelVersion() >= FEX::HLE::SyscallHandler::KernelVersion(5, 11, 0)) { +#ifndef SYS_epoll_pwait2 +#define SYS_epoll_pwait2 354 +#endif + REGISTER_SYSCALL_IMPL_X64(epoll_pwait2, [](FEXCore::Core::CpuStateFrame *Frame, int epfd, epoll_event_x86 *events, int maxevent, timespec *timeout, const uint64_t* sigmask, size_t sigsetsize) -> uint64_t { + std::vector Events; + Events.resize(maxevent); + + uint64_t Result = ::syscall(SYS_epoll_pwait2, + epfd, + &Events.at(0), + maxevent, + timeout, + sigmask, + sigsetsize); + + if (Result != -1) { + for (size_t i = 0; i < Result; ++i) { + events[i] = Events[i]; + } + } + + SYSCALL_ERRNO(); + }); + } + else { + REGISTER_SYSCALL_IMPL_X64(epoll_pwait2, UnimplementedSyscallSafe); + } } } diff --git a/Source/Tests/LinuxSyscalls/x64/Syscalls.cpp b/Source/Tests/LinuxSyscalls/x64/Syscalls.cpp index 0194fd891..4323aeb38 100644 --- a/Source/Tests/LinuxSyscalls/x64/Syscalls.cpp +++ b/Source/Tests/LinuxSyscalls/x64/Syscalls.cpp @@ -13,7 +13,7 @@ $end_info$ #include namespace FEX::HLE::x64 { - void RegisterEpoll(); + void RegisterEpoll(FEX::HLE::SyscallHandler *const Handler); void RegisterFD(); void RegisterInfo(); void RegisterIO(); @@ -118,7 +118,7 @@ namespace FEX::HLE::x64 { FEX::HLE::RegisterStubs(); // 64bit specific - FEX::HLE::x64::RegisterEpoll(); + FEX::HLE::x64::RegisterEpoll(this); FEX::HLE::x64::RegisterFD(); FEX::HLE::x64::RegisterInfo(); FEX::HLE::x64::RegisterIO(); From 77a111cf19ab1e046d6d0071424494ff93c02ae8 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Thu, 27 May 2021 17:40:00 -0700 Subject: [PATCH 10/13] Implements process_madvise 64-bit syscall --- Source/Tests/LinuxSyscalls/x64/Memory.cpp | 15 ++++++++++++++- Source/Tests/LinuxSyscalls/x64/Syscalls.cpp | 4 ++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/Source/Tests/LinuxSyscalls/x64/Memory.cpp b/Source/Tests/LinuxSyscalls/x64/Memory.cpp index 035023550..81d2018e3 100644 --- a/Source/Tests/LinuxSyscalls/x64/Memory.cpp +++ b/Source/Tests/LinuxSyscalls/x64/Memory.cpp @@ -27,7 +27,7 @@ static std::string get_fdpath(int fd) } namespace FEX::HLE::x64 { - void RegisterMemory() { + void RegisterMemory(FEX::HLE::SyscallHandler *const Handler) { REGISTER_SYSCALL_IMPL_X64(munmap, [](FEXCore::Core::CpuStateFrame *Frame, void *addr, size_t length) -> uint64_t { uint64_t Result = FEXCore::Allocator::munmap(addr, length); @@ -106,5 +106,18 @@ namespace FEX::HLE::x64 { uint64_t Result = reinterpret_cast(shmat(shmid, shmaddr, shmflg)); SYSCALL_ERRNO(); }); + +#ifndef SYS_process_madvise +#define SYS_process_madvise 440 +#endif + if (Handler->GetHostKernelVersion() >= FEX::HLE::SyscallHandler::KernelVersion(5, 10, 0)) { + REGISTER_SYSCALL_IMPL_X64(process_madvise, [](FEXCore::Core::CpuStateFrame *Frame, int pidfd, const struct iovec *iovec, size_t vlen, int advice, unsigned int flags) -> uint64_t { + uint64_t Result = ::syscall(SYS_process_madvise, pidfd, iovec, vlen, advice, flags); + SYSCALL_ERRNO(); + }); + } + else { + REGISTER_SYSCALL_IMPL_X64(process_madvise, UnimplementedSyscallSafe); + } } } diff --git a/Source/Tests/LinuxSyscalls/x64/Syscalls.cpp b/Source/Tests/LinuxSyscalls/x64/Syscalls.cpp index 4323aeb38..58d0dec1e 100644 --- a/Source/Tests/LinuxSyscalls/x64/Syscalls.cpp +++ b/Source/Tests/LinuxSyscalls/x64/Syscalls.cpp @@ -18,7 +18,7 @@ namespace FEX::HLE::x64 { void RegisterInfo(); void RegisterIO(); void RegisterIoctl(); - void RegisterMemory(); + void RegisterMemory(FEX::HLE::SyscallHandler *const Handler); void RegisterMsg(); void RegisterSched(); void RegisterSocket(); @@ -123,7 +123,7 @@ namespace FEX::HLE::x64 { FEX::HLE::x64::RegisterInfo(); FEX::HLE::x64::RegisterIO(); FEX::HLE::x64::RegisterIoctl(); - FEX::HLE::x64::RegisterMemory(); + FEX::HLE::x64::RegisterMemory(this); FEX::HLE::x64::RegisterMsg(); FEX::HLE::x64::RegisterSched(); FEX::HLE::x64::RegisterSocket(); From 4c7447861028613852f148aaa2df9561ea1b16df Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Thu, 27 May 2021 17:46:09 -0700 Subject: [PATCH 11/13] Implements iouring syscalls This is a very simple initial implementation. io_uring allows some things that are hard to capture like setting personalities. Assume sane usage for now --- Source/Tests/LinuxSyscalls/CMakeLists.txt | 1 + Source/Tests/LinuxSyscalls/Syscalls.h | 1 + .../Tests/LinuxSyscalls/Syscalls/IOUring.cpp | 45 +++++++++++++++++++ Source/Tests/LinuxSyscalls/x32/Syscalls.cpp | 1 + Source/Tests/LinuxSyscalls/x64/Syscalls.cpp | 1 + 5 files changed, 49 insertions(+) create mode 100644 Source/Tests/LinuxSyscalls/Syscalls/IOUring.cpp diff --git a/Source/Tests/LinuxSyscalls/CMakeLists.txt b/Source/Tests/LinuxSyscalls/CMakeLists.txt index f8558a3be..77e4a8896 100644 --- a/Source/Tests/LinuxSyscalls/CMakeLists.txt +++ b/Source/Tests/LinuxSyscalls/CMakeLists.txt @@ -41,6 +41,7 @@ add_library(LinuxEmulation STATIC Syscalls/FS.cpp Syscalls/Info.cpp Syscalls/IO.cpp + Syscalls/IOUring.cpp Syscalls/Key.cpp Syscalls/Memory.cpp Syscalls/Msg.cpp diff --git a/Source/Tests/LinuxSyscalls/Syscalls.h b/Source/Tests/LinuxSyscalls/Syscalls.h index 1d209471a..ba1f56da5 100644 --- a/Source/Tests/LinuxSyscalls/Syscalls.h +++ b/Source/Tests/LinuxSyscalls/Syscalls.h @@ -35,6 +35,7 @@ class SyscallHandler; void RegisterFS(); void RegisterInfo(); void RegisterIO(); + void RegisterIOUring(FEX::HLE::SyscallHandler *const Handler); void RegisterKey(); void RegisterMemory(); void RegisterMsg(); diff --git a/Source/Tests/LinuxSyscalls/Syscalls/IOUring.cpp b/Source/Tests/LinuxSyscalls/Syscalls/IOUring.cpp new file mode 100644 index 000000000..40622d8aa --- /dev/null +++ b/Source/Tests/LinuxSyscalls/Syscalls/IOUring.cpp @@ -0,0 +1,45 @@ +/* +$info$ +tags: LinuxSyscalls|syscalls-shared +$end_info$ +*/ + +#include "Tests/LinuxSyscalls/Syscalls.h" +#include "Tests/LinuxSyscalls/Syscalls/Thread.h" +#include "Tests/LinuxSyscalls/x64/Syscalls.h" +#include "Tests/LinuxSyscalls/x32/Syscalls.h" + +#include +#include +#include + +namespace SignalDelegator { + struct GuestSigAction; +} + + +namespace FEX::HLE { + void RegisterIOUring(FEX::HLE::SyscallHandler *const Handler) { + if (Handler->GetHostKernelVersion() >= FEX::HLE::SyscallHandler::KernelVersion(5, 1, 0)) { + REGISTER_SYSCALL_IMPL(io_uring_setup, [](FEXCore::Core::CpuStateFrame *Frame, uint32_t entries, void* params) -> uint64_t { + uint64_t Result = ::syscall(SYS_io_uring_setup, entries, params); + SYSCALL_ERRNO(); + }); + + REGISTER_SYSCALL_IMPL(io_uring_enter, [](FEXCore::Core::CpuStateFrame *Frame, unsigned int fd, uint32_t to_submit, uint32_t min_complete, uint32_t flags, void *argp, size_t argsz) -> uint64_t { + uint64_t Result = ::syscall(SYS_io_uring_enter, fd, to_submit, min_complete, flags, argp, argsz); + SYSCALL_ERRNO(); + }); + + REGISTER_SYSCALL_IMPL(io_uring_register, [](FEXCore::Core::CpuStateFrame *Frame, unsigned int fd, unsigned int opcode, void *arg, uint32_t nr_args) -> uint64_t { + uint64_t Result = ::syscall(SYS_io_uring_register, fd, opcode, arg, nr_args); + SYSCALL_ERRNO(); + }); + } + else { + REGISTER_SYSCALL_IMPL(io_uring_setup, UnimplementedSyscallSafe); + REGISTER_SYSCALL_IMPL(io_uring_enter, UnimplementedSyscallSafe); + REGISTER_SYSCALL_IMPL(io_uring_register, UnimplementedSyscallSafe); + } + } +} diff --git a/Source/Tests/LinuxSyscalls/x32/Syscalls.cpp b/Source/Tests/LinuxSyscalls/x32/Syscalls.cpp index a8ac86675..67d448a9d 100644 --- a/Source/Tests/LinuxSyscalls/x32/Syscalls.cpp +++ b/Source/Tests/LinuxSyscalls/x32/Syscalls.cpp @@ -662,6 +662,7 @@ public: FEX::HLE::RegisterFS(); FEX::HLE::RegisterInfo(); FEX::HLE::RegisterIO(); + FEX::HLE::RegisterIOUring(this); FEX::HLE::RegisterKey(); FEX::HLE::RegisterMemory(); FEX::HLE::RegisterMsg(); diff --git a/Source/Tests/LinuxSyscalls/x64/Syscalls.cpp b/Source/Tests/LinuxSyscalls/x64/Syscalls.cpp index 58d0dec1e..14c5abe56 100644 --- a/Source/Tests/LinuxSyscalls/x64/Syscalls.cpp +++ b/Source/Tests/LinuxSyscalls/x64/Syscalls.cpp @@ -103,6 +103,7 @@ namespace FEX::HLE::x64 { FEX::HLE::RegisterFS(); FEX::HLE::RegisterInfo(); FEX::HLE::RegisterIO(); + FEX::HLE::RegisterIOUring(this); FEX::HLE::RegisterKey(); FEX::HLE::RegisterMemory(); FEX::HLE::RegisterMsg(); From d115a57fe67e8347064c0554801f44d4f19400bc Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Thu, 27 May 2021 17:53:11 -0700 Subject: [PATCH 12/13] Implements support for execveat --- Source/Tests/LinuxSyscalls/Syscalls.cpp | 23 +++++++++++++--- Source/Tests/LinuxSyscalls/Syscalls.h | 7 ++++- Source/Tests/LinuxSyscalls/Syscalls/Stubs.cpp | 5 ---- Source/Tests/LinuxSyscalls/x32/Thread.cpp | 25 +++++++++++++++++- Source/Tests/LinuxSyscalls/x64/Thread.cpp | 26 ++++++++++++++++++- 5 files changed, 74 insertions(+), 12 deletions(-) diff --git a/Source/Tests/LinuxSyscalls/Syscalls.cpp b/Source/Tests/LinuxSyscalls/Syscalls.cpp index 70a75f375..de7e16d9d 100644 --- a/Source/Tests/LinuxSyscalls/Syscalls.cpp +++ b/Source/Tests/LinuxSyscalls/Syscalls.cpp @@ -95,7 +95,7 @@ static bool IsSupportedByInterpreter(std::string const &Filename) { return false; } -uint64_t ExecveHandler(const char *pathname, std::vector &argv, std::vector &envp) { +uint64_t ExecveHandler(const char *pathname, std::vector &argv, std::vector &envp, ExecveAtArgs *Args) { std::string Filename{}; std::error_code ec; @@ -134,7 +134,12 @@ uint64_t ExecveHandler(const char *pathname, std::vector &argv, std uint64_t Result{}; if (FEX::HLE::_SyscallHandler->IsInterpreterInstalled()) { // If the FEX interpreter is installed then just execve the thing - Result = execve(Filename.c_str(), const_cast(&argv.at(0)), const_cast(&envp.at(0))); + if (Args) { + Result = ::syscall(SYS_execveat, Args->dirfd, Filename.c_str(), const_cast(&argv.at(0)), const_cast(&envp.at(0)), Args->flags); + } + else { + Result = execve(Filename.c_str(), const_cast(&argv.at(0)), const_cast(&envp.at(0))); + } SYSCALL_ERRNO(); } @@ -153,7 +158,12 @@ uint64_t ExecveHandler(const char *pathname, std::vector &argv, std // We are trying to execute an ELF of a different architecture // We can't know if we can support this without architecture specific checks and binfmt_misc parsing // Just execve it and let the kernel handle the process - Result = execve(Filename.c_str(), const_cast(&argv.at(0)), const_cast(&envp.at(0))); + if (Args) { + Result = ::syscall(SYS_execveat, Args->dirfd, Filename.c_str(), const_cast(&argv.at(0)), const_cast(&envp.at(0)), Args->flags); + } + else { + Result = execve(Filename.c_str(), const_cast(&argv.at(0)), const_cast(&envp.at(0))); + } SYSCALL_ERRNO(); } @@ -172,7 +182,12 @@ uint64_t ExecveHandler(const char *pathname, std::vector &argv, std // Append the arguments together ExecveArgs.insert(ExecveArgs.end(), argv.begin(), argv.end()); - Result = execve("/proc/self/exe", const_cast(&ExecveArgs.at(0)), const_cast(&envp.at(0))); + if (Args) { + Result = ::syscall(SYS_execveat, Args->dirfd, "/proc/self/exe", const_cast(&ExecveArgs.at(0)), const_cast(&envp.at(0)), Args->flags); + } + else { + Result = execve("/proc/self/exe", const_cast(&ExecveArgs.at(0)), const_cast(&envp.at(0))); + } SYSCALL_ERRNO(); } diff --git a/Source/Tests/LinuxSyscalls/Syscalls.h b/Source/Tests/LinuxSyscalls/Syscalls.h index ba1f56da5..301c0e882 100644 --- a/Source/Tests/LinuxSyscalls/Syscalls.h +++ b/Source/Tests/LinuxSyscalls/Syscalls.h @@ -54,7 +54,12 @@ class SyscallHandler; uint64_t UnimplementedSyscall(FEXCore::Core::CpuStateFrame *Frame, uint64_t SyscallNumber); uint64_t UnimplementedSyscallSafe(FEXCore::Core::CpuStateFrame *Frame, uint64_t SyscallNumber); -uint64_t ExecveHandler(const char *pathname, std::vector &argv, std::vector &envp); +struct ExecveAtArgs { + int dirfd; + int flags; +}; + +uint64_t ExecveHandler(const char *pathname, std::vector &argv, std::vector &envp, ExecveAtArgs *Args); class SyscallHandler : public FEXCore::HLE::SyscallHandler { public: diff --git a/Source/Tests/LinuxSyscalls/Syscalls/Stubs.cpp b/Source/Tests/LinuxSyscalls/Syscalls/Stubs.cpp index 304734a48..42752f896 100644 --- a/Source/Tests/LinuxSyscalls/Syscalls/Stubs.cpp +++ b/Source/Tests/LinuxSyscalls/Syscalls/Stubs.cpp @@ -64,11 +64,6 @@ namespace FEX::HLE { SYSCALL_STUB(rt_tgsigqueueinfo); }); - // execute program relative to a directory file descriptor - REGISTER_SYSCALL_IMPL(execveat, [](FEXCore::Core::CpuStateFrame *Frame, int dirfd, const char *pathname, char *const argv[], char *const envp[], int flags) -> uint64_t { - SYSCALL_STUB(execveat); - }); - REGISTER_SYSCALL_IMPL(rseq, [](FEXCore::Core::CpuStateFrame *Frame, struct rseq *rseq, uint32_t rseq_len, int flags, uint32_t sig) -> uint64_t { SYSCALL_STUB(rseq); }); diff --git a/Source/Tests/LinuxSyscalls/x32/Thread.cpp b/Source/Tests/LinuxSyscalls/x32/Thread.cpp index de97473d5..4aa887ffe 100644 --- a/Source/Tests/LinuxSyscalls/x32/Thread.cpp +++ b/Source/Tests/LinuxSyscalls/x32/Thread.cpp @@ -294,9 +294,32 @@ namespace FEX::HLE::x32 { } Envp.push_back(nullptr); - return FEX::HLE::ExecveHandler(pathname, Args, Envp); + return FEX::HLE::ExecveHandler(pathname, Args, Envp, nullptr); }); + REGISTER_SYSCALL_IMPL_X32(execveat, ([](FEXCore::Core::CpuStateFrame *Frame, int dirfd, const char *pathname, uint32_t *argv, uint32_t *envp, int flags) -> uint64_t { + std::vector Args; + std::vector Envp; + + for (int i = 0; argv[i]; i++) { + Args.push_back(reinterpret_cast(static_cast(argv[i]))); + } + + Args.push_back(nullptr); + + for (int i = 0; envp[i]; i++) { + Envp.push_back(reinterpret_cast(static_cast(envp[i]))); + } + Envp.push_back(nullptr); + + FEX::HLE::ExecveAtArgs AtArgs { + .dirfd = dirfd, + .flags = flags, + }; + + return FEX::HLE::ExecveHandler(pathname, Args, Envp, &AtArgs); + })); + REGISTER_SYSCALL_IMPL_X32(wait4, [](FEXCore::Core::CpuStateFrame *Frame, pid_t pid, int *wstatus, int options, struct rusage_32 *rusage) -> uint64_t { struct rusage usage64{}; struct rusage *usage64_p{}; diff --git a/Source/Tests/LinuxSyscalls/x64/Thread.cpp b/Source/Tests/LinuxSyscalls/x64/Thread.cpp index c275cf8b8..f2463b718 100644 --- a/Source/Tests/LinuxSyscalls/x64/Thread.cpp +++ b/Source/Tests/LinuxSyscalls/x64/Thread.cpp @@ -173,9 +173,33 @@ namespace FEX::HLE::x64 { Envp.push_back(nullptr); - return FEX::HLE::ExecveHandler(pathname, Args, Envp); + return FEX::HLE::ExecveHandler(pathname, Args, Envp, nullptr); }); + REGISTER_SYSCALL_IMPL_X64(execveat, ([](FEXCore::Core::CpuStateFrame *Frame, int dirfd, const char *pathname, char *const argv[], char *const envp[], int flags) -> uint64_t { + std::vector Args; + std::vector Envp; + + for (int i = 0; argv[i]; i++) { + Args.push_back(argv[i]); + } + + Args.push_back(nullptr); + + for (int i = 0; envp[i]; i++) { + Envp.push_back(envp[i]); + } + + Envp.push_back(nullptr); + + FEX::HLE::ExecveAtArgs AtArgs { + .dirfd = dirfd, + .flags = flags, + }; + + return FEX::HLE::ExecveHandler(pathname, Args, Envp, &AtArgs); + })); + REGISTER_SYSCALL_IMPL_X64(wait4, [](FEXCore::Core::CpuStateFrame *Frame, pid_t pid, int *wstatus, int options, struct rusage *rusage) -> uint64_t { uint64_t Result = ::wait4(pid, wstatus, options, rusage); SYSCALL_ERRNO(); From 0b7587768d6ebc017e8cf507498a67beec037589 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Thu, 27 May 2021 18:03:16 -0700 Subject: [PATCH 13/13] Unifies 32bit and 64bit clone implementation Syscall entry points still have different argument orders, Moves the arguments to the clone3 argument structure and passes to generic handler. Also implements clone3 while doing this --- Source/Tests/LinuxSyscalls/Syscalls.cpp | 122 ++++++++++++++++++ Source/Tests/LinuxSyscalls/Syscalls.h | 15 +++ .../Tests/LinuxSyscalls/Syscalls/Thread.cpp | 37 ++++-- Source/Tests/LinuxSyscalls/Syscalls/Thread.h | 2 +- Source/Tests/LinuxSyscalls/x32/Thread.cpp | 108 +++------------- Source/Tests/LinuxSyscalls/x64/Thread.cpp | 112 +++------------- 6 files changed, 199 insertions(+), 197 deletions(-) diff --git a/Source/Tests/LinuxSyscalls/Syscalls.cpp b/Source/Tests/LinuxSyscalls/Syscalls.cpp index de7e16d9d..577952a9b 100644 --- a/Source/Tests/LinuxSyscalls/Syscalls.cpp +++ b/Source/Tests/LinuxSyscalls/Syscalls.cpp @@ -10,11 +10,13 @@ $end_info$ #include "Common/MathUtils.h" #include "Tests/LinuxSyscalls/Syscalls.h" +#include "Tests/LinuxSyscalls/Syscalls/Thread.h" #include "Tests/LinuxSyscalls/x64/Syscalls.h" #include "Tests/LinuxSyscalls/x32/Syscalls.h" #include #include +#include #include #include #include @@ -192,6 +194,126 @@ uint64_t ExecveHandler(const char *pathname, std::vector &argv, std SYSCALL_ERRNO(); } +static bool AnyFlagsSet(uint64_t Flags, uint64_t Mask) { + return (Flags & Mask) != 0; +} + +static bool AllFlagsSet(uint64_t Flags, uint64_t Mask) { + return (Flags & Mask) == Mask; +} + +uint64_t CloneHandler(FEXCore::Core::CpuStateFrame *Frame, FEX::HLE::clone3_args *args) { + uint64_t flags = args->flags; +#define FLAGPRINT(x, y) if (args->flags & (y)) LogMan::Msg::I("\tFlag: " #x) + FLAGPRINT(CSIGNAL, 0x000000FF); + FLAGPRINT(CLONE_VM, 0x00000100); + FLAGPRINT(CLONE_FS, 0x00000200); + FLAGPRINT(CLONE_FILES, 0x00000400); + FLAGPRINT(CLONE_SIGHAND, 0x00000800); + FLAGPRINT(CLONE_PTRACE, 0x00002000); + FLAGPRINT(CLONE_VFORK, 0x00004000); + FLAGPRINT(CLONE_PARENT, 0x00008000); + FLAGPRINT(CLONE_THREAD, 0x00010000); + FLAGPRINT(CLONE_NEWNS, 0x00020000); + FLAGPRINT(CLONE_SYSVSEM, 0x00040000); + FLAGPRINT(CLONE_SETTLS, 0x00080000); + FLAGPRINT(CLONE_PARENT_SETTID, 0x00100000); + FLAGPRINT(CLONE_CHILD_CLEARTID, 0x00200000); + FLAGPRINT(CLONE_DETACHED, 0x00400000); + FLAGPRINT(CLONE_UNTRACED, 0x00800000); + FLAGPRINT(CLONE_CHILD_SETTID, 0x01000000); + FLAGPRINT(CLONE_NEWCGROUP, 0x02000000); + FLAGPRINT(CLONE_NEWUTS, 0x04000000); + FLAGPRINT(CLONE_NEWIPC, 0x08000000); + FLAGPRINT(CLONE_NEWUSER, 0x10000000); + FLAGPRINT(CLONE_NEWPID, 0x20000000); + FLAGPRINT(CLONE_NEWNET, 0x40000000); + FLAGPRINT(CLONE_IO, 0x80000000); + + auto Thread = Frame->Thread; + + if (AnyFlagsSet(flags, CLONE_UNTRACED | CLONE_PTRACE)) { + LogMan::Msg::D("clone: Ptrace* not supported"); + } + + // Clone3 flags +#ifndef CLONE_CLEAR_SIGHAND +#define CLONE_CLEAR_SIGHAND 0x100000000ULL +#endif +#ifndef CLONE_INTO_CGROUP +#define CLONE_INTO_CGROUP 0x200000000ULL +#endif + + if (AnyFlagsSet(flags, CLONE_CLEAR_SIGHAND)) { + LogMan::Msg::D("clone3: CLONE_CLEAR_SIGHAND unsupported"); + } + + if (AnyFlagsSet(flags, CLONE_INTO_CGROUP)) { + LogMan::Msg::D("clone3: CLONE_INTO_CGROUP unsupported"); + return -EOPNOTSUPP; + } + + if (args->set_tid_size > 0) { + LogMan::Msg::D("clone3: set_tid unsupported"); + return -EPERM; + } + + if (AnyFlagsSet(flags, CLONE_NEWNS | CLONE_NEWCGROUP | CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWNET)) { + // NEWUSER doesn't need any privileges from 3.8 onward + // We just don't support it yet + LogMan::Msg::I("Unconditionally returning EPERM on clone namespace"); + return -EPERM; + } + + if (!(flags & CLONE_THREAD)) { + + if (flags & CLONE_VFORK) { + flags &= ~CLONE_VFORK; + flags &= ~CLONE_VM; + LogMan::Msg::D("clone: WARNING: CLONE_VFORK w/o CLONE_THREAD"); + } + + if (AnyFlagsSet(flags, CLONE_SYSVSEM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_VM)) { + LogMan::Msg::I("clone: Unsuported flags w/o CLONE_THREAD (Shared Resources), %X", flags); + return -EPERM; + } + + // CLONE_PARENT is ignored (Implied by CLONE_THREAD) + return FEX::HLE::ForkGuest(Thread, Frame, flags, + reinterpret_cast(args->stack), + reinterpret_cast(args->parent_tid), + reinterpret_cast(args->child_tid), + reinterpret_cast(args->tls)); + } else { + + if (!AllFlagsSet(flags, CLONE_SYSVSEM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND)) { + LogMan::Msg::I("clone: CLONE_THREAD: Unsuported flags w/ CLONE_THREAD (Shared Resources), %X", flags); + return -EPERM; + } + + auto NewThread = FEX::HLE::CreateNewThread(Thread->CTX, Frame, args); + + // Return the new threads TID + uint64_t Result = NewThread->ThreadManager.GetTID(); + + if (flags & CLONE_VFORK) { + NewThread->DestroyedByParent = true; + } + + // Actually start the thread + FEXCore::Context::RunThread(Thread->CTX, NewThread); + + if (flags & CLONE_VFORK) { + // If VFORK is set then the calling process is suspended until the thread exits with execve or exit + NewThread->ExecutionThread->join(nullptr); + + // Normally a thread cleans itself up on exit. But because we need to join, we are now responsible + FEXCore::Context::DestroyThread(Thread->CTX, NewThread); + } + SYSCALL_ERRNO(); + } +}; + uint64_t SyscallHandler::HandleBRK(FEXCore::Core::CpuStateFrame *Frame, void *Addr) { std::lock_guard lk(MMapMutex); diff --git a/Source/Tests/LinuxSyscalls/Syscalls.h b/Source/Tests/LinuxSyscalls/Syscalls.h index 301c0e882..ba69bd0a2 100644 --- a/Source/Tests/LinuxSyscalls/Syscalls.h +++ b/Source/Tests/LinuxSyscalls/Syscalls.h @@ -267,6 +267,21 @@ struct open_how { uint64_t resolve; }; +struct clone3_args { + uint64_t flags; + uint64_t pidfd; + uint64_t child_tid; + uint64_t parent_tid; + uint64_t exit_signal; + uint64_t stack; + uint64_t stack_size; + uint64_t tls; + uint64_t set_tid; + uint64_t set_tid_size; + uint64_t cgroup; +}; +uint64_t CloneHandler(FEXCore::Core::CpuStateFrame *Frame, FEX::HLE::clone3_args *args); + inline static int RemapFromX86Flags(int flags) { #ifdef _M_X86_64 // Nothing to change here diff --git a/Source/Tests/LinuxSyscalls/Syscalls/Thread.cpp b/Source/Tests/LinuxSyscalls/Syscalls/Thread.cpp index bfefdd9aa..3d27dc7b7 100644 --- a/Source/Tests/LinuxSyscalls/Syscalls/Thread.cpp +++ b/Source/Tests/LinuxSyscalls/Syscalls/Thread.cpp @@ -33,7 +33,8 @@ $end_info$ ARG_TO_STR(idtype_t, "%u") namespace FEX::HLE { - FEXCore::Core::InternalThreadState *CreateNewThread(FEXCore::Context:: Context *CTX, FEXCore::Core::CpuStateFrame *Frame, uint32_t flags, void *stack, pid_t *parent_tid, pid_t *child_tid, void *tls) { + FEXCore::Core::InternalThreadState *CreateNewThread(FEXCore::Context:: Context *CTX, FEXCore::Core::CpuStateFrame *Frame, FEX::HLE::clone3_args *args) { + uint64_t flags = args->flags; FEXCore::Core::CPUState NewThreadState{}; // Clone copies the parent thread's state memcpy(&NewThreadState, Frame, sizeof(FEXCore::Core::CPUState)); @@ -41,21 +42,21 @@ namespace FEX::HLE { NewThreadState.gregs[FEXCore::X86State::REG_RAX] = 0; NewThreadState.gregs[FEXCore::X86State::REG_RBX] = 0; NewThreadState.gregs[FEXCore::X86State::REG_RBP] = 0; - NewThreadState.gregs[FEXCore::X86State::REG_RSP] = reinterpret_cast(stack); + NewThreadState.gregs[FEXCore::X86State::REG_RSP] = args->stack; - auto NewThread = FEXCore::Context::CreateThread(CTX, &NewThreadState, reinterpret_cast(parent_tid)); + auto NewThread = FEXCore::Context::CreateThread(CTX, &NewThreadState, args->parent_tid); FEXCore::Context::InitializeThread(CTX, NewThread); if (FEX::HLE::_SyscallHandler->Is64BitMode()) { if (flags & CLONE_SETTLS) { - x64::SetThreadArea(NewThread->CurrentFrame, tls); + x64::SetThreadArea(NewThread->CurrentFrame, reinterpret_cast(args->tls)); } // Set us to start just after the syscall instruction x64::AdjustRipForNewThread(NewThread->CurrentFrame); } else { if (flags & CLONE_SETTLS) { - x32::SetThreadArea(NewThread->CurrentFrame, tls); + x32::SetThreadArea(NewThread->CurrentFrame, reinterpret_cast(args->tls)); } x32::AdjustRipForNewThread(NewThread->CurrentFrame); } @@ -65,20 +66,32 @@ namespace FEX::HLE { // Sets the child TID to pointer in ParentTID if (flags & CLONE_PARENT_SETTID) { - *parent_tid = Result; + *reinterpret_cast(args->parent_tid) = Result; } // Sets the child TID to the pointer in ChildTID if (flags & CLONE_CHILD_SETTID) { - NewThread->ThreadManager.set_child_tid = child_tid; - *child_tid = Result; + NewThread->ThreadManager.set_child_tid = reinterpret_cast(args->child_tid); + *reinterpret_cast(args->child_tid) = Result; } // When the thread exits, clear the child thread ID at ChildTID // Additionally wakeup a futex at that address // Address /may/ be changed with SET_TID_ADDRESS syscall if (flags & CLONE_CHILD_CLEARTID) { - NewThread->ThreadManager.clear_child_tid = child_tid; + NewThread->ThreadManager.clear_child_tid = reinterpret_cast(args->child_tid); + } + + // clone3 flag + if (flags & CLONE_PIDFD) { + // Use pidfd_open to emulate this flag + int pidfd = ::syscall(SYS_pidfd_open, Result, 0); + if (Result == ~0ULL) { + LogMan::Msg::E("Couldn't get pidfd of TID %d\n", Result); + } + else { + *reinterpret_cast(args->pidfd) = pidfd; + } } return NewThread; @@ -162,6 +175,12 @@ namespace FEX::HLE { return ForkGuest(Frame->Thread, Frame, 0, 0, 0, 0, 0); }); + REGISTER_SYSCALL_IMPL(clone3, ([](FEXCore::Core::CpuStateFrame *Frame, FEX::HLE::clone3_args *cl_args, size_t size) -> uint64_t { + FEX::HLE::clone3_args args{}; + memcpy(&args, cl_args, std::min(sizeof(FEX::HLE::clone3_args), size)); + return CloneHandler(Frame, &args); + })); + REGISTER_SYSCALL_IMPL(exit, [](FEXCore::Core::CpuStateFrame *Frame, int status) -> uint64_t { auto Thread = Frame->Thread; if (Thread->ThreadManager.clear_child_tid) { diff --git a/Source/Tests/LinuxSyscalls/Syscalls/Thread.h b/Source/Tests/LinuxSyscalls/Syscalls/Thread.h index 8ee7e4c7e..23f290a33 100644 --- a/Source/Tests/LinuxSyscalls/Syscalls/Thread.h +++ b/Source/Tests/LinuxSyscalls/Syscalls/Thread.h @@ -14,6 +14,6 @@ struct CPUState; } namespace FEX::HLE { - FEXCore::Core::InternalThreadState *CreateNewThread(FEXCore::Context::Context *CTX, FEXCore::Core::CpuStateFrame *Frame, uint32_t flags, void *stack, pid_t *parent_tid, pid_t *child_tid, void *tls); + FEXCore::Core::InternalThreadState *CreateNewThread(FEXCore::Context::Context *CTX, FEXCore::Core::CpuStateFrame *Frame, FEX::HLE::clone3_args *args); uint64_t ForkGuest(FEXCore::Core::InternalThreadState *Thread, FEXCore::Core::CpuStateFrame *Frame, uint32_t flags, void *stack, pid_t *parent_tid, pid_t *child_tid, void *tls); } diff --git a/Source/Tests/LinuxSyscalls/x32/Thread.cpp b/Source/Tests/LinuxSyscalls/x32/Thread.cpp index 4aa887ffe..2606051bc 100644 --- a/Source/Tests/LinuxSyscalls/x32/Thread.cpp +++ b/Source/Tests/LinuxSyscalls/x32/Thread.cpp @@ -43,99 +43,23 @@ namespace FEX::HLE::x32 { Frame->State.rip += 2; } - static bool AnyFlagsSet(uint64_t Flags, uint64_t Mask) { - return (Flags & Mask) != 0; - } - - static bool AllFlagsSet(uint64_t Flags, uint64_t Mask) { - return (Flags & Mask) == Mask; - } - void RegisterThread() { - REGISTER_SYSCALL_IMPL_X32(clone, [](FEXCore::Core::CpuStateFrame *Frame, uint32_t flags, void *stack, pid_t *parent_tid, void *tls, pid_t *child_tid) -> uint64_t { - #define FLAGPRINT(x, y) if (flags & (y)) LogMan::Msg::I("\tFlag: " #x) - FLAGPRINT(CSIGNAL, 0x000000FF); - FLAGPRINT(CLONE_VM, 0x00000100); - FLAGPRINT(CLONE_FS, 0x00000200); - FLAGPRINT(CLONE_FILES, 0x00000400); - FLAGPRINT(CLONE_SIGHAND, 0x00000800); - FLAGPRINT(CLONE_PTRACE, 0x00002000); - FLAGPRINT(CLONE_VFORK, 0x00004000); - FLAGPRINT(CLONE_PARENT, 0x00008000); - FLAGPRINT(CLONE_THREAD, 0x00010000); - FLAGPRINT(CLONE_NEWNS, 0x00020000); - FLAGPRINT(CLONE_SYSVSEM, 0x00040000); - FLAGPRINT(CLONE_SETTLS, 0x00080000); - FLAGPRINT(CLONE_PARENT_SETTID, 0x00100000); - FLAGPRINT(CLONE_CHILD_CLEARTID, 0x00200000); - FLAGPRINT(CLONE_DETACHED, 0x00400000); - FLAGPRINT(CLONE_UNTRACED, 0x00800000); - FLAGPRINT(CLONE_CHILD_SETTID, 0x01000000); - FLAGPRINT(CLONE_NEWCGROUP, 0x02000000); - FLAGPRINT(CLONE_NEWUTS, 0x04000000); - FLAGPRINT(CLONE_NEWIPC, 0x08000000); - FLAGPRINT(CLONE_NEWUSER, 0x10000000); - FLAGPRINT(CLONE_NEWPID, 0x20000000); - FLAGPRINT(CLONE_NEWNET, 0x40000000); - FLAGPRINT(CLONE_IO, 0x80000000); - - auto Thread = Frame->Thread; - - if (AnyFlagsSet(flags, CLONE_UNTRACED | CLONE_PTRACE)) { - LogMan::Msg::D("clone: Ptrace* not supported"); - } - - if (AnyFlagsSet(flags, CLONE_NEWNS | CLONE_NEWCGROUP | CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWNET)) { - // NEWUSER doesn't need any privileges from 3.8 onward - // We just don't support it yet - LogMan::Msg::I("Unconditionally returning EPERM on clone namespace"); - return -EPERM; - } - - if (!(flags & CLONE_THREAD)) { - - if (flags & CLONE_VFORK) { - flags &= ~CLONE_VFORK; - flags &= ~CLONE_VM; - LogMan::Msg::D("clone: WARNING: CLONE_VFORK w/o CLONE_THREAD"); - } - - if (AnyFlagsSet(flags, CLONE_SYSVSEM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_VM)) { - LogMan::Msg::I("clone: Unsuported flags w/o CLONE_THREAD (Shared Resources), %X", flags); - return -EPERM; - } - - // CLONE_PARENT is ignored (Implied by CLONE_THREAD) - return FEX::HLE::ForkGuest(Thread, Frame, flags, stack, parent_tid, child_tid, tls); - } else { - - if (!AllFlagsSet(flags, CLONE_SYSVSEM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND)) { - LogMan::Msg::I("clone: CLONE_THREAD: Unsuported flags w/ CLONE_THREAD (Shared Resources), %X", flags); - return -EPERM; - } - - auto NewThread = FEX::HLE::CreateNewThread(Thread->CTX, Frame, flags, stack, parent_tid, child_tid, tls); - - // Return the new threads TID - uint64_t Result = NewThread->ThreadManager.GetTID(); - - if (flags & CLONE_VFORK) { - NewThread->DestroyedByParent = true; - } - - // Actually start the thread - FEXCore::Context::RunThread(Thread->CTX, NewThread); - - if (flags & CLONE_VFORK) { - // If VFORK is set then the calling process is suspended until the thread exits with execve or exit - NewThread->ExecutionThread->join(nullptr); - - // Normally a thread cleans itself up on exit. But because we need to join, we are now responsible - FEXCore::Context::DestroyThread(Thread->CTX, NewThread); - } - SYSCALL_ERRNO(); - } - }); + REGISTER_SYSCALL_IMPL_X32(clone, ([](FEXCore::Core::CpuStateFrame *Frame, uint32_t flags, void *stack, pid_t *parent_tid, void *tls, pid_t *child_tid) -> uint64_t { + FEX::HLE::clone3_args args { + .flags = flags & ~CSIGNAL, // This no longer contains CSIGNAL + .pidfd = reinterpret_cast(parent_tid), // For clone, pidfd is duplicated here + .child_tid = reinterpret_cast(child_tid), + .parent_tid = reinterpret_cast(parent_tid), + .exit_signal = flags & CSIGNAL, + .stack = reinterpret_cast(stack), + .stack_size = ~0ULL, // This syscall isn't able to see the stack size + .tls = reinterpret_cast(tls), + .set_tid = 0, // This syscall isn't able to select TIDs + .set_tid_size = 0, + .cgroup = 0, // This syscall can't select cgroups + }; + return CloneHandler(Frame, &args); + })); REGISTER_SYSCALL_IMPL_X32(waitpid, [](FEXCore::Core::CpuStateFrame *Frame, pid_t pid, int32_t *status, int32_t options) -> uint64_t { uint64_t Result = ::waitpid(pid, status, options); diff --git a/Source/Tests/LinuxSyscalls/x64/Thread.cpp b/Source/Tests/LinuxSyscalls/x64/Thread.cpp index f2463b718..16ddb69ac 100644 --- a/Source/Tests/LinuxSyscalls/x64/Thread.cpp +++ b/Source/Tests/LinuxSyscalls/x64/Thread.cpp @@ -32,15 +32,24 @@ namespace FEX::HLE::x64 { Frame->State.rip += 2; } - static bool AnyFlagsSet(uint64_t Flags, uint64_t Mask) { - return (Flags & Mask) != 0; - } - - static bool AllFlagsSet(uint64_t Flags, uint64_t Mask) { - return (Flags & Mask) == Mask; - } - void RegisterThread() { + REGISTER_SYSCALL_IMPL_X64(clone, ([](FEXCore::Core::CpuStateFrame *Frame, uint32_t flags, void *stack, pid_t *parent_tid, pid_t *child_tid, void *tls) -> uint64_t { + FEX::HLE::clone3_args args { + .flags = flags & ~CSIGNAL, // This no longer contains CSIGNAL + .pidfd = reinterpret_cast(parent_tid), // For clone, pidfd is duplicated here + .child_tid = reinterpret_cast(child_tid), + .parent_tid = reinterpret_cast(parent_tid), + .exit_signal = flags & CSIGNAL, + .stack = reinterpret_cast(stack), + .stack_size = ~0ULL, // This syscall isn't able to see the stack size + .tls = reinterpret_cast(tls), + .set_tid = 0, // This syscall isn't able to select TIDs + .set_tid_size = 0, + .cgroup = 0, // This syscall can't select cgroups + }; + return CloneHandler(Frame, &args); + })); + REGISTER_SYSCALL_IMPL_X64(futex, [](FEXCore::Core::CpuStateFrame *Frame, int *uaddr, int futex_op, int val, const struct timespec *timeout, int *uaddr2, uint32_t val3) -> uint64_t { uint64_t Result = syscall(SYS_futex, uaddr, @@ -52,93 +61,6 @@ namespace FEX::HLE::x64 { SYSCALL_ERRNO(); }); - REGISTER_SYSCALL_IMPL_X64(clone, [](FEXCore::Core::CpuStateFrame *Frame, uint32_t flags, void *stack, pid_t *parent_tid, pid_t *child_tid, void *tls) -> uint64_t { - #define FLAGPRINT(x, y) if (flags & (y)) LogMan::Msg::I("\tFlag: " #x) - FLAGPRINT(CSIGNAL, 0x000000FF); - FLAGPRINT(CLONE_VM, 0x00000100); - FLAGPRINT(CLONE_FS, 0x00000200); - FLAGPRINT(CLONE_FILES, 0x00000400); - FLAGPRINT(CLONE_SIGHAND, 0x00000800); - FLAGPRINT(CLONE_PTRACE, 0x00002000); - FLAGPRINT(CLONE_VFORK, 0x00004000); - FLAGPRINT(CLONE_PARENT, 0x00008000); - FLAGPRINT(CLONE_THREAD, 0x00010000); - FLAGPRINT(CLONE_NEWNS, 0x00020000); - FLAGPRINT(CLONE_SYSVSEM, 0x00040000); - FLAGPRINT(CLONE_SETTLS, 0x00080000); - FLAGPRINT(CLONE_PARENT_SETTID, 0x00100000); - FLAGPRINT(CLONE_CHILD_CLEARTID, 0x00200000); - FLAGPRINT(CLONE_DETACHED, 0x00400000); - FLAGPRINT(CLONE_UNTRACED, 0x00800000); - FLAGPRINT(CLONE_CHILD_SETTID, 0x01000000); - FLAGPRINT(CLONE_NEWCGROUP, 0x02000000); - FLAGPRINT(CLONE_NEWUTS, 0x04000000); - FLAGPRINT(CLONE_NEWIPC, 0x08000000); - FLAGPRINT(CLONE_NEWUSER, 0x10000000); - FLAGPRINT(CLONE_NEWPID, 0x20000000); - FLAGPRINT(CLONE_NEWNET, 0x40000000); - FLAGPRINT(CLONE_IO, 0x80000000); - - auto Thread = Frame->Thread; - - if (AnyFlagsSet(flags, CLONE_UNTRACED | CLONE_PTRACE)) { - LogMan::Msg::D("clone: Ptrace* not supported"); - } - - if (AnyFlagsSet(flags, CLONE_NEWNS | CLONE_NEWCGROUP | CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWNET)) { - // NEWUSER doesn't need any privileges from 3.8 onward - // We just don't support it yet - LogMan::Msg::I("Unconditionally returning EPERM on clone namespace"); - return -EPERM; - } - - if (!(flags & CLONE_THREAD)) { - - if (flags & CLONE_VFORK) { - flags &= ~CLONE_VFORK; - flags &= ~CLONE_VM; - LogMan::Msg::D("clone: WARNING: CLONE_VFORK w/o CLONE_THREAD"); - } - - if (AnyFlagsSet(flags, CLONE_SYSVSEM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_VM)) { - LogMan::Msg::I("clone: Unsuported flags w/o CLONE_THREAD (Shared Resources), %X", flags); - return -EPERM; - } - - // CLONE_PARENT is ignored (Implied by CLONE_THREAD) - - return FEX::HLE::ForkGuest(Thread, Frame, flags, stack, parent_tid, child_tid, tls); - } else { - - if (!AllFlagsSet(flags, CLONE_SYSVSEM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND)) { - LogMan::Msg::I("clone: CLONE_THREAD: Unsuported flags w/ CLONE_THREAD (Shared Resources), %X", flags); - return -EPERM; - } - - auto NewThread = FEX::HLE::CreateNewThread(Thread->CTX, Frame, flags, stack, parent_tid, child_tid, tls); - - // Return the new threads TID - uint64_t Result = NewThread->ThreadManager.GetTID(); - LogMan::Msg::D("Child [%d] starting at: 0x%lx. Parent was at 0x%lx", Result, NewThread->CurrentFrame->State.rip, Thread->CurrentFrame->State.rip); - - if (flags & CLONE_VFORK) { - NewThread->DestroyedByParent = true; - } - - // Actually start the thread - FEXCore::Context::RunThread(Thread->CTX, NewThread); - - if (flags & CLONE_VFORK) { - // If VFORK is set then the calling process is suspended until the thread exits with execve or exit - NewThread->ExecutionThread->join(nullptr); - - // Normally a thread cleans itself up on exit. But because we need to join, we are now responsible - FEXCore::Context::DestroyThread(Thread->CTX, NewThread); - } - SYSCALL_ERRNO(); - } - }); - REGISTER_SYSCALL_IMPL_X64(set_robust_list, [](FEXCore::Core::CpuStateFrame *Frame, struct robust_list_head *head, size_t len) -> uint64_t { auto Thread = Frame->Thread; Thread->ThreadManager.robust_list_head = reinterpret_cast(head);