/* $info$ tags: LinuxSyscalls|syscalls-x86-64 $end_info$ */ #include "Tests/LinuxSyscalls/Syscalls.h" #include "Tests/LinuxSyscalls/x64/Syscalls.h" #include "Tests/LinuxSyscalls/x64/Thread.h" #include "Tests/LinuxSyscalls/Syscalls/Thread.h" #include #include #include #include #include #include #include #include #include #include namespace FEX::HLE::x64 { uint64_t SetThreadArea(FEXCore::Core::CpuStateFrame *Frame, void *tls) { Frame->State.fs = reinterpret_cast(tls); return 0; } void AdjustRipForNewThread(FEXCore::Core::CpuStateFrame *Frame) { 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(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, futex_op, val, timeout, uaddr2, val3); 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)) { ERROR_AND_DIE("clone: Unsuported flags w/o CLONE_THREAD (Shared Resources), %X", flags); } // 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)) { ERROR_AND_DIE("clone: CLONE_THREAD: Unsuported flags w/ CLONE_THREAD (Shared Resources), %X", flags); } 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); uint64_t Result = ::syscall(SYS_set_robust_list, head, len); SYSCALL_ERRNO(); }); REGISTER_SYSCALL_IMPL_X64(get_robust_list, [](FEXCore::Core::CpuStateFrame *Frame, int pid, struct robust_list_head **head, size_t *len_ptr) -> uint64_t { uint64_t Result = ::syscall(SYS_get_robust_list, pid, head, len_ptr); SYSCALL_ERRNO(); }); REGISTER_SYSCALL_IMPL_X64(sigaltstack, [](FEXCore::Core::CpuStateFrame *Frame, const stack_t *ss, stack_t *old_ss) -> uint64_t { return FEX::HLE::_SyscallHandler->GetSignalDelegator()->RegisterGuestSigAltStack(ss, old_ss); }); // launch a new process under fex // currently does not propagate argv[0] correctly REGISTER_SYSCALL_IMPL_X64(execve, [](FEXCore::Core::CpuStateFrame *Frame, const char *pathname, char *const argv[], char *const envp[]) -> 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); return FEX::HLE::ExecveHandler(pathname, Args, Envp); }); 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(); }); } }