Merge pull request #5148 from Sonicadvance1/29

Interpreter: Moves around the thread and ELF initialization code
This commit is contained in:
Ryan Houdek authored and GitHub committed 2026-01-07 08:12:09 -08:00
commit ed1d49520f
7 files changed
+193 -191

No files matched your search

+30 -19
View File
@@ -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
+130 -122
View File
@@ -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();
+8 -7
View File
@@ -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);
}
}
+17 -24
View File
@@ -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
+6 -2
View File
@@ -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);