mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-17 22:00:37 +02:00
Merge pull request #5148 from Sonicadvance1/29
Interpreter: Moves around the thread and ELF initialization code
This commit is contained in:
7 files changed
+193
-191
No files matched your search
@@ -76,7 +76,8 @@ class ELFCodeLoader final : public FEX::CodeLoader {
|
||||
return FEXCore::AlignUp(max_map_address - min_map_address, FEXCore::Utils::FEX_PAGE_SIZE);
|
||||
}
|
||||
|
||||
bool MapFile(const ELFParser& file, uintptr_t Base, const Elf64_Phdr& Header, int prot, int flags, FEX::HLE::SyscallMmapInterface* const Handler) {
|
||||
bool MapFile(const ELFParser& file, uintptr_t Base, const Elf64_Phdr& Header, int prot, int flags,
|
||||
FEX::HLE::SyscallMmapInterface* const Handler, FEXCore::Core::InternalThreadState* Thread) {
|
||||
|
||||
auto addr = Base + PAGE_START(Header.p_vaddr);
|
||||
auto size = Header.p_filesz + PAGE_OFFSET(Header.p_vaddr);
|
||||
@@ -89,7 +90,7 @@ class ELFCodeLoader final : public FEX::CodeLoader {
|
||||
return true;
|
||||
}
|
||||
|
||||
void* rv = Handler->GuestMmap(nullptr, (void*)addr, size, prot, flags, file.fd, off);
|
||||
void* rv = Handler->GuestMmap(Thread, (void*)addr, size, prot, flags, file.fd, off);
|
||||
|
||||
if (FEX::HLE::HasSyscallError(rv)) {
|
||||
// uhoh, something went wrong
|
||||
@@ -124,7 +125,8 @@ class ELFCodeLoader final : public FEX::CodeLoader {
|
||||
return rv;
|
||||
}
|
||||
|
||||
std::optional<uintptr_t> LoadElfFile(ELFParser& Elf, uintptr_t* BrkBase, FEX::HLE::SyscallMmapInterface* const Handler, uint64_t LoadHint = 0) {
|
||||
std::optional<uintptr_t> LoadElfFile(ELFParser& Elf, uintptr_t* BrkBase, FEX::HLE::SyscallMmapInterface* const Handler,
|
||||
FEXCore::Core::InternalThreadState* Thread, uint64_t LoadHint = 0) {
|
||||
|
||||
uintptr_t LoadBase = 0;
|
||||
uintptr_t BrkLoadBase = 0;
|
||||
@@ -135,7 +137,7 @@ class ELFCodeLoader final : public FEX::CodeLoader {
|
||||
// Allocate a base address plus BRK padding.
|
||||
auto TotalSize = CalculateDYNELFSize(Elf.phdrs) + (BrkBase ? BRK_SIZE : 0);
|
||||
LoadBase =
|
||||
(uintptr_t)Handler->GuestMmap(nullptr, reinterpret_cast<void*>(LoadHint), TotalSize, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
(uintptr_t)Handler->GuestMmap(Thread, reinterpret_cast<void*>(LoadHint), TotalSize, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
if (FEX::HLE::HasSyscallError(LoadBase)) {
|
||||
return {};
|
||||
}
|
||||
@@ -154,7 +156,7 @@ class ELFCodeLoader final : public FEX::CodeLoader {
|
||||
int MapProt = MapFlags(Header);
|
||||
int MapType = MAP_PRIVATE | MAP_DENYWRITE | MAP_FIXED;
|
||||
|
||||
if (!MapFile(Elf, LoadBase, Header, MapProt, MapType, Handler)) {
|
||||
if (!MapFile(Elf, LoadBase, Header, MapProt, MapType, Handler, Thread)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -170,7 +172,7 @@ class ELFCodeLoader final : public FEX::CodeLoader {
|
||||
}
|
||||
|
||||
if (BSSPageStart != BSSPageEnd) {
|
||||
auto bss = Handler->GuestMmap(nullptr, (void*)BSSPageStart, BSSPageEnd - BSSPageStart, MapProt, MapType | MAP_ANONYMOUS, -1, 0);
|
||||
auto bss = Handler->GuestMmap(Thread, (void*)BSSPageStart, BSSPageEnd - BSSPageStart, MapProt, MapType | MAP_ANONYMOUS, -1, 0);
|
||||
if (FEX::HLE::HasSyscallError(bss)) {
|
||||
LogMan::Msg::EFmt("Failed to allocate BSS @ {}, {}\n", fmt::ptr(bss), errno);
|
||||
return {};
|
||||
@@ -192,7 +194,7 @@ class ELFCodeLoader final : public FEX::CodeLoader {
|
||||
if (NeedsLateBRKMap) {
|
||||
// Map the BRK after ELF if possible.
|
||||
BrkLoadBase =
|
||||
(uintptr_t)Handler->GuestMmap(nullptr, reinterpret_cast<void*>(BrkLoadBase), BRK_SIZE, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
(uintptr_t)Handler->GuestMmap(Thread, reinterpret_cast<void*>(BrkLoadBase), BRK_SIZE, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
if (FEX::HLE::HasSyscallError(BrkLoadBase)) {
|
||||
// This isn't a catastrophic failure. This just means the BRK conflicted with the ELF.
|
||||
BrkLoadBase = 0;
|
||||
@@ -381,11 +383,12 @@ public:
|
||||
return MainElf.fd;
|
||||
}
|
||||
|
||||
std::optional<uintptr_t> LoadMainElfFile(uintptr_t* BrkBase, FEX::HLE::SyscallMmapInterface* const Handler, uint64_t LoadHint = 0) {
|
||||
return LoadElfFile(MainElf, BrkBase, Handler, LoadHint);
|
||||
std::optional<uintptr_t> LoadMainElfFile(uintptr_t* BrkBase, FEX::HLE::SyscallMmapInterface* const Handler,
|
||||
FEXCore::Core::InternalThreadState* Thread, uint64_t LoadHint = 0) {
|
||||
return LoadElfFile(MainElf, BrkBase, Handler, Thread, LoadHint);
|
||||
}
|
||||
|
||||
bool MapMemory(FEX::HLE::SyscallMmapInterface* const Handler) {
|
||||
bool MapMemory(FEX::HLE::SyscallMmapInterface* const Handler, FEXCore::Core::InternalThreadState* Thread) {
|
||||
for (const auto& Header : MainElf.phdrs) {
|
||||
if (Header.p_type == PT_GNU_STACK) {
|
||||
if (Header.p_flags & PF_X) {
|
||||
@@ -406,6 +409,12 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Thread) {
|
||||
// Update the thread persona.
|
||||
auto ThreadObject = static_cast<FEX::HLE::ThreadStateObject*>(Thread->FrontendPtr);
|
||||
ThreadObject->persona = ::personality(0xffffffff);
|
||||
}
|
||||
|
||||
// What about ASLR and such ?
|
||||
// ADDR_LIMIT_3GB STACK -> 0xc0000000 else -> 0xFFFFe000
|
||||
|
||||
@@ -440,7 +449,7 @@ public:
|
||||
uint64_t StackHint = Is64BitMode() ? STACK_HINT_64 : STACK_HINT_32;
|
||||
|
||||
// Allocate the base of the full 128MB stack range.
|
||||
StackPointerBase = Handler->GuestMmap(nullptr, reinterpret_cast<void*>(StackHint), FULL_STACK_SIZE, PROT_NONE,
|
||||
StackPointerBase = Handler->GuestMmap(Thread, reinterpret_cast<void*>(StackHint), FULL_STACK_SIZE, PROT_NONE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK | MAP_GROWSDOWN | MAP_NORESERVE, -1, 0);
|
||||
|
||||
|
||||
@@ -451,7 +460,7 @@ public:
|
||||
|
||||
// Allocate with permissions the 8MB of regular stack size.
|
||||
StackPointer = reinterpret_cast<uintptr_t>(
|
||||
Handler->GuestMmap(nullptr, reinterpret_cast<void*>(reinterpret_cast<uint64_t>(StackPointerBase) + FULL_STACK_SIZE - StackSize()),
|
||||
Handler->GuestMmap(Thread, reinterpret_cast<void*>(reinterpret_cast<uint64_t>(StackPointerBase) + FULL_STACK_SIZE - StackSize()),
|
||||
StackSize(), PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK | MAP_GROWSDOWN, -1, 0));
|
||||
|
||||
if (FEX::HLE::HasSyscallError(StackPointer)) {
|
||||
@@ -498,7 +507,7 @@ public:
|
||||
|
||||
if (!MainElf.InterpreterElf.empty()) {
|
||||
uint64_t InterpLoadBase = 0;
|
||||
if (auto elf = LoadElfFile(InterpElf, nullptr, Handler)) {
|
||||
if (auto elf = LoadElfFile(InterpElf, nullptr, Handler, Thread)) {
|
||||
InterpLoadBase = *elf;
|
||||
} else {
|
||||
LogMan::Msg::EFmt("Failed to load interpreter elf file");
|
||||
@@ -560,7 +569,7 @@ public:
|
||||
|
||||
uintptr_t LoadBase = 0;
|
||||
|
||||
if (auto elf = LoadElfFile(MainElf, &BrkStart, Handler, ELFLoadHint)) {
|
||||
if (auto elf = LoadElfFile(MainElf, &BrkStart, Handler, Thread, ELFLoadHint)) {
|
||||
LoadBase = *elf;
|
||||
if (MainElf.ehdr.e_type == ET_DYN) {
|
||||
BaseOffset = LoadBase;
|
||||
@@ -573,8 +582,8 @@ public:
|
||||
if (BrkStart) {
|
||||
// BRK usually comes directly after where the ELF is loaded.
|
||||
// If a value was returned then we have mapped the entire `BRK_SIZE` and need to change protections.
|
||||
BrkStart = (uint64_t)Handler->GuestMmap(nullptr, (void*)BrkStart, BRK_SIZE, PROT_READ | PROT_WRITE,
|
||||
MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0);
|
||||
BrkStart =
|
||||
(uint64_t)Handler->GuestMmap(Thread, (void*)BrkStart, BRK_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE | MAP_FIXED, -1, 0);
|
||||
if (FEX::HLE::HasSyscallError(BrkStart)) {
|
||||
LogMan::Msg::EFmt("Failed to allocate BRK @ {:x}, {}\n", BrkStart, errno);
|
||||
return false;
|
||||
@@ -618,7 +627,7 @@ public:
|
||||
// If the VDSO thunk doesn't exist then we might not have a vsyscall entry.
|
||||
// Newer glibc requires vsyscall to exist now. So let's allocate a buffer and stick a vsyscall in to it.
|
||||
auto VSyscallPage =
|
||||
Handler->GuestMmap(nullptr, nullptr, FEXCore::Utils::FEX_PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
Handler->GuestMmap(Thread, nullptr, FEXCore::Utils::FEX_PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
||||
constexpr static uint8_t VSyscallCode[] = {
|
||||
0xcd, 0x80, // int 0x80
|
||||
0xc3, // ret
|
||||
@@ -643,10 +652,12 @@ public:
|
||||
|
||||
SetupStack();
|
||||
|
||||
// Cleanup FDs so they don't stay open
|
||||
return true;
|
||||
}
|
||||
|
||||
void CloseFDs() {
|
||||
MainElf.Closefd();
|
||||
InterpElf.Closefd();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Helper for stack setup
|
||||
|
||||
@@ -239,71 +239,72 @@ bool QueryInterpreterInstalled(bool ExecutedWithFD, const FEX::Config::PortableI
|
||||
return ExecutedWithFD || (access("/proc/sys/fs/binfmt_misc/FEX-x86", F_OK) == 0 && access("/proc/sys/fs/binfmt_misc/FEX-x86_64", F_OK) == 0);
|
||||
}
|
||||
|
||||
namespace FEX::TSO {
|
||||
void SetupTSOEmulation(FEXCore::Context::Context* CTX) {
|
||||
// Check to see if this is supported.
|
||||
auto Result = prctl(PR_GET_MEM_MODEL, 0, 0, 0, 0);
|
||||
if (Result == -1) {
|
||||
// Unsupported, early exit.
|
||||
return;
|
||||
}
|
||||
namespace FEX::Kernel {
|
||||
namespace TSO {
|
||||
void SetupTSOEmulation(FEXCore::Context::Context* CTX) {
|
||||
// Check to see if this is supported.
|
||||
auto Result = prctl(PR_GET_MEM_MODEL, 0, 0, 0, 0);
|
||||
if (Result == -1) {
|
||||
// Unsupported, early exit.
|
||||
return;
|
||||
}
|
||||
|
||||
FEX_CONFIG_OPT(TSOEnabled, TSOENABLED);
|
||||
FEX_CONFIG_OPT(TSOEnabled, TSOENABLED);
|
||||
|
||||
if (!TSOEnabled()) {
|
||||
// TSO emulation isn't even enabled, early exit.
|
||||
return;
|
||||
}
|
||||
if (!TSOEnabled()) {
|
||||
// TSO emulation isn't even enabled, early exit.
|
||||
return;
|
||||
}
|
||||
|
||||
if (Result == PR_SET_MEM_MODEL_DEFAULT) {
|
||||
// Try to set the TSO mode if we are currently default.
|
||||
Result = prctl(PR_SET_MEM_MODEL, PR_SET_MEM_MODEL_TSO, 0, 0, 0);
|
||||
if (Result == 0) {
|
||||
// TSO mode successfully enabled. Tell the context to disable TSO emulation through atomics.
|
||||
// This flag gets inherited on thread creation, so FEX only needs to set it at the start.
|
||||
CTX->SetHardwareTSOSupport(true);
|
||||
if (Result == PR_SET_MEM_MODEL_DEFAULT) {
|
||||
// Try to set the TSO mode if we are currently default.
|
||||
Result = prctl(PR_SET_MEM_MODEL, PR_SET_MEM_MODEL_TSO, 0, 0, 0);
|
||||
if (Result == 0) {
|
||||
// TSO mode successfully enabled. Tell the context to disable TSO emulation through atomics.
|
||||
// This flag gets inherited on thread creation, so FEX only needs to set it at the start.
|
||||
CTX->SetHardwareTSOSupport(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace FEX::TSO
|
||||
} // namespace TSO
|
||||
|
||||
namespace FEX::CompatInput {
|
||||
void SetupCompatInput(bool enable) {
|
||||
// Check to see if this is supported.
|
||||
auto Result = prctl(PR_GET_COMPAT_INPUT, 0, 0, 0, 0);
|
||||
if (Result == -1) {
|
||||
// Unsupported, early exit.
|
||||
return;
|
||||
namespace CompatInput {
|
||||
void SetupCompatInput(bool enable) {
|
||||
// Check to see if this is supported.
|
||||
auto Result = prctl(PR_GET_COMPAT_INPUT, 0, 0, 0, 0);
|
||||
if (Result == -1) {
|
||||
// Unsupported, early exit.
|
||||
return;
|
||||
}
|
||||
|
||||
if (enable) {
|
||||
prctl(PR_SET_COMPAT_INPUT, PR_SET_COMPAT_INPUT_ENABLE, 0, 0, 0);
|
||||
} else {
|
||||
prctl(PR_SET_COMPAT_INPUT, PR_SET_COMPAT_INPUT_DISABLE, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
} // namespace CompatInput
|
||||
|
||||
if (enable) {
|
||||
prctl(PR_SET_COMPAT_INPUT, PR_SET_COMPAT_INPUT_ENABLE, 0, 0, 0);
|
||||
} else {
|
||||
prctl(PR_SET_COMPAT_INPUT, PR_SET_COMPAT_INPUT_DISABLE, 0, 0, 0);
|
||||
namespace GCS {
|
||||
void CheckForGCS() {
|
||||
uint64_t ShadowStackWord {};
|
||||
if (prctl(PR_GET_SHADOW_STACK_STATUS, &ShadowStackWord, 0, 0, 0) == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Kernel supports shadow stack.
|
||||
if (ShadowStackWord & PR_SHADOW_STACK_ENABLE) {
|
||||
// Welp.
|
||||
ERROR_AND_DIE_FMT("Shadow stack is enabled which FEX is incompatible with!");
|
||||
}
|
||||
|
||||
// Disable if we've gotten this far, to ensure guest can't try.
|
||||
prctl(PR_LOCK_SHADOW_STACK_STATUS, ~0ULL, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
} // namespace FEX::CompatInput
|
||||
} // namespace GCS
|
||||
|
||||
namespace FEX::GCS {
|
||||
void CheckForGCS() {
|
||||
uint64_t ShadowStackWord {};
|
||||
if (prctl(PR_GET_SHADOW_STACK_STATUS, &ShadowStackWord, 0, 0, 0) == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Kernel supports shadow stack.
|
||||
if (ShadowStackWord & PR_SHADOW_STACK_ENABLE) {
|
||||
// Welp.
|
||||
ERROR_AND_DIE_FMT("Shadow stack is enabled which FEX is incompatible with!");
|
||||
}
|
||||
|
||||
// Disable if we've gotten this far, to ensure guest can't try.
|
||||
prctl(PR_LOCK_SHADOW_STACK_STATUS, ~0ULL, 0, 0, 0);
|
||||
}
|
||||
} // namespace FEX::GCS
|
||||
|
||||
namespace FEX::UnalignedAtomic {
|
||||
void SetupKernelUnalignedAtomics() {
|
||||
namespace UnalignedAtomic {
|
||||
void SetupKernelUnalignedAtomics() {
|
||||
#ifndef PR_ARM64_SET_UNALIGN_ATOMIC
|
||||
#define PR_ARM64_SET_UNALIGN_ATOMIC 0x46455849
|
||||
#define PR_ARM64_UNALIGN_ATOMIC_EMULATE (1UL << 0)
|
||||
@@ -311,16 +312,34 @@ void SetupKernelUnalignedAtomics() {
|
||||
#define PR_ARM64_UNALIGN_ATOMIC_STRICT_SPLIT_LOCKS (1UL << 2)
|
||||
#endif
|
||||
|
||||
// Interfaces with downstream FEX kernel patches to control unaligned atomic handling
|
||||
FEX_CONFIG_OPT(StrictInProcessSplitLocks, STRICTINPROCESSSPLITLOCKS);
|
||||
FEX_CONFIG_OPT(KernelUnalignedAtomicBackpatching, KERNELUNALIGNEDATOMICBACKPATCHING);
|
||||
// Interfaces with downstream FEX kernel patches to control unaligned atomic handling
|
||||
FEX_CONFIG_OPT(StrictInProcessSplitLocks, STRICTINPROCESSSPLITLOCKS);
|
||||
FEX_CONFIG_OPT(KernelUnalignedAtomicBackpatching, KERNELUNALIGNEDATOMICBACKPATCHING);
|
||||
|
||||
uint64_t Flags = (StrictInProcessSplitLocks() ? PR_ARM64_UNALIGN_ATOMIC_STRICT_SPLIT_LOCKS : 0) |
|
||||
(KernelUnalignedAtomicBackpatching() ? PR_ARM64_UNALIGN_ATOMIC_BACKPATCH : 0) | PR_ARM64_UNALIGN_ATOMIC_EMULATE;
|
||||
uint64_t Flags = (StrictInProcessSplitLocks() ? PR_ARM64_UNALIGN_ATOMIC_STRICT_SPLIT_LOCKS : 0) |
|
||||
(KernelUnalignedAtomicBackpatching() ? PR_ARM64_UNALIGN_ATOMIC_BACKPATCH : 0) | PR_ARM64_UNALIGN_ATOMIC_EMULATE;
|
||||
|
||||
prctl(PR_ARM64_SET_UNALIGN_ATOMIC, Flags, 0, 0, 0);
|
||||
prctl(PR_ARM64_SET_UNALIGN_ATOMIC, Flags, 0, 0, 0);
|
||||
}
|
||||
} // namespace UnalignedAtomic
|
||||
|
||||
void Init(bool Is64Bit, FEXCore::Context::Context* CTX) {
|
||||
// Setup TSO hardware emulation immediately after initializing the context.
|
||||
TSO::SetupTSOEmulation(CTX);
|
||||
UnalignedAtomic::SetupKernelUnalignedAtomics();
|
||||
|
||||
if (!Is64Bit) {
|
||||
// Tell the kernel we want to use the compat input syscalls even though we're
|
||||
// a 64 bit process.
|
||||
CompatInput::SetupCompatInput(true);
|
||||
} else {
|
||||
// Our parent could be an instance running a 32 bit application, so we need
|
||||
// to disable compat input if we're running a 64 bit one ourselves.
|
||||
CompatInput::SetupCompatInput(false);
|
||||
}
|
||||
}
|
||||
} // namespace FEX::UnalignedAtomic
|
||||
|
||||
} // namespace FEX::Kernel
|
||||
|
||||
/**
|
||||
* @brief Get an FD from an environment variable and then unset the environment variable.
|
||||
@@ -364,7 +383,7 @@ int main(int argc, char** argv, char** const envp) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
FEX::GCS::CheckForGCS();
|
||||
FEX::Kernel::GCS::CheckForGCS();
|
||||
|
||||
FEX::Config::LoadConfig(Program.ProgramName, envp, PortableInfo);
|
||||
|
||||
@@ -442,6 +461,7 @@ int main(int argc, char** argv, char** const envp) {
|
||||
}
|
||||
|
||||
ELFCodeLoader Loader {Program.ProgramPath, FEXFD, LDPath(), Args, ParsedArgs, envp, &Environment};
|
||||
FEXCore::Config::Set(FEXCore::Config::CONFIG_IS64BIT_MODE, Loader.Is64BitMode() ? "1" : "0");
|
||||
|
||||
if (!Loader.ELFWasLoaded()) {
|
||||
// Loader couldn't load this program for some reason
|
||||
@@ -483,8 +503,6 @@ int main(int argc, char** argv, char** const envp) {
|
||||
// Setup Thread handlers, so FEXCore can create threads.
|
||||
auto StackTracker = FEX::LinuxEmulation::Threads::SetupThreadHandlers();
|
||||
|
||||
FEXCore::Config::Set(FEXCore::Config::CONFIG_IS64BIT_MODE, Loader.Is64BitMode() ? "1" : "0");
|
||||
|
||||
FEX::Allocator::Init(Loader.Is64BitMode());
|
||||
|
||||
FEXCore::Profiler::Init(Program.ProgramName, Program.ProgramPath);
|
||||
@@ -497,19 +515,7 @@ int main(int argc, char** argv, char** const envp) {
|
||||
SupportsAVX = HostFeatures.SupportsAVX;
|
||||
}
|
||||
|
||||
// Setup TSO hardware emulation immediately after initializing the context.
|
||||
FEX::TSO::SetupTSOEmulation(CTX.get());
|
||||
FEX::UnalignedAtomic::SetupKernelUnalignedAtomics();
|
||||
|
||||
if (!Loader.Is64BitMode()) {
|
||||
// Tell the kernel we want to use the compat input syscalls even though we're
|
||||
// a 64 bit process.
|
||||
FEX::CompatInput::SetupCompatInput(true);
|
||||
} else {
|
||||
// Our parent could be an instance running a 32 bit application, so we need
|
||||
// to disable compat input if we're running a 64 bit one ourselves.
|
||||
FEX::CompatInput::SetupCompatInput(false);
|
||||
}
|
||||
FEX::Kernel::Init(Loader.Is64BitMode(), CTX.get());
|
||||
|
||||
auto SignalDelegation = FEX::HLE::CreateSignalDelegator(CTX.get(), Program.ProgramName, SupportsAVX);
|
||||
auto ThunkHandler = FEX::HLE::CreateThunkHandler();
|
||||
@@ -517,16 +523,20 @@ int main(int argc, char** argv, char** const envp) {
|
||||
auto SyscallHandler = Loader.Is64BitMode() ? FEX::HLE::x64::CreateHandler(CTX.get(), SignalDelegation.get(), ThunkHandler.get()) :
|
||||
FEX::HLE::x32::CreateHandler(CTX.get(), SignalDelegation.get(), ThunkHandler.get(),
|
||||
std::move(FEX::Allocator::Allocator));
|
||||
SyscallHandler->SetCodeLoader(&Loader);
|
||||
CTX->SetSignalDelegator(SignalDelegation.get());
|
||||
CTX->SetSyscallHandler(SyscallHandler.get());
|
||||
CTX->SetThunkHandler(ThunkHandler.get());
|
||||
|
||||
if (FEXCore::Config::Get_ENABLECODECACHINGWIP()) {
|
||||
CTX->SetCodeMapWriter(fextl::make_unique<FEXCore::CodeMapWriter>(*SyscallHandler));
|
||||
}
|
||||
|
||||
// Load VDSO in to memory prior to mapping our ELFs.
|
||||
auto VDSOMapping = FEX::VDSO::LoadVDSOThunks(Loader.Is64BitMode(), SyscallHandler.get());
|
||||
|
||||
// Pass in our VDSO thunks
|
||||
ThunkHandler->AppendThunkDefinitions(FEX::VDSO::GetVDSOThunkDefinitions(Loader.Is64BitMode()));
|
||||
SignalDelegation->SetVDSOSymbols();
|
||||
FEX_CONFIG_OPT(GdbServer, GDBSERVER);
|
||||
fextl::unique_ptr<FEX::GdbServer> DebugServer;
|
||||
if (GdbServer) {
|
||||
DebugServer = fextl::make_unique<FEX::GdbServer>(CTX.get(), SignalDelegation.get(), SyscallHandler.get());
|
||||
}
|
||||
|
||||
// Now that we have the syscall handler. Track some FDs that are FEX owned.
|
||||
if (FEX::Logging::OutputFD > 2) {
|
||||
@@ -537,53 +547,51 @@ int main(int argc, char** argv, char** const envp) {
|
||||
SyscallHandler->FM.TrackFEXFD(FEX::Logging::FEXServer::FEXServerFD);
|
||||
}
|
||||
|
||||
// query ProgramFD now since MapMemory will close it
|
||||
const int ProgramFD = dup(Loader.GetMainElfFD());
|
||||
|
||||
{
|
||||
Loader.SetVDSOBase(VDSOMapping.VDSOBase);
|
||||
Loader.CalculateHWCaps(CTX.get());
|
||||
|
||||
if (!Loader.MapMemory(SyscallHandler.get())) {
|
||||
// failed to map
|
||||
LogMan::Msg::EFmt("Failed to map {}-bit elf file.", Loader.Is64BitMode() ? 64 : 32);
|
||||
return -ENOEXEC;
|
||||
}
|
||||
}
|
||||
|
||||
SyscallHandler->SetCodeLoader(&Loader);
|
||||
|
||||
auto BRKInfo = Loader.GetBRKInfo();
|
||||
|
||||
SyscallHandler->DefaultProgramBreak(BRKInfo.Base, BRKInfo.Size);
|
||||
|
||||
CTX->SetSignalDelegator(SignalDelegation.get());
|
||||
CTX->SetSyscallHandler(SyscallHandler.get());
|
||||
CTX->SetThunkHandler(ThunkHandler.get());
|
||||
|
||||
FEX_CONFIG_OPT(GdbServer, GDBSERVER);
|
||||
fextl::unique_ptr<FEX::GdbServer> DebugServer;
|
||||
if (GdbServer) {
|
||||
DebugServer = fextl::make_unique<FEX::GdbServer>(CTX.get(), SignalDelegation.get(), SyscallHandler.get());
|
||||
}
|
||||
|
||||
if (!CTX->InitCore()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto ParentThread = SyscallHandler->TM.CreateThread(Loader.DefaultRIP(), Loader.GetStackPointer());
|
||||
// Create a thread without a RIP or stack pointer setup initially.
|
||||
auto ParentThread = SyscallHandler->TM.CreateThread(0, 0);
|
||||
SyscallHandler->TM.TrackThread(ParentThread);
|
||||
SignalDelegation->RegisterTLSState(ParentThread);
|
||||
ThunkHandler->RegisterTLSState(ParentThread);
|
||||
|
||||
SyscallHandler->DeserializeSeccompFD(ParentThread, FEXSeccompFD);
|
||||
|
||||
// Request code cache generation and load caches for all binaries loaded previously
|
||||
if (FEXCore::Config::Get_ENABLECODECACHINGWIP()) {
|
||||
FEXServerClient::PopulateCodeCache(FEXServerClient::GetServerFD(), ProgramFD, FEXCore::Config::Get_MULTIBLOCK());
|
||||
SyscallHandler->TriggerPostStartupCodeCacheLoad(*ParentThread->Thread);
|
||||
// Load VDSO in to memory prior to mapping our ELFs.
|
||||
auto VDSOMapping = FEX::VDSO::LoadVDSOThunks(ParentThread->Thread, Loader.Is64BitMode(), SyscallHandler.get());
|
||||
|
||||
// Pass in our VDSO thunks
|
||||
ThunkHandler->AppendThunkDefinitions(FEX::VDSO::GetVDSOThunkDefinitions(Loader.Is64BitMode()));
|
||||
SignalDelegation->SetVDSOSymbols();
|
||||
|
||||
{
|
||||
Loader.SetVDSOBase(VDSOMapping.VDSOBase);
|
||||
Loader.CalculateHWCaps(CTX.get());
|
||||
|
||||
if (!Loader.MapMemory(SyscallHandler.get(), ParentThread->Thread)) {
|
||||
// failed to map
|
||||
LogMan::Msg::EFmt("Failed to map {}-bit elf file.", Loader.Is64BitMode() ? 64 : 32);
|
||||
return -ENOEXEC;
|
||||
}
|
||||
}
|
||||
close(ProgramFD);
|
||||
|
||||
auto BRKInfo = Loader.GetBRKInfo();
|
||||
|
||||
SyscallHandler->DefaultProgramBreak(BRKInfo.Base, BRKInfo.Size);
|
||||
|
||||
// Request code cache generation
|
||||
if (FEXCore::Config::Get_ENABLECODECACHINGWIP()) {
|
||||
FEXServerClient::PopulateCodeCache(FEXServerClient::GetServerFD(), Loader.GetMainElfFD(), FEXCore::Config::Get_MULTIBLOCK());
|
||||
}
|
||||
|
||||
// Pull RIP and stack pointer from loader and set the thread data to it.
|
||||
ParentThread->Thread->CurrentFrame->State.rip = Loader.DefaultRIP();
|
||||
ParentThread->Thread->CurrentFrame->State.gregs[FEXCore::X86State::REG_RSP] = Loader.GetStackPointer();
|
||||
|
||||
// Close the loader FDs after everything has been parsed and mapped.
|
||||
Loader.CloseFDs();
|
||||
|
||||
CTX->ExecuteThread(ParentThread->Thread);
|
||||
|
||||
@@ -592,11 +600,11 @@ int main(int argc, char** argv, char** const envp) {
|
||||
|
||||
auto ProgramStatus = ParentThread->StatusCode;
|
||||
|
||||
FEX::VDSO::UnloadVDSOMapping(ParentThread->Thread, SyscallHandler.get(), VDSOMapping);
|
||||
|
||||
SignalDelegation->UninstallTLSState(ParentThread);
|
||||
SyscallHandler->TM.DestroyThread(ParentThread);
|
||||
|
||||
FEX::VDSO::UnloadVDSOMapping(VDSOMapping);
|
||||
|
||||
DebugServer.reset();
|
||||
SyscallHandler.reset();
|
||||
SignalDelegation.reset();
|
||||
|
||||
@@ -153,19 +153,20 @@ GenerateSingleCache(const FEXCore::ExecutableFileInfo& Binary, fextl::set<uintpt
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (!Is64Bit) {
|
||||
// Block upper address space
|
||||
FEXCore::Allocator::SetupHooks();
|
||||
}
|
||||
|
||||
auto Thread = SetupCompileThread(*CTX, Is64Bit);
|
||||
|
||||
{
|
||||
if (!Is64Bit) {
|
||||
// Block upper address space
|
||||
FEXCore::Allocator::SetupHooks();
|
||||
}
|
||||
auto ElfBase = Loader.LoadMainElfFile(nullptr, SyscallHandler.get());
|
||||
auto ElfBase = Loader.LoadMainElfFile(nullptr, SyscallHandler.get(), Thread);
|
||||
if (!ElfBase.has_value()) {
|
||||
ERROR_AND_DIE_FMT("Failed to load ELF file {} ({})", Binary.Filename, Binary.FileId);
|
||||
}
|
||||
}
|
||||
|
||||
auto Thread = SetupCompileThread(*CTX, Is64Bit);
|
||||
|
||||
CTX->GetCodeCache().InitiateCacheGeneration();
|
||||
|
||||
{
|
||||
|
||||
@@ -292,7 +292,6 @@ public:
|
||||
std::optional<FEXCore::ExecutableFileSectionInfo>
|
||||
LookupExecutableFileSection(FEXCore::Core::InternalThreadState* Thread, uint64_t GuestAddr) final override;
|
||||
|
||||
void TriggerPostStartupCodeCacheLoad(FEXCore::Core::InternalThreadState&);
|
||||
int OpenCodeMapFile() override;
|
||||
|
||||
FEXCore::HLE::ExecutableRangeInfo QueryGuestExecutableRange(FEXCore::Core::InternalThreadState* Thread, uint64_t Address) override;
|
||||
@@ -324,8 +323,6 @@ public:
|
||||
constexpr static size_t LDT_ENTRY_SIZE = sizeof(FEXCore::Core::CPUState::gdt_segment);
|
||||
|
||||
VMATracking::VMATracking VMATracking;
|
||||
// Collects file mappings added during FEX startup.
|
||||
fextl::vector<FEXCore::ExecutableFileSectionInfo> StartupBinaryLoads;
|
||||
|
||||
const uint64_t CodeCacheConfigId = 0; // TODO: Make unique to active configuration
|
||||
|
||||
|
||||
@@ -351,18 +351,6 @@ uint64_t SyscallHandler::GuestMremap(bool Is64Bit, FEXCore::Core::InternalThread
|
||||
return Result;
|
||||
}
|
||||
|
||||
void SyscallHandler::TriggerPostStartupCodeCacheLoad(FEXCore::Core::InternalThreadState& Thread) {
|
||||
if (!EnableCodeCaching) {
|
||||
return;
|
||||
}
|
||||
|
||||
FEX_CONFIG_OPT(Multiblock, MULTIBLOCK);
|
||||
for (auto& FileSection : StartupBinaryLoads) {
|
||||
LoadCodeCache(Thread, FileSection, CodeCacheConfigId);
|
||||
}
|
||||
StartupBinaryLoads.clear();
|
||||
}
|
||||
|
||||
int SyscallHandler::OpenCodeMapFile() {
|
||||
// Query from FEXServer whether this is the first instance of this executable; if it is, also enable code dumping!
|
||||
FEX_CONFIG_OPT(RootFSPath, ROOTFS);
|
||||
@@ -580,8 +568,8 @@ SyscallHandler::TrackMmap(FEXCore::Core::InternalThreadState* Thread, uint64_t a
|
||||
if (Thread) {
|
||||
CachedSection.emplace(BuildSectionInfo(*Resource, addr, Size));
|
||||
} else {
|
||||
// Delay loading this entry until FEX is fully initialized
|
||||
StartupBinaryLoads.push_back(BuildSectionInfo(*Resource, addr, Size));
|
||||
// Cache can't be loaded with a thread; skip this for now
|
||||
LogMan::Msg::DFmt("Oops, tried caching without a thread: {}", Resource->MappedFile->Filename);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -669,7 +669,7 @@ void LoadGuestVDSOSymbols(char* VDSOBase) {
|
||||
}
|
||||
}
|
||||
|
||||
void LoadFEXGeneratedCode(bool Is64Bit, VDSOMapping* Mapping, FEX::HLE::SyscallHandler* const Handler) {
|
||||
void LoadFEXGeneratedCode(FEXCore::Core::InternalThreadState* Thread, bool Is64Bit, VDSOMapping* Mapping, FEX::HLE::SyscallHandler* const Handler) {
|
||||
if (VDSOPointers.VDSO_FEX_CallbackRET && (!Is64Bit || (VDSOPointers.VDSO_kernel_sigreturn && VDSOPointers.VDSO_kernel_rt_sigreturn))) {
|
||||
// Unnecessary if all VDSO paths have already been loaded.
|
||||
return;
|
||||
@@ -682,8 +682,10 @@ void LoadFEXGeneratedCode(bool Is64Bit, VDSOMapping* Mapping, FEX::HLE::SyscallH
|
||||
|
||||
if (Is64Bit) {
|
||||
// 64bit mode can have its code anywhere
|
||||
auto Result = FEXCore::Allocator::VirtualAlloc(Mapping->X86GeneratedCodeSize);
|
||||
if (Result != MAP_FAILED) {
|
||||
auto Result =
|
||||
Handler->GuestMmap(Is64Bit, Thread, nullptr, Mapping->X86GeneratedCodeSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
|
||||
if (!FEX::HLE::HasSyscallError(Result)) {
|
||||
Mapping->X86GeneratedCodePtr = Result;
|
||||
}
|
||||
} else {
|
||||
@@ -693,18 +695,18 @@ void LoadFEXGeneratedCode(bool Is64Bit, VDSOMapping* Mapping, FEX::HLE::SyscallH
|
||||
// We need to have the sigret handler in the lower 32bits of memory space
|
||||
// Scan top down and try to allocate a location
|
||||
for (size_t Location = 0xFFFF'E000; Location != 0x0; Location -= PageSize) {
|
||||
void* Ptr =
|
||||
::mmap(reinterpret_cast<void*>(Location), PageSize, PROT_READ | PROT_WRITE, MAP_FIXED_NOREPLACE | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
auto Ptr = Handler->GuestMmap(Is64Bit, Thread, reinterpret_cast<void*>(Location), PageSize, PROT_READ | PROT_WRITE,
|
||||
MAP_FIXED_NOREPLACE | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
|
||||
if (Ptr != MAP_FAILED && reinterpret_cast<uintptr_t>(Ptr) >= LOCATION_MAX) {
|
||||
if (!FEX::HLE::HasSyscallError(Ptr) && reinterpret_cast<uintptr_t>(Ptr) >= LOCATION_MAX) {
|
||||
// Failed to map in the lower 32bits
|
||||
// Try again
|
||||
// Can happen in the case that host kernel ignores MAP_FIXED_NOREPLACE
|
||||
::munmap(Ptr, PageSize);
|
||||
Handler->GuestMunmap(Thread, Ptr, PageSize);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Ptr != MAP_FAILED) {
|
||||
if (!FEX::HLE::HasSyscallError(Ptr)) {
|
||||
Mapping->X86GeneratedCodePtr = Ptr;
|
||||
break;
|
||||
}
|
||||
@@ -765,29 +767,20 @@ void LoadFEXGeneratedCode(bool Is64Bit, VDSOMapping* Mapping, FEX::HLE::SyscallH
|
||||
CurrentCodeOffset += CallbackRetCode.size();
|
||||
}
|
||||
|
||||
mprotect(Mapping->X86GeneratedCodePtr, Mapping->X86GeneratedCodeSize, PROT_READ | PROT_EXEC);
|
||||
{
|
||||
auto lk = FEXCore::GuardSignalDeferringSectionWithFallback(Handler->VMATracking.Mutex, nullptr);
|
||||
std::optional<FEXCore::ExecutableFileSectionInfo> IgnoredSection;
|
||||
FEX::HLE::_SyscallHandler->TrackMmap(nullptr, reinterpret_cast<uint64_t>(Mapping->X86GeneratedCodePtr), Mapping->X86GeneratedCodeSize,
|
||||
PROT_READ | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0, IgnoredSection);
|
||||
}
|
||||
|
||||
FEX::HLE::_SyscallHandler->InvalidateCodeRangeIfNecessary(nullptr, reinterpret_cast<uint64_t>(Mapping->X86GeneratedCodePtr),
|
||||
Mapping->X86GeneratedCodeSize);
|
||||
Handler->GuestMprotect(Thread, Mapping->X86GeneratedCodePtr, Mapping->X86GeneratedCodeSize, PROT_READ | PROT_EXEC);
|
||||
}
|
||||
|
||||
void UnloadVDSOMapping(const VDSOMapping& Mapping) {
|
||||
void UnloadVDSOMapping(FEXCore::Core::InternalThreadState* Thread, FEX::HLE::SyscallHandler* const Handler, const VDSOMapping& Mapping) {
|
||||
if (Mapping.VDSOBase) {
|
||||
munmap(Mapping.VDSOBase, Mapping.VDSOSize);
|
||||
Handler->GuestMunmap(Thread, Mapping.VDSOBase, Mapping.VDSOSize);
|
||||
}
|
||||
|
||||
if (Mapping.X86GeneratedCodePtr) {
|
||||
munmap(Mapping.X86GeneratedCodePtr, Mapping.X86GeneratedCodeSize);
|
||||
Handler->GuestMunmap(Thread, Mapping.X86GeneratedCodePtr, Mapping.X86GeneratedCodeSize);
|
||||
}
|
||||
}
|
||||
|
||||
VDSOMapping LoadVDSOThunks(bool Is64Bit, FEX::HLE::SyscallHandler* const Handler) {
|
||||
VDSOMapping LoadVDSOThunks(FEXCore::Core::InternalThreadState* Thread, bool Is64Bit, FEX::HLE::SyscallHandler* const Handler) {
|
||||
VDSOMapping Mapping {};
|
||||
FEX_CONFIG_OPT(ThunkGuestLibs, THUNKGUESTLIBS);
|
||||
fextl::string ThunkGuestPath = ThunkGuestLibs();
|
||||
@@ -808,7 +801,7 @@ VDSOMapping LoadVDSOThunks(bool Is64Bit, FEX::HLE::SyscallHandler* const Handler
|
||||
Mapping.VDSOSize = FEXCore::AlignUp(Mapping.VDSOSize, FEXCore::Utils::FEX_PAGE_SIZE);
|
||||
|
||||
// Map the VDSO file to memory
|
||||
Mapping.VDSOBase = Handler->GuestMmap(nullptr, nullptr, Mapping.VDSOSize, PROT_READ | PROT_EXEC, MAP_SHARED, VDSOFD, 0);
|
||||
Mapping.VDSOBase = Handler->GuestMmap(Thread, nullptr, Mapping.VDSOSize, PROT_READ | PROT_EXEC, MAP_SHARED, VDSOFD, 0);
|
||||
|
||||
// Since we found our VDSO thunk library, find our host VDSO function implementations.
|
||||
LoadHostVDSO();
|
||||
@@ -822,7 +815,7 @@ VDSOMapping LoadVDSOThunks(bool Is64Bit, FEX::HLE::SyscallHandler* const Handler
|
||||
}
|
||||
|
||||
// If VDSO couldn't find sigreturn then FEX needs to provide unique implementations.
|
||||
LoadFEXGeneratedCode(Is64Bit, &Mapping, Handler);
|
||||
LoadFEXGeneratedCode(Thread, Is64Bit, &Mapping, Handler);
|
||||
|
||||
if (Is64Bit) {
|
||||
// Set the Thunk definition pointers for x86-64
|
||||
|
||||
@@ -7,6 +7,10 @@
|
||||
#include <cstdint>
|
||||
#include <span>
|
||||
|
||||
namespace FEXCore::Core {
|
||||
struct InternalThreadState;
|
||||
}
|
||||
|
||||
namespace FEX::HLE {
|
||||
class SyscallHandler;
|
||||
}
|
||||
@@ -24,8 +28,8 @@ struct VDSOEntrypoints {
|
||||
void* VDSO_kernel_rt_sigreturn;
|
||||
void* VDSO_FEX_CallbackRET;
|
||||
};
|
||||
VDSOMapping LoadVDSOThunks(bool Is64Bit, FEX::HLE::SyscallHandler* const Handler);
|
||||
void UnloadVDSOMapping(const VDSOMapping& Mapping);
|
||||
VDSOMapping LoadVDSOThunks(FEXCore::Core::InternalThreadState* Thread, bool Is64Bit, FEX::HLE::SyscallHandler* const Handler);
|
||||
void UnloadVDSOMapping(FEXCore::Core::InternalThreadState* Thread, FEX::HLE::SyscallHandler* const Handler, const VDSOMapping& Mapping);
|
||||
|
||||
uint64_t GetVSyscallEntry(const void* VDSOBase);
|
||||
|
||||
|
||||
Reference in new issue
Block a user