diff --git a/Source/Tools/FEXInterpreter/ELFCodeLoader.h b/Source/Tools/FEXInterpreter/ELFCodeLoader.h index d282ba1a72..6afefaeff2 100644 --- a/Source/Tools/FEXInterpreter/ELFCodeLoader.h +++ b/Source/Tools/FEXInterpreter/ELFCodeLoader.h @@ -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 LoadElfFile(ELFParser& Elf, uintptr_t* BrkBase, FEX::HLE::SyscallMmapInterface* const Handler, uint64_t LoadHint = 0) { + std::optional 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(LoadHint), TotalSize, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + (uintptr_t)Handler->GuestMmap(Thread, reinterpret_cast(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(BrkLoadBase), BRK_SIZE, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + (uintptr_t)Handler->GuestMmap(Thread, reinterpret_cast(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 @@ class ELFCodeLoader final : public FEX::CodeLoader { return MainElf.fd; } - std::optional LoadMainElfFile(uintptr_t* BrkBase, FEX::HLE::SyscallMmapInterface* const Handler, uint64_t LoadHint = 0) { - return LoadElfFile(MainElf, BrkBase, Handler, LoadHint); + std::optional 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 @@ class ELFCodeLoader final : public FEX::CodeLoader { return false; } + if (Thread) { + // Update the thread persona. + auto ThreadObject = static_cast(Thread->FrontendPtr); + ThreadObject->persona = ::personality(0xffffffff); + } + // What about ASLR and such ? // ADDR_LIMIT_3GB STACK -> 0xc0000000 else -> 0xFFFFe000 @@ -440,7 +449,7 @@ class ELFCodeLoader final : public FEX::CodeLoader { 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(StackHint), FULL_STACK_SIZE, PROT_NONE, + StackPointerBase = Handler->GuestMmap(Thread, reinterpret_cast(StackHint), FULL_STACK_SIZE, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK | MAP_GROWSDOWN | MAP_NORESERVE, -1, 0); @@ -451,7 +460,7 @@ class ELFCodeLoader final : public FEX::CodeLoader { // Allocate with permissions the 8MB of regular stack size. StackPointer = reinterpret_cast( - Handler->GuestMmap(nullptr, reinterpret_cast(reinterpret_cast(StackPointerBase) + FULL_STACK_SIZE - StackSize()), + Handler->GuestMmap(Thread, reinterpret_cast(reinterpret_cast(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 @@ class ELFCodeLoader final : public FEX::CodeLoader { 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 @@ class ELFCodeLoader final : public FEX::CodeLoader { 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 @@ class ELFCodeLoader final : public FEX::CodeLoader { 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 @@ class ELFCodeLoader final : public FEX::CodeLoader { // 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 @@ class ELFCodeLoader final : public FEX::CodeLoader { SetupStack(); - // Cleanup FDs so they don't stay open + return true; + } + + void CloseFDs() { MainElf.Closefd(); InterpElf.Closefd(); - return true; } // Helper for stack setup diff --git a/Source/Tools/FEXInterpreter/FEXInterpreter.cpp b/Source/Tools/FEXInterpreter/FEXInterpreter.cpp index 1c6f138f40..2243105c04 100644 --- a/Source/Tools/FEXInterpreter/FEXInterpreter.cpp +++ b/Source/Tools/FEXInterpreter/FEXInterpreter.cpp @@ -241,71 +241,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 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 TSO - 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 FEX::CompatInput +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; + } -namespace FEX::GCS { -void CheckForGCS() { - uint64_t ShadowStackWord {}; - if (prctl(PR_GET_SHADOW_STACK_STATUS, &ShadowStackWord, 0, 0, 0) == -1) { - 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 - // Kernel supports shadow stack. - if (ShadowStackWord & PR_SHADOW_STACK_ENABLE) { - // Welp. - ERROR_AND_DIE_FMT("Shadow stack is enabled which FEX is incompatible with!"); - } +namespace GCS { + void CheckForGCS() { + uint64_t ShadowStackWord {}; + if (prctl(PR_GET_SHADOW_STACK_STATUS, &ShadowStackWord, 0, 0, 0) == -1) { + return; + } - // 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 + // 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 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) @@ -313,16 +314,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; + + prctl(PR_ARM64_SET_UNALIGN_ATOMIC, Flags, 0, 0, 0); + } +} // namespace UnalignedAtomic - uint64_t Flags = (StrictInProcessSplitLocks() ? PR_ARM64_UNALIGN_ATOMIC_STRICT_SPLIT_LOCKS : 0) | - (KernelUnalignedAtomicBackpatching() ? PR_ARM64_UNALIGN_ATOMIC_BACKPATCH : 0) | PR_ARM64_UNALIGN_ATOMIC_EMULATE; +void Init(bool Is64Bit, FEXCore::Context::Context* CTX) { + // Setup TSO hardware emulation immediately after initializing the context. + TSO::SetupTSOEmulation(CTX); + UnalignedAtomic::SetupKernelUnalignedAtomics(); - prctl(PR_ARM64_SET_UNALIGN_ATOMIC, Flags, 0, 0, 0); + 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. @@ -366,7 +385,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); @@ -444,6 +463,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 @@ -485,8 +505,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); @@ -499,19 +517,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(); @@ -519,16 +525,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(*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 DebugServer; + if (GdbServer) { + DebugServer = fextl::make_unique(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) { @@ -539,53 +549,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()); + if (!CTX->InitCore()) { + return 1; + } + + // 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); + + // 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())) { + 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; } } - 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 DebugServer; - if (GdbServer) { - DebugServer = fextl::make_unique(CTX.get(), SignalDelegation.get(), SyscallHandler.get()); - } - - if (!CTX->InitCore()) { - return 1; + // Request code cache generation + if (FEXCore::Config::Get_ENABLECODECACHINGWIP()) { + FEXServerClient::PopulateCodeCache(FEXServerClient::GetServerFD(), Loader.GetMainElfFD(), FEXCore::Config::Get_MULTIBLOCK()); } - auto ParentThread = SyscallHandler->TM.CreateThread(Loader.DefaultRIP(), Loader.GetStackPointer()); - SyscallHandler->TM.TrackThread(ParentThread); - SignalDelegation->RegisterTLSState(ParentThread); - ThunkHandler->RegisterTLSState(ParentThread); - - SyscallHandler->DeserializeSeccompFD(ParentThread, FEXSeccompFD); + // 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(); - // 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); - } - close(ProgramFD); + // Close the loader FDs after everything has been parsed and mapped. + Loader.CloseFDs(); CTX->ExecuteThread(ParentThread->Thread); @@ -594,11 +602,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(); diff --git a/Source/Tools/FEXOfflineCompiler/Main.cpp b/Source/Tools/FEXOfflineCompiler/Main.cpp index e6d43b731c..1f1a66aa17 100644 --- a/Source/Tools/FEXOfflineCompiler/Main.cpp +++ b/Source/Tools/FEXOfflineCompiler/Main.cpp @@ -153,19 +153,20 @@ GenerateSingleCache(const FEXCore::ExecutableFileInfo& Binary, fextl::setGetCodeCache().InitiateCacheGeneration(); { diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h b/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h index bc92a67b9a..0e3f3aeb43 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/Syscalls.h @@ -292,7 +292,6 @@ class SyscallHandler : public FEXCore::HLE::SyscallHandler, std::optional 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 @@ class SyscallHandler : public FEXCore::HLE::SyscallHandler, 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 StartupBinaryLoads; const uint64_t CodeCacheConfigId = 0; // TODO: Make unique to active configuration diff --git a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp index b13fc1ae20..65944a22f5 100644 --- a/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp +++ b/Source/Tools/LinuxEmulation/LinuxSyscalls/SyscallsSMCTracking.cpp @@ -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); } } diff --git a/Source/Tools/LinuxEmulation/VDSO_Emulation.cpp b/Source/Tools/LinuxEmulation/VDSO_Emulation.cpp index 3823d57a65..0e67c66be4 100644 --- a/Source/Tools/LinuxEmulation/VDSO_Emulation.cpp +++ b/Source/Tools/LinuxEmulation/VDSO_Emulation.cpp @@ -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(Location), PageSize, PROT_READ | PROT_WRITE, MAP_FIXED_NOREPLACE | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + auto Ptr = Handler->GuestMmap(Is64Bit, Thread, reinterpret_cast(Location), PageSize, PROT_READ | PROT_WRITE, + MAP_FIXED_NOREPLACE | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - if (Ptr != MAP_FAILED && reinterpret_cast(Ptr) >= LOCATION_MAX) { + if (!FEX::HLE::HasSyscallError(Ptr) && reinterpret_cast(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 IgnoredSection; - FEX::HLE::_SyscallHandler->TrackMmap(nullptr, reinterpret_cast(Mapping->X86GeneratedCodePtr), Mapping->X86GeneratedCodeSize, - PROT_READ | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0, IgnoredSection); - } - - FEX::HLE::_SyscallHandler->InvalidateCodeRangeIfNecessary(nullptr, reinterpret_cast(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 diff --git a/Source/Tools/LinuxEmulation/VDSO_Emulation.h b/Source/Tools/LinuxEmulation/VDSO_Emulation.h index 771e299088..f05ba69bc1 100644 --- a/Source/Tools/LinuxEmulation/VDSO_Emulation.h +++ b/Source/Tools/LinuxEmulation/VDSO_Emulation.h @@ -7,6 +7,10 @@ #include #include +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);