mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-10 04:03:44 +02:00
Syscalls on x86-64 have a gap in the range of [335, 424) where these are defined as returning ENOSYS. This was a decision that the Linux developers decided on so x86-64 can realign its syscall numbers to a common infrastructure. Turns out that Proton Experimental was using a 32-bit only syscall to check if the feature existed and was listening for ENOSYS. They did this unconditionally on both x86 and x86-64 and it hits this gap section. This was introduced in Proton Experimental with commit 563fb0fbe2a49734aede87bedccb20daefc553b0 Message: "ntdll: Use clock_gettime64 if supported." This is technically valid since it falls within this gap section but leaves a bad taste. We can safely ignore this section so set it up with the UnimplementedSyscallSafe handler and also assign it to Syscall MAX so it can go down our fast handler. Not that this is likely to be a performance critical path. Fixes Proton Experimental crashing when run under FEX.
190 lines
5.6 KiB
C++
190 lines
5.6 KiB
C++
/*
|
|
$info$
|
|
tags: LinuxSyscalls|syscalls-x86-64
|
|
$end_info$
|
|
*/
|
|
|
|
#include "Tests/LinuxSyscalls/Syscalls.h"
|
|
#include "Tests/LinuxSyscalls/x64/Syscalls.h"
|
|
#include "Tests/LinuxSyscalls/x64/SyscallsEnum.h"
|
|
|
|
#include <FEXCore/HLE/SyscallHandler.h>
|
|
|
|
#include <map>
|
|
|
|
namespace FEX::HLE::x64 {
|
|
void RegisterEpoll(FEX::HLE::SyscallHandler *const Handler);
|
|
void RegisterFD();
|
|
void RegisterInfo();
|
|
void RegisterIO();
|
|
void RegisterIoctl();
|
|
void RegisterMemory(FEX::HLE::SyscallHandler *const Handler);
|
|
void RegisterMsg();
|
|
void RegisterSched();
|
|
void RegisterSocket();
|
|
void RegisterSemaphore();
|
|
void RegisterSignals();
|
|
void RegisterThread();
|
|
void RegisterTime();
|
|
void RegisterNotImplemented();
|
|
|
|
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
|
|
[[nodiscard]] static const char* GetSyscallName(int SyscallNumber) {
|
|
static const std::map<int, const char*> SyscallNames = {
|
|
#include "SyscallsNames.inl"
|
|
};
|
|
|
|
const auto EntryIter = SyscallNames.find(SyscallNumber);
|
|
if (EntryIter == SyscallNames.cend()) {
|
|
return "[unknown syscall]";
|
|
}
|
|
|
|
return EntryIter->second;
|
|
}
|
|
#endif
|
|
|
|
struct InternalSyscallDefinition {
|
|
int SyscallNumber;
|
|
void* SyscallHandler;
|
|
int ArgumentCount;
|
|
int32_t HostSyscallNumber;
|
|
#ifdef DEBUG_STRACE
|
|
std::string TraceFormatString;
|
|
#endif
|
|
};
|
|
|
|
std::vector<InternalSyscallDefinition> syscalls_x64;
|
|
|
|
void RegisterSyscallInternal(int SyscallNumber,
|
|
int32_t HostSyscallNumber,
|
|
#ifdef DEBUG_STRACE
|
|
const std::string& TraceFormatString,
|
|
#endif
|
|
void* SyscallHandler, int ArgumentCount) {
|
|
syscalls_x64.push_back({SyscallNumber,
|
|
SyscallHandler,
|
|
ArgumentCount,
|
|
HostSyscallNumber,
|
|
#ifdef DEBUG_STRACE
|
|
TraceFormatString
|
|
#endif
|
|
});
|
|
}
|
|
|
|
class x64SyscallHandler final : public FEX::HLE::SyscallHandler {
|
|
public:
|
|
x64SyscallHandler(FEXCore::Context::Context *ctx, FEX::HLE::SignalDelegator *_SignalDelegation);
|
|
|
|
private:
|
|
void RegisterSyscallHandlers();
|
|
};
|
|
|
|
x64SyscallHandler::x64SyscallHandler(FEXCore::Context::Context *ctx, FEX::HLE::SignalDelegator *_SignalDelegation)
|
|
: SyscallHandler {ctx, _SignalDelegation} {
|
|
OSABI = FEXCore::HLE::SyscallOSABI::OS_LINUX64;
|
|
|
|
RegisterSyscallHandlers();
|
|
}
|
|
|
|
void x64SyscallHandler::RegisterSyscallHandlers() {
|
|
Definitions.resize(FEX::HLE::x64::SYSCALL_x64_MAX);
|
|
auto cvt = [](auto in) {
|
|
union {
|
|
decltype(in) val;
|
|
void *raw;
|
|
} raw;
|
|
raw.val = in;
|
|
return raw.raw;
|
|
};
|
|
|
|
// Clear all definitions
|
|
for (auto &Def : Definitions) {
|
|
Def.NumArgs = 255;
|
|
Def.Ptr = cvt(&UnimplementedSyscall);
|
|
}
|
|
|
|
FEX::HLE::RegisterEpoll();
|
|
FEX::HLE::RegisterFD(this);
|
|
FEX::HLE::RegisterFS(this);
|
|
FEX::HLE::RegisterInfo();
|
|
FEX::HLE::RegisterIO();
|
|
FEX::HLE::RegisterIOUring(this);
|
|
FEX::HLE::RegisterKey();
|
|
FEX::HLE::RegisterMemory();
|
|
FEX::HLE::RegisterMsg();
|
|
FEX::HLE::RegisterNamespace(this);
|
|
FEX::HLE::RegisterSched();
|
|
FEX::HLE::RegisterSemaphore();
|
|
FEX::HLE::RegisterSHM();
|
|
FEX::HLE::RegisterSignals(this);
|
|
FEX::HLE::RegisterSocket();
|
|
FEX::HLE::RegisterThread(this);
|
|
FEX::HLE::RegisterTime();
|
|
FEX::HLE::RegisterTimer();
|
|
FEX::HLE::RegisterNotImplemented();
|
|
FEX::HLE::RegisterStubs();
|
|
|
|
// 64bit specific
|
|
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(this);
|
|
FEX::HLE::x64::RegisterMsg();
|
|
FEX::HLE::x64::RegisterSched();
|
|
FEX::HLE::x64::RegisterSocket();
|
|
FEX::HLE::x64::RegisterSemaphore();
|
|
FEX::HLE::x64::RegisterSignals();
|
|
FEX::HLE::x64::RegisterThread();
|
|
FEX::HLE::x64::RegisterTime();
|
|
FEX::HLE::x64::RegisterNotImplemented();
|
|
|
|
// Set all the new definitions
|
|
for (auto &Syscall : syscalls_x64) {
|
|
auto SyscallNumber = Syscall.SyscallNumber;
|
|
auto &Def = Definitions.at(SyscallNumber);
|
|
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
|
|
auto Name = GetSyscallName(SyscallNumber);
|
|
LOGMAN_THROW_A_FMT(Def.Ptr == cvt(&UnimplementedSyscall), "Oops overwriting sysall problem, {}, {}", SyscallNumber, Name);
|
|
#endif
|
|
Def.Ptr = Syscall.SyscallHandler;
|
|
Def.NumArgs = Syscall.ArgumentCount;
|
|
Def.HostSyscallNumber = Syscall.HostSyscallNumber;
|
|
#ifdef DEBUG_STRACE
|
|
Def.StraceFmt = Syscall.TraceFormatString;
|
|
#endif
|
|
}
|
|
|
|
// x86-64 has a gap of syscalls in the range of [335, 424) where there aren't any
|
|
// These are defined that these must return -ENOSYS
|
|
// This allows x86-64 to start using the common syscall numbers
|
|
// Fill the gap to ensure that FEX doesn't assert
|
|
constexpr int SYSCALL_GAP_BEGIN = 335;
|
|
constexpr int SYSCALL_GAP_END = 424;
|
|
for (int SyscallNumber = SYSCALL_GAP_BEGIN; SyscallNumber < SYSCALL_GAP_END; ++SyscallNumber) {
|
|
auto &Def = Definitions.at(SyscallNumber);
|
|
Def.Ptr = cvt(&UnimplementedSyscallSafe);
|
|
Def.NumArgs = 0;
|
|
// This will allow our syscall optimization code to make this code more optimal
|
|
// Unlikely to hit a hot path though
|
|
Def.HostSyscallNumber = SYSCALL_DEF(MAX);
|
|
#ifdef DEBUG_STRACE
|
|
Def.StraceFmt = "Invalid";
|
|
#endif
|
|
}
|
|
|
|
#if PRINT_MISSING_SYSCALLS
|
|
for (auto &Syscall: SyscallNames) {
|
|
if (Definitions[Syscall.first].Ptr == cvt(&UnimplementedSyscall)) {
|
|
LogMan::Msg::DFmt("Unimplemented syscall: %s", Syscall.second);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
std::unique_ptr<FEX::HLE::SyscallHandler> CreateHandler(FEXCore::Context::Context *ctx, FEX::HLE::SignalDelegator *_SignalDelegation) {
|
|
return std::make_unique<x64SyscallHandler>(ctx, _SignalDelegation);
|
|
}
|
|
}
|