mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-10 02:00:18 +02:00
Merge pull request #1040 from Sonicadvance1/more_syscalls_5_12
Implements more syscalls for supporting a higher guest kernel version
This commit is contained in:
22 files changed
+568
-238
No files matched your search
@@ -175,16 +175,23 @@ bool Dispatcher::HandleGuestSignal(int Signal, void *info, void *ucontext, Guest
|
||||
|
||||
// siginfo_t
|
||||
siginfo_t *HostSigInfo = reinterpret_cast<siginfo_t*>(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;
|
||||
|
||||
@@ -107,7 +107,7 @@ namespace FEXCore::Core {
|
||||
alignas(16) FEXCore::Core::CpuStateFrame BaseFrameState{};
|
||||
|
||||
};
|
||||
static_assert(std::is_standard_layout<InternalThreadState>::value, "This needs to be standard layout");
|
||||
// static_assert(std::is_standard_layout<InternalThreadState>::value, "This needs to be standard layout");
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 <Release> (<Compile By>@<Compile Host>) (<Linux Compiler>) #<version> {SMP, PREEMPT, PREEMPT_RT} <UTS version>\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;
|
||||
|
||||
@@ -6,6 +6,7 @@ $end_info$
|
||||
*/
|
||||
|
||||
#include "Tests/LinuxSyscalls/FileManagement.h"
|
||||
#include "Tests/LinuxSyscalls/Syscalls.h"
|
||||
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
#include <cstring>
|
||||
@@ -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<std::mutex> 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<std::mutex> 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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 <FEXCore/Core/X86Enums.h>
|
||||
#include <FEXCore/Core/CodeLoader.h>
|
||||
#include <FEXCore/Debug/InternalThreadState.h>
|
||||
#include <FEXCore/Utils/Allocator.h>
|
||||
#include <FEXCore/Utils/ELFContainer.h>
|
||||
#include <fcntl.h>
|
||||
@@ -95,7 +97,7 @@ static bool IsSupportedByInterpreter(std::string const &Filename) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t ExecveHandler(const char *pathname, std::vector<const char*> &argv, std::vector<const char*> &envp) {
|
||||
uint64_t ExecveHandler(const char *pathname, std::vector<const char*> &argv, std::vector<const char*> &envp, ExecveAtArgs *Args) {
|
||||
std::string Filename{};
|
||||
|
||||
std::error_code ec;
|
||||
@@ -134,7 +136,12 @@ uint64_t ExecveHandler(const char *pathname, std::vector<const char*> &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<char *const *>(&argv.at(0)), const_cast<char *const *>(&envp.at(0)));
|
||||
if (Args) {
|
||||
Result = ::syscall(SYS_execveat, Args->dirfd, Filename.c_str(), const_cast<char *const *>(&argv.at(0)), const_cast<char *const *>(&envp.at(0)), Args->flags);
|
||||
}
|
||||
else {
|
||||
Result = execve(Filename.c_str(), const_cast<char *const *>(&argv.at(0)), const_cast<char *const *>(&envp.at(0)));
|
||||
}
|
||||
SYSCALL_ERRNO();
|
||||
}
|
||||
|
||||
@@ -153,7 +160,12 @@ uint64_t ExecveHandler(const char *pathname, std::vector<const char*> &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<char *const *>(&argv.at(0)), const_cast<char *const *>(&envp.at(0)));
|
||||
if (Args) {
|
||||
Result = ::syscall(SYS_execveat, Args->dirfd, Filename.c_str(), const_cast<char *const *>(&argv.at(0)), const_cast<char *const *>(&envp.at(0)), Args->flags);
|
||||
}
|
||||
else {
|
||||
Result = execve(Filename.c_str(), const_cast<char *const *>(&argv.at(0)), const_cast<char *const *>(&envp.at(0)));
|
||||
}
|
||||
SYSCALL_ERRNO();
|
||||
}
|
||||
|
||||
@@ -172,11 +184,136 @@ uint64_t ExecveHandler(const char *pathname, std::vector<const char*> &argv, std
|
||||
// Append the arguments together
|
||||
ExecveArgs.insert(ExecveArgs.end(), argv.begin(), argv.end());
|
||||
|
||||
Result = execve("/proc/self/exe", const_cast<char *const *>(&ExecveArgs.at(0)), const_cast<char *const *>(&envp.at(0)));
|
||||
if (Args) {
|
||||
Result = ::syscall(SYS_execveat, Args->dirfd, "/proc/self/exe", const_cast<char *const *>(&ExecveArgs.at(0)), const_cast<char *const *>(&envp.at(0)), Args->flags);
|
||||
}
|
||||
else {
|
||||
Result = execve("/proc/self/exe", const_cast<char *const *>(&ExecveArgs.at(0)), const_cast<char *const *>(&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<void*>(args->stack),
|
||||
reinterpret_cast<pid_t*>(args->parent_tid),
|
||||
reinterpret_cast<pid_t*>(args->child_tid),
|
||||
reinterpret_cast<void*>(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<std::mutex> 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;
|
||||
|
||||
@@ -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<const char*> &argv, std::vector<const char*> &envp);
|
||||
struct ExecveAtArgs {
|
||||
int dirfd;
|
||||
int flags;
|
||||
};
|
||||
|
||||
uint64_t ExecveHandler(const char *pathname, std::vector<const char*> &argv, std::vector<const char*> &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<SyscallFunctionDefinition> 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<epoll_event_x86>::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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 <signal.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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!");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -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<uint64_t>(stack);
|
||||
NewThreadState.gregs[FEXCore::X86State::REG_RSP] = args->stack;
|
||||
|
||||
auto NewThread = FEXCore::Context::CreateThread(CTX, &NewThreadState, reinterpret_cast<uint64_t>(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<void*>(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<void*>(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<pid_t*>(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<int32_t*>(args->child_tid);
|
||||
*reinterpret_cast<pid_t*>(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<int32_t*>(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<int*>(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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -21,7 +21,7 @@ $end_info$
|
||||
ARG_TO_STR(FEX::HLE::x32::compat_ptr<FEX::HLE::epoll_event_x86>, "%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<epoll_event_x86> events, int maxevents, int timeout) -> uint64_t {
|
||||
std::vector<struct epoll_event> 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<epoll_event_x86> events, int maxevent, compat_ptr<timespec32> timeout, const uint64_t* sigmask, size_t sigsetsize) -> uint64_t {
|
||||
std::vector<struct epoll_event> 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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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<uint64_t>(parent_tid), // For clone, pidfd is duplicated here
|
||||
.child_tid = reinterpret_cast<uint64_t>(child_tid),
|
||||
.parent_tid = reinterpret_cast<uint64_t>(parent_tid),
|
||||
.exit_signal = flags & CSIGNAL,
|
||||
.stack = reinterpret_cast<uint64_t>(stack),
|
||||
.stack_size = ~0ULL, // This syscall isn't able to see the stack size
|
||||
.tls = reinterpret_cast<uint64_t>(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<const char*> Args;
|
||||
std::vector<const char*> Envp;
|
||||
|
||||
for (int i = 0; argv[i]; i++) {
|
||||
Args.push_back(reinterpret_cast<const char*>(static_cast<uintptr_t>(argv[i])));
|
||||
}
|
||||
|
||||
Args.push_back(nullptr);
|
||||
|
||||
for (int i = 0; envp[i]; i++) {
|
||||
Envp.push_back(reinterpret_cast<const char*>(static_cast<uintptr_t>(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{};
|
||||
|
||||
@@ -19,7 +19,7 @@ $end_info$
|
||||
#include <unistd.h>
|
||||
|
||||
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<struct epoll_event> 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<struct epoll_event> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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<uint64_t>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,12 +13,12 @@ $end_info$
|
||||
#include <map>
|
||||
|
||||
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();
|
||||
|
||||
@@ -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<uint64_t>(parent_tid), // For clone, pidfd is duplicated here
|
||||
.child_tid = reinterpret_cast<uint64_t>(child_tid),
|
||||
.parent_tid = reinterpret_cast<uint64_t>(parent_tid),
|
||||
.exit_signal = flags & CSIGNAL,
|
||||
.stack = reinterpret_cast<uint64_t>(stack),
|
||||
.stack_size = ~0ULL, // This syscall isn't able to see the stack size
|
||||
.tls = reinterpret_cast<uint64_t>(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<uint64_t>(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<const char*> Args;
|
||||
std::vector<const char*> 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();
|
||||
|
||||
Reference in new issue
Block a user