Files
FEX-Emu--FEX/Source/Interface/HLE/Syscalls.cpp
T
2020-03-06 07:48:39 +02:00

465 lines
14 KiB
C++

#include "Common/MathUtils.h"
#include "Interface/Context/Context.h"
#include "Interface/Core/InternalThreadState.h"
#include "Interface/HLE/Syscalls.h"
#include "LogManager.h"
#include <FEXCore/Core/X86Enums.h>
#include <sys/mman.h>
constexpr uint64_t PAGE_SIZE = 4096;
namespace FEXCore {
void SyscallHandler::DefaultProgramBreak(FEXCore::Core::InternalThreadState *Thread, uint64_t Addr) {
DataSpaceSize = 0;
DataSpace = Addr;
DefaultProgramBreakAddress = Addr;
// Just allocate 1GB of data memory past the default program break location at this point
CTX->MapRegion(Thread, Addr, 0x1000'0000);
}
uint64_t SyscallHandler::HandleSyscall(FEXCore::Core::InternalThreadState *Thread, FEXCore::HLE::SyscallArguments *Args) {
uint64_t Result = 0;
LogMan::Msg::D("Syscall: %d", Args->Argument[0]);
switch (Args->Argument[0]) {
case SYSCALL_UNAME: {
struct _utsname {
char sysname[65];
char nodename[65];
char release[65];
char version[65];
char machine[65];
};
_utsname *Local = CTX->MemoryMapper.GetPointer<_utsname*>(Args->Argument[1]);
strcpy(Local->sysname, "Linux");
strcpy(Local->nodename, "FEXCore");
strcpy(Local->release, "5.0.0");
strcpy(Local->version, "#1");
strcpy(Local->machine, "x86_64");
Result = 0;
break;
}
// Memory management
case SYSCALL_BRK: {
LogMan::Msg::D("\tBRK: 0x%lx - 0x%lx", Args->Argument[1], DataSpace);
if (Args->Argument[1] == 0) { // Just wants to get the location of the program break atm
Result = DataSpace + DataSpaceSize;
}
else {
// Allocating out data space
uint64_t NewEnd = Args->Argument[1];
if (NewEnd < DataSpace) {
// Not allowed to move brk end below original start
// Set the size to zero
DataSpaceSize = 0;
}
else {
uint64_t NewSize = NewEnd - DataSpace;
DataSpaceSize = NewSize;
}
Result = DataSpace + DataSpaceSize;
}
break;
}
case SYSCALL_MMAP: {
LogMan::Msg::D("\tMMAP(<addr> %p, <length> 0x%lx, <prot>%d, <flags>0x%x, <fd>%d, <offset>0x%lx)",
Args->Argument[1], Args->Argument[2],
Args->Argument[3], Args->Argument[4],
Args->Argument[5], Args->Argument[6]);
int Flags = Args->Argument[4];
int GuestFD = Args->Argument[5];
int HostFD = -1;
if (GuestFD != -1) {
HostFD = FM.FindHostFD(GuestFD);
}
uint64_t Base = AlignDown(LastMMAP, PAGE_SIZE);
uint64_t Size = AlignUp(Args->Argument[2], PAGE_SIZE);
uint64_t FileSizeToUse = Args->Argument[2];
uint64_t Prot = Args->Argument[3];
#ifdef DEBUG_MMAP
FileSizeToUse = Size;
Prot = PROT_READ | PROT_WRITE | PROT_EXEC;
#endif
if (Flags & MAP_FIXED) {
Base = Args->Argument[1];
void *HostPtr = CTX->MemoryMapper.GetPointer<void*>(Base);
if (!HostPtr) {
HostPtr = CTX->MapRegion(Thread, Base, Size, true);
}
else {
LogMan::Msg::D("\tMapping Fixed pointer in already mapped space: 0x%lx -> %p", Base, HostPtr);
}
if (HostFD != -1) {
#ifdef DEBUG_MMAP
// We are a file. Screw you I'm going to just memcpy you in to place
void *FileMem = mmap(nullptr, FileSizeToUse, Prot, MAP_PRIVATE, HostFD, Args->Argument[6]);
if (FileMem == MAP_FAILED) {
LogMan::Msg::A("Couldn't map file to %p\n", HostPtr);
}
memcpy(HostPtr, FileMem, FileSizeToUse);
munmap(FileMem, Size);
#else
void *FileMem = mmap(HostPtr, FileSizeToUse, Prot, MAP_PRIVATE | MAP_FIXED, HostFD, Args->Argument[6]);
if (FileMem == MAP_FAILED) {
LogMan::Msg::A("Couldn't map file to %p\n", HostPtr);
}
#endif
}
else {
LogMan::Throw::A(Args->Argument[6] == 0, "Don't understand a fixed offset mmap");
}
Result = Base;
}
else {
// XXX: MMAP should map memory regions for all threads
void *HostPtr = CTX->MapRegion(Thread, Base, Size, true);
if (HostFD != -1) {
#ifdef DEBUG_MMAP
// We are a file. Screw you I'm going to just memcpy you in to place
void *FileMem = mmap(nullptr, FileSizeToUse, Prot, MAP_PRIVATE, HostFD, Args->Argument[6]);
if (FileMem == MAP_FAILED) {
LogMan::Msg::A("Couldn't map file to %p\n", HostPtr);
}
memcpy(HostPtr, FileMem, FileSizeToUse);
munmap(FileMem, Size);
#else
void *FileMem = mmap(HostPtr, FileSizeToUse, Prot, MAP_PRIVATE | MAP_FIXED, HostFD, Args->Argument[6]);
if (FileMem == MAP_FAILED) {
LogMan::Msg::A("Couldn't map file to %p\n", HostPtr);
}
#endif
}
LastMMAP += Size;
Result = Base;
}
break;
}
case SYSCALL_MPROTECT: {
LogMan::Msg::D("\tMPROTECT: 0x%x, 0x%lx, 0x%lx", Args->Argument[1], Args->Argument[2], Args->Argument[3]);
void *HostPtr = CTX->MemoryMapper.GetPointer<void*>(Args->Argument[1]);
Result = mprotect(HostPtr, Args->Argument[2], Args->Argument[3]);
break;
}
case SYSCALL_ARCH_PRCTL: {
LogMan::Msg::D("\tPRTCL: 0x%x: 0x%lx", Args->Argument[1], Args->Argument[2]);
switch (Args->Argument[1]) {
case 0x1001: // ARCH_SET_GS
Thread->State.State.gs = Args->Argument[2];
break;
case 0x1002: // ARCH_SET_FS
Thread->State.State.fs = Args->Argument[2];
break;
case 0x1003: // ARCH_GET_FS
*CTX->MemoryMapper.GetPointer<uint64_t*>(Args->Argument[2]) = Thread->State.State.fs;
break;
case 0x1004: // ARCH_GET_GS
*CTX->MemoryMapper.GetPointer<uint64_t*>(Args->Argument[2]) = Thread->State.State.gs;
break;
default:
LogMan::Msg::E("Unknown prctl: 0x%x", Args->Argument[1]);
CTX->ShouldStop = true;
break;
}
Result = 0;
break;
}
// Thread management
case SYSCALL_GETUID:
Result = Thread->State.ThreadManager.GetUID();
break;
case SYSCALL_GETGID:
Result = Thread->State.ThreadManager.GetGID();
break;
case SYSCALL_GETEUID:
Result = Thread->State.ThreadManager.GetEUID();
break;
case SYSCALL_GETEGID:
Result = Thread->State.ThreadManager.GetEGID();
break;
case SYSCALL_GETTID:
Result = Thread->State.ThreadManager.GetTID();
break;
case SYSCALL_GETPID:
Result = Thread->State.ThreadManager.GetPID();
break;
case SYSCALL_EXIT:
LogMan::Msg::D("Thread exit with: %zd\n", Args->Argument[1]);
Thread->State.RunningEvents.ShouldStop = true;
break;
case SYSCALL_WAIT4:
LogMan::Msg::I("wait4(%lx,\n\t%lx,\n\t%lx,\n\t%lx)",
Args->Argument[1],
Args->Argument[2],
Args->Argument[3],
Args->Argument[4]);
break;
// Futexes
case SYSCALL_FUTEX: {
// 0 : uaddr
// 1 : op
// 2: val
// 3: utime
// 4: uaddr2
// 5: val3
LogMan::Msg::I("futex(%lx,\n\t%lx,\n\t%lx,\n\t%lx,\n\t%lx,\n\t%lx)",
Args->Argument[1],
Args->Argument[2],
Args->Argument[3],
Args->Argument[4],
Args->Argument[5],
Args->Argument[6]);
uint8_t Command = Args->Argument[2] & 0xF;
Result = 0;
switch (Command) {
case 0: { // WAIT
LogMan::Throw::A(!Args->Argument[4], "Can't handle timed futexes");
Futex *futex = new Futex{}; // XXX: Definitely a memory leak. When should we free this?
futex->Addr = CTX->MemoryMapper.GetPointer<std::atomic<uint32_t>*>(Args->Argument[1]);
futex->Val = Args->Argument[3];
EmplaceFutex(Args->Argument[1], futex);
{
std::unique_lock<std::mutex> lk(futex->Mutex);
futex->cv.wait(lk, [futex] { return futex->Addr->load() != futex->Val; });
}
break;
}
case 1: { // WAKE
Futex *futex = GetFutex(Args->Argument[1]);
if (!futex) {
Result = 0;
break;
}
for (uint64_t i = 0; i < Args->Argument[3]; ++i)
futex->cv.notify_one();
break;
}
default:
LogMan::Msg::A("Unknown futex command");
break;
}
break;
}
case SYSCALL_CLONE: {
// 0: clone_flags
// 1: New SP
// 2: parent tidptr
// 3: child tidptr
// 4: TLS
LogMan::Msg::I("clone(%lx,\n\t%lx,\n\t%lx,\n\t%lx,\n\t%lx,\n\t%lx,\n\t%lx)",
Args->Argument[0],
Args->Argument[1],
Args->Argument[2],
Args->Argument[3],
Args->Argument[4],
Args->Argument[5]);
uint32_t Flags = Args->Argument[1];
uint64_t NewSP = Args->Argument[2];
uint64_t ParentTID = Args->Argument[3];
uint64_t ChildTID = Args->Argument[4];
uint64_t NewTLS = Args->Argument[5];
#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);
FEXCore::Core::CPUState NewThreadState{};
// Clone copies the parent thread's state
memcpy(&NewThreadState, &Thread->State.State, sizeof(FEXCore::Core::CPUState));
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] = NewSP;
NewThreadState.fs = NewTLS;
// Set us to start just after the syscall instruction
NewThreadState.rip += 2;
auto NewThread = CTX->CreateThread(&NewThreadState, ParentTID, ChildTID);
CTX->CopyMemoryMapping(Thread, NewThread);
// Sets the child TID to pointer in ParentTID
if (Flags & CLONE_PARENT_SETTID) {
uint64_t *TIDPtr = CTX->MemoryMapper.GetPointer<uint64_t*>(ParentTID);
TIDPtr[0] = NewThread->State.ThreadManager.GetTID();
}
// Sets the child TID to the pointer in ChildTID
if (Flags & CLONE_CHILD_SETTID) {
uint64_t *TIDPtr = CTX->MemoryMapper.GetPointer<uint64_t*>(ChildTID);
TIDPtr[0] = NewThread->State.ThreadManager.GetTID();
}
// 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) {
}
CTX->InitializeThread(NewThread);
// Actually start the thread
CTX->RunThread(NewThread);
// Return the new threads TID
Result = NewThread->State.ThreadManager.GetTID();
break;
}
// File management
case SYSCALL_READ:
Result = FM.Read(Args->Argument[1],
CTX->MemoryMapper.GetPointer(Args->Argument[2]),
Args->Argument[3]);
break;
case SYSCALL_WRITE:
Result = FM.Write(Args->Argument[1],
CTX->MemoryMapper.GetPointer(Args->Argument[2]),
Args->Argument[3]);
break;
case SYSCALL_OPEN:
Result = FM.Open(CTX->MemoryMapper.GetPointer<char const*>(Args->Argument[1]),
Args->Argument[2],
Args->Argument[3]);
break;
case SYSCALL_CLOSE:
Result = FM.Close(Args->Argument[1]);
break;
case SYSCALL_STAT:
Result = FM.Stat(CTX->MemoryMapper.GetPointer<char const*>(Args->Argument[1]),
CTX->MemoryMapper.GetPointer(Args->Argument[2]));
break;
case SYSCALL_FSTAT:
Result = FM.Fstat(Args->Argument[1],
CTX->MemoryMapper.GetPointer(Args->Argument[2]));
break;
case SYSCALL_LSEEK:
Result = FM.Lseek(Args->Argument[1],
Args->Argument[2],
Args->Argument[3]);
break;
case SYSCALL_WRITEV:
Result = FM.Writev(Args->Argument[1],
CTX->MemoryMapper.GetPointer(Args->Argument[2]),
Args->Argument[3]);
break;
case SYSCALL_ACCESS:
Result = FM.Access(
CTX->MemoryMapper.GetPointer<const char*>(Args->Argument[1]),
Args->Argument[2]);
break;
case SYSCALL_READLINK:
Result = FM.Readlink(
CTX->MemoryMapper.GetPointer<const char*>(Args->Argument[1]),
CTX->MemoryMapper.GetPointer<char*>(Args->Argument[2]),
Args->Argument[3]);
break;
case SYSCALL_OPENAT:
Result = FM.Openat(
Args->Argument[1],
CTX->MemoryMapper.GetPointer<const char*>(Args->Argument[2]),
Args->Argument[3],
Args->Argument[4]);
break;
case SYSCALL_CLOCK_GETTIME: {
timespec *ClockResult = CTX->MemoryMapper.GetPointer<timespec*>(Args->Argument[2]);
Result = clock_gettime(Args->Argument[1], ClockResult);
// XXX: Debug
// memset(ClockResult, 0, sizeof(timespec));
}
break;
case SYSCALL_NANOSLEEP: {
timespec const* req = CTX->MemoryMapper.GetPointer<timespec const*>(Args->Argument[1]);
timespec *rem = CTX->MemoryMapper.GetPointer<timespec*>(Args->Argument[2]);
Result = nanosleep(req, rem);
break;
}
case SYSCALL_SET_TID_ADDRESS: {
Thread->State.ThreadManager.child_tid = Args->Argument[1];
Result = Thread->State.ThreadManager.GetTID();
break;
}
case SYSCALL_SET_ROBUST_LIST: {
Thread->State.ThreadManager.robust_list_head = Args->Argument[1];
Result = 0;
break;
}
case SYSCALL_PRLIMIT64: {
LogMan::Throw::A(Args->Argument[3] == 0, "Guest trying to set limit for %d", Args->Argument[2]);
struct rlimit {
uint64_t rlim_cur;
uint64_t rlim_max;
};
switch (Args->Argument[2]) {
case 3: { // Stack limits
rlimit *old_limit = CTX->MemoryMapper.GetPointer<rlimit*>(Args->Argument[3]);
// Default size
old_limit->rlim_cur = 8 * 1024;
old_limit->rlim_max = ~0ULL;
break;
}
default: LogMan::Msg::A("Unknown PRLimit: %d", Args->Argument[2]);
}
Result = 0;
break;
}
// Currently unhandled
// Return fake result
case SYSCALL_RT_SIGACTION:
case SYSCALL_RT_SIGPROCMASK:
case SYSCALL_EXIT_GROUP:
case SYSCALL_TGKILL:
case SYSCALL_MUNMAP:
Result = 0;
break;
default:
Result = -1;
LogMan::Msg::A("Unknown syscall: %d", Args->Argument[0]);
break;
}
return Result;
}
}