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; 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"); } 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/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/FileManagement.cpp b/Source/Tests/LinuxSyscalls/FileManagement.cpp index 3c3e039fd..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 @@ -184,6 +185,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; @@ -335,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 c7ac5eee3..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; @@ -32,6 +34,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); @@ -41,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.cpp b/Source/Tests/LinuxSyscalls/Syscalls.cpp index 142bfcbcb..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 @@ -95,7 +97,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 +136,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 +160,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,11 +184,136 @@ 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(); } +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); @@ -253,6 +390,8 @@ SyscallHandler::SyscallHandler(FEXCore::Context::Context *ctx, FEX::HLE::SignalD , SignalDelegation {_SignalDelegation} { FEX::HLE::_SyscallHandler = this; HostKernelVersion = CalculateHostKernelVersion(); + GuestKernelVersion = CalculateGuestKernelVersion(); + } SyscallHandler::~SyscallHandler() { @@ -278,6 +417,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 ee98353c2..ba69bd0a2 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(); @@ -42,7 +43,7 @@ class SyscallHandler; void RegisterSched(); void RegisterSemaphore(); void RegisterSHM(); - void RegisterSignals(); + void RegisterSignals(FEX::HLE::SyscallHandler *const Handler); void RegisterSocket(); void RegisterThread(); void RegisterTime(); @@ -53,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: @@ -114,12 +120,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 +145,7 @@ protected: // (Major << 24) | (Minor << 16) | Patch uint32_t HostKernelVersion{}; + uint32_t GuestKernelVersion{}; private: @@ -247,6 +261,27 @@ 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; +}; + +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/FD.cpp b/Source/Tests/LinuxSyscalls/Syscalls/FD.cpp index 0302b642d..eb44970ac 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 { @@ -319,5 +330,25 @@ 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, 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); + SYSCALL_ERRNO(); + }); + } + else { + REGISTER_SYSCALL_IMPL(close_range, UnimplementedSyscallSafe); + } } } 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/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!"); 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/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/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/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 2536d93ee..67d448a9d 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(); @@ -662,13 +662,14 @@ public: FEX::HLE::RegisterFS(); FEX::HLE::RegisterInfo(); FEX::HLE::RegisterIO(); + FEX::HLE::RegisterIOUring(this); FEX::HLE::RegisterKey(); FEX::HLE::RegisterMemory(); FEX::HLE::RegisterMsg(); 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(); @@ -677,7 +678,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/x32/Thread.cpp b/Source/Tests/LinuxSyscalls/x32/Thread.cpp index de97473d5..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); @@ -294,9 +218,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/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/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 d5fd9332f..14c5abe56 100644 --- a/Source/Tests/LinuxSyscalls/x64/Syscalls.cpp +++ b/Source/Tests/LinuxSyscalls/x64/Syscalls.cpp @@ -13,12 +13,12 @@ $end_info$ #include namespace FEX::HLE::x64 { - void RegisterEpoll(); + void RegisterEpoll(FEX::HLE::SyscallHandler *const Handler); void RegisterFD(); void RegisterInfo(); void RegisterIO(); void RegisterIoctl(); - void RegisterMemory(); + void RegisterMemory(FEX::HLE::SyscallHandler *const Handler); void RegisterMsg(); void RegisterSched(); void RegisterSocket(); @@ -103,13 +103,14 @@ 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(); 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(); @@ -118,12 +119,12 @@ 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(); 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(); diff --git a/Source/Tests/LinuxSyscalls/x64/Thread.cpp b/Source/Tests/LinuxSyscalls/x64/Thread.cpp index c275cf8b8..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); @@ -173,9 +95,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();