mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-09 11:00:21 +02:00
ERROR_AND_DIE was using __builtin_trap which would send our application either a SIGILL or SIGTRAP depending on architecture. This would then be captured by our faulting system and passed over to the guest application. If the guest application happened to have a signal handler installed for these then it would pick up this fault and potentially continue unsafely. Now we can remove this usage of __builtin_trap and switch over to our own handler. Our frontend will check to see if the fault came from our handler and uninstall the host signal handlers in this case. Which is what we want for "ERROR_AND_DIE"
664 lines
21 KiB
C++
664 lines
21 KiB
C++
/*
|
|
$info$
|
|
tags: Bin|FEXLoader
|
|
desc: Glues the ELF loader, FEXCore and LinuxSyscalls to launch an elf under fex
|
|
$end_info$
|
|
*/
|
|
|
|
#include "Common/ArgumentLoader.h"
|
|
#include "Common/RootFSSetup.h"
|
|
#include "ELFCodeLoader2.h"
|
|
#include "Tests/LinuxSyscalls/LinuxAllocator.h"
|
|
#include "Tests/LinuxSyscalls/Syscalls.h"
|
|
#include "Tests/LinuxSyscalls/x32/Syscalls.h"
|
|
#include "Tests/LinuxSyscalls/x64/Syscalls.h"
|
|
#include "Tests/LinuxSyscalls/SignalDelegator.h"
|
|
#include "Linux/Utils/ELFContainer.h"
|
|
|
|
#include <FEXCore/Config/Config.h>
|
|
#include <FEXCore/Core/Context.h>
|
|
#include <FEXCore/Core/CoreState.h>
|
|
#include <FEXCore/Utils/Allocator.h>
|
|
#include <FEXCore/Utils/LogManager.h>
|
|
#include <FEXCore/Utils/Telemetry.h>
|
|
#include <FEXCore/Utils/Threads.h>
|
|
|
|
#include <atomic>
|
|
#include <cerrno>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <elf.h>
|
|
#include <fcntl.h>
|
|
#include <fstream>
|
|
#include <filesystem>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <queue>
|
|
#include <set>
|
|
#include <string>
|
|
#include <sys/auxv.h>
|
|
#include <sys/resource.h>
|
|
#include <sys/select.h>
|
|
#include <system_error>
|
|
#include <thread>
|
|
#include <unistd.h>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include <fmt/format.h>
|
|
#include <sys/sysinfo.h>
|
|
#include <sys/signal.h>
|
|
|
|
namespace {
|
|
static bool SilentLog;
|
|
static int OutputFD {STDERR_FILENO};
|
|
static bool ExecutedWithFD {false};
|
|
|
|
void MsgHandler(LogMan::DebugLevels Level, char const *Message) {
|
|
if (SilentLog) {
|
|
return;
|
|
}
|
|
|
|
const char *CharLevel{nullptr};
|
|
|
|
switch (Level) {
|
|
case LogMan::NONE:
|
|
CharLevel = "NONE";
|
|
break;
|
|
case LogMan::ASSERT:
|
|
CharLevel = "ASSERT";
|
|
break;
|
|
case LogMan::ERROR:
|
|
CharLevel = "ERROR";
|
|
break;
|
|
case LogMan::DEBUG:
|
|
CharLevel = "DEBUG";
|
|
break;
|
|
case LogMan::INFO:
|
|
CharLevel = "Info";
|
|
break;
|
|
case LogMan::STDOUT:
|
|
CharLevel = "STDOUT";
|
|
break;
|
|
case LogMan::STDERR:
|
|
CharLevel = "STDERR";
|
|
break;
|
|
default:
|
|
CharLevel = "???";
|
|
break;
|
|
}
|
|
|
|
const auto Output = fmt::format("[{}] {}\n", CharLevel, Message);
|
|
write(OutputFD, Output.c_str(), Output.size());
|
|
fsync(OutputFD);
|
|
}
|
|
|
|
void AssertHandler(char const *Message) {
|
|
if (SilentLog) {
|
|
return;
|
|
}
|
|
|
|
const auto Output = fmt::format("[ASSERT] {}\n", Message);
|
|
write(OutputFD, Output.c_str(), Output.size());
|
|
fsync(OutputFD);
|
|
}
|
|
|
|
bool CheckMemMapping() {
|
|
std::fstream fs("/proc/self/maps", std::fstream::in | std::fstream::binary);
|
|
std::string Line;
|
|
while (std::getline(fs, Line)) {
|
|
if (fs.eof()) {
|
|
break;
|
|
}
|
|
uint64_t Begin{};
|
|
uint64_t End{};
|
|
if (sscanf(Line.c_str(), "%lx-%lx", &Begin, &End) == 2) {
|
|
// If a memory range is living inside the 32bit memory space then we have a problem
|
|
if (Begin < 0x1'0000'0000) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
} // Anonymous namespace
|
|
|
|
void InterpreterHandler(std::string *Filename, std::string const &RootFS, std::vector<std::string> *args) {
|
|
// Open the file pointer to the filename and see if we need to find an interpreter
|
|
std::fstream File(*Filename, std::fstream::in | std::fstream::binary);
|
|
|
|
if (!File.is_open()) {
|
|
return;
|
|
}
|
|
|
|
File.seekg(0, std::fstream::end);
|
|
const auto FileSize = File.tellg();
|
|
File.seekg(0, std::fstream::beg);
|
|
|
|
// Is the file large enough for shebang
|
|
if (FileSize <= 2) {
|
|
return;
|
|
}
|
|
|
|
// Handle shebang files
|
|
if (File.get() == '#' &&
|
|
File.get() == '!') {
|
|
std::string InterpreterLine;
|
|
std::getline(File, InterpreterLine);
|
|
std::vector<std::string> ShebangArguments{};
|
|
|
|
// Shebang line can have a single argument
|
|
std::istringstream InterpreterSS(InterpreterLine);
|
|
std::string Argument;
|
|
while (std::getline(InterpreterSS, Argument, ' ')) {
|
|
if (Argument.empty()) {
|
|
continue;
|
|
}
|
|
ShebangArguments.push_back(std::move(Argument));
|
|
}
|
|
|
|
// Executable argument
|
|
std::string &ShebangProgram = ShebangArguments[0];
|
|
|
|
// If the filename is absolute then prepend the rootfs
|
|
// If it is relative then don't append the rootfs
|
|
if (ShebangProgram[0] == '/') {
|
|
ShebangProgram = RootFS + ShebangProgram;
|
|
}
|
|
*Filename = ShebangProgram;
|
|
|
|
// Insert all the arguments at the start
|
|
args->insert(args->begin(), ShebangArguments.begin(), ShebangArguments.end());
|
|
|
|
// Done here
|
|
return;
|
|
}
|
|
}
|
|
|
|
bool RanAsInterpreter(const char *Program) {
|
|
return ExecutedWithFD || strstr(Program, "FEXInterpreter") != nullptr;
|
|
}
|
|
|
|
bool IsInterpreterInstalled() {
|
|
// The interpreter is installed if both the binfmt_misc handlers are available
|
|
// Or if we were originally executed with FD. Which means the interpreter is installed
|
|
|
|
std::error_code ec{};
|
|
return ExecutedWithFD ||
|
|
(std::filesystem::exists("/proc/sys/fs/binfmt_misc/FEX-x86", ec) &&
|
|
std::filesystem::exists("/proc/sys/fs/binfmt_misc/FEX-x86_64", ec));
|
|
}
|
|
|
|
void AOTGenSection(FEXCore::Context::Context *CTX, ELFCodeLoader2::LoadedSection &Section) {
|
|
|
|
// Make sure this section is executable and big enough
|
|
if (!Section.Executable || Section.Size < 16)
|
|
return;
|
|
|
|
std::set<uintptr_t> InitialBranchTargets;
|
|
|
|
// Load the ELF again with symbol parsing this time
|
|
ELFLoader::ELFContainer container{Section.Filename, "", false};
|
|
|
|
// Add symbols to the branch targets list
|
|
container.AddSymbols([&](ELFLoader::ELFSymbol* sym) {
|
|
auto Destination = sym->Address + Section.ElfBase;
|
|
|
|
if (! (Destination >= Section.Base && Destination <= (Section.Base + Section.Size)) ) {
|
|
return; // outside of current section, unlikely to be real code
|
|
}
|
|
|
|
InitialBranchTargets.insert(Destination);
|
|
});
|
|
|
|
LogMan::Msg::IFmt("Symbol seed: {}", InitialBranchTargets.size());
|
|
|
|
// Add unwind entries to the branch target list
|
|
container.AddUnwindEntries([&](uintptr_t Entry) {
|
|
auto Destination = Entry + Section.ElfBase;
|
|
|
|
if (! (Destination >= Section.Base && Destination <= (Section.Base + Section.Size)) ) {
|
|
return; // outside of current section, unlikely to be real code
|
|
}
|
|
|
|
InitialBranchTargets.insert(Destination);
|
|
});
|
|
|
|
LogMan::Msg::IFmt("Symbol + Unwind seed: {}", InitialBranchTargets.size());
|
|
|
|
// Scan the executable section and try to find function entries
|
|
for (size_t Offset = 0; Offset < (Section.Size - 16); Offset++) {
|
|
uint8_t *pCode = (uint8_t *)(Section.Base + Offset);
|
|
|
|
// Possible CALL <disp32>
|
|
if (*pCode == 0xE8) {
|
|
uintptr_t Destination = (int)(pCode[1] | (pCode[2] << 8) | (pCode[3] << 16) | (pCode[4] << 24));
|
|
Destination += (uintptr_t)pCode + 5;
|
|
|
|
auto DestinationPtr = (uint8_t*)Destination;
|
|
|
|
if (! (Destination >= Section.Base && Destination <= (Section.Base + Section.Size)) )
|
|
continue; // outside of current section, unlikely to be real code
|
|
|
|
if (DestinationPtr[0] == 0 && DestinationPtr[1] == 0)
|
|
continue; // add al, [rax], unlikely to be real code
|
|
|
|
InitialBranchTargets.insert(Destination);
|
|
}
|
|
|
|
// endbr64 marker marks an indirect branch destination
|
|
if (pCode[0] == 0xf3 && pCode[1] == 0x0f && pCode[2] == 0x1e && pCode[3] == 0xfa) {
|
|
InitialBranchTargets.insert((uintptr_t)pCode);
|
|
}
|
|
}
|
|
|
|
uint64_t SectionMaxAddress = Section.Base + Section.Size;
|
|
|
|
std::set<uint64_t> Compiled;
|
|
std::atomic<int> counter = 0;
|
|
|
|
std::queue<uint64_t> BranchTargets;
|
|
|
|
// Setup BranchTargets, Compiled sets from InitiaBranchTargets
|
|
|
|
Compiled.insert(InitialBranchTargets.begin(), InitialBranchTargets.end());
|
|
for (auto BranchTarget: InitialBranchTargets) {
|
|
BranchTargets.push(BranchTarget);
|
|
}
|
|
|
|
InitialBranchTargets.clear();
|
|
|
|
|
|
std::mutex QueueMutex;
|
|
std::vector<std::thread> ThreadPool;
|
|
|
|
for (int i = 0; i < get_nprocs_conf(); i++) {
|
|
std::thread thd([&BranchTargets, CTX, &counter, &Compiled, &Section, &QueueMutex, SectionMaxAddress]() {
|
|
// Set the priority of the thread so it doesn't overwhelm the system when running in the background
|
|
setpriority(PRIO_PROCESS, ::gettid(), 19);
|
|
|
|
// Setup thread - Each compilation thread uses its own backing FEX thread
|
|
FEXCore::Core::CPUState state;
|
|
auto Thread = FEXCore::Context::CreateThread(CTX, &state, gettid());
|
|
std::set<uint64_t> ExternalBranchesLocal;
|
|
FEXCore::Context::ConfigureAOTGen(Thread, &ExternalBranchesLocal, SectionMaxAddress);
|
|
|
|
|
|
for (;;) {
|
|
uint64_t BranchTarget;
|
|
|
|
// Get a entrypoint to process from the queue
|
|
QueueMutex.lock();
|
|
if (BranchTargets.empty()) {
|
|
QueueMutex.unlock();
|
|
break; // no entrypoint to process - exit
|
|
}
|
|
|
|
BranchTarget = BranchTargets.front();
|
|
BranchTargets.pop();
|
|
QueueMutex.unlock();
|
|
|
|
// Compile entrypoint
|
|
counter++;
|
|
FEXCore::Context::CompileRIP(Thread, BranchTarget);
|
|
|
|
// Are there more branches?
|
|
if (ExternalBranchesLocal.size() > 0) {
|
|
// Add them to the "to process" list
|
|
QueueMutex.lock();
|
|
for(auto Destination: ExternalBranchesLocal) {
|
|
if (! (Destination >= Section.Base && Destination <= (Section.Base + Section.Size)) )
|
|
continue;
|
|
if (Compiled.contains(Destination))
|
|
continue;
|
|
Compiled.insert(Destination);
|
|
BranchTargets.push(Destination);
|
|
}
|
|
QueueMutex.unlock();
|
|
ExternalBranchesLocal.clear();
|
|
}
|
|
}
|
|
|
|
// All entryproints processed, cleanup this thread
|
|
FEXCore::Context::DestroyThread(CTX, Thread);
|
|
});
|
|
|
|
// Add to the thread pool
|
|
ThreadPool.push_back(std::move(thd));
|
|
}
|
|
|
|
// Make sure all threads are finished
|
|
for (auto & Thread: ThreadPool) {
|
|
Thread.join();
|
|
}
|
|
|
|
ThreadPool.clear();
|
|
|
|
LogMan::Msg::IFmt("\nAll Done: {}", counter.load());
|
|
}
|
|
|
|
int main(int argc, char **argv, char **const envp) {
|
|
const bool IsInterpreter = RanAsInterpreter(argv[0]);
|
|
|
|
ExecutedWithFD = getauxval(AT_EXECFD) != 0;
|
|
|
|
LogMan::Throw::InstallHandler(AssertHandler);
|
|
LogMan::Msg::InstallHandler(MsgHandler);
|
|
|
|
#if !(defined(ENABLE_ASAN) && ENABLE_ASAN)
|
|
// LLVM ASAN maps things to the lower 32bits
|
|
// Valgrind also places us in the lower 32-bits
|
|
if (!getenv("VALGRIND_LAUNCHER") &&
|
|
!CheckMemMapping()) {
|
|
LogMan::Msg::EFmt("[Unsupported] FEX mapped to lower 32bits! Exiting!");
|
|
return -1;
|
|
}
|
|
#endif
|
|
|
|
FEXCore::Config::Initialize();
|
|
FEXCore::Config::AddLayer(FEXCore::Config::CreateMainLayer());
|
|
|
|
if (IsInterpreter) {
|
|
FEX::ArgLoader::LoadWithoutArguments(argc, argv);
|
|
}
|
|
else {
|
|
FEXCore::Config::AddLayer(std::make_unique<FEX::ArgLoader::ArgLoader>(argc, argv));
|
|
}
|
|
|
|
FEXCore::Config::AddLayer(FEXCore::Config::CreateEnvironmentLayer(envp));
|
|
FEXCore::Config::Load();
|
|
|
|
auto Args = FEX::ArgLoader::Get();
|
|
auto ParsedArgs = FEX::ArgLoader::GetParsedArgs();
|
|
|
|
if (Args.empty()) {
|
|
// Early exit if we weren't passed an argument
|
|
return 0;
|
|
}
|
|
|
|
std::string Program = Args[0];
|
|
|
|
// These layers load on initialization
|
|
auto ProgramName = std::filesystem::path(Program).filename();
|
|
FEXCore::Config::AddLayer(FEXCore::Config::CreateAppLayer(ProgramName, true));
|
|
FEXCore::Config::AddLayer(FEXCore::Config::CreateAppLayer(ProgramName, false));
|
|
|
|
// Reload the meta layer
|
|
FEXCore::Config::ReloadMetaLayer();
|
|
FEXCore::Config::Set(FEXCore::Config::CONFIG_IS_INTERPRETER, IsInterpreter ? "1" : "0");
|
|
FEXCore::Config::Set(FEXCore::Config::CONFIG_INTERPRETER_INSTALLED, IsInterpreterInstalled() ? "1" : "0");
|
|
|
|
// Early check for process stall
|
|
// Doesn't use CONFIG_ROOTFS and we don't want it to spin up a squashfs instance
|
|
FEX_CONFIG_OPT(StallProcess, STALLPROCESS);
|
|
if (StallProcess) {
|
|
while (1) {
|
|
// Stall this process out forever
|
|
select(0, nullptr, nullptr, nullptr, nullptr);
|
|
}
|
|
}
|
|
|
|
// Ensure RootFS is setup before config options try to pull CONFIG_ROOTFS
|
|
if (!FEX::RootFS::Setup(envp)) {
|
|
LogMan::Msg::EFmt("RootFS failure");
|
|
return -1;
|
|
}
|
|
|
|
FEX_CONFIG_OPT(SilentLog, SILENTLOG);
|
|
FEX_CONFIG_OPT(AOTIRCapture, AOTIRCAPTURE);
|
|
FEX_CONFIG_OPT(AOTIRGenerate, AOTIRGENERATE);
|
|
FEX_CONFIG_OPT(AOTIRLoad, AOTIRLOAD);
|
|
FEX_CONFIG_OPT(OutputLog, OUTPUTLOG);
|
|
FEX_CONFIG_OPT(LDPath, ROOTFS);
|
|
FEX_CONFIG_OPT(Environment, ENV);
|
|
FEX_CONFIG_OPT(HostEnvironment, HOSTENV);
|
|
::SilentLog = SilentLog();
|
|
|
|
if (!::SilentLog) {
|
|
auto LogFile = OutputLog();
|
|
// If stderr or stdout then we need to dup the FD
|
|
// In some cases some applications will close stderr and stdout
|
|
// then redirect the FD to either a log OR some cases just not use
|
|
// stderr/stdout and the FD will be reused for regular FD ops.
|
|
//
|
|
// We want to maintain the original output location otherwise we
|
|
// can run in to problems of writing to some file
|
|
if (LogFile == "stderr") {
|
|
OutputFD = dup(STDERR_FILENO);
|
|
}
|
|
else if (LogFile == "stdout") {
|
|
OutputFD = dup(STDOUT_FILENO);
|
|
}
|
|
else if (!LogFile.empty()) {
|
|
OutputFD = open(LogFile.c_str(), O_CREAT | O_CLOEXEC | O_WRONLY);
|
|
}
|
|
}
|
|
|
|
FEXCore::Telemetry::Initialize();
|
|
InterpreterHandler(&Program, LDPath(), &Args);
|
|
|
|
std::error_code ec{};
|
|
if (!std::filesystem::exists(Program, ec)) {
|
|
// Early exit if the program passed in doesn't exist
|
|
// Will prevent a crash later
|
|
fmt::print(stderr, "{}: command not found\n", Program);
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
|
|
uint32_t KernelVersion = FEX::HLE::SyscallHandler::CalculateHostKernelVersion();
|
|
if (KernelVersion < FEX::HLE::SyscallHandler::KernelVersion(4, 17)) {
|
|
// We require 4.17 minimum for MAP_FIXED_NOREPLACE
|
|
LogMan::Msg::EFmt("FEXLoader requires kernel 4.17 minimum. Expect problems.");
|
|
}
|
|
|
|
// Before we go any further, set all of our host environment variables that the config has provided
|
|
for (auto &HostEnv : HostEnvironment.All()) {
|
|
// We are going to keep these alive in memory.
|
|
// No need to split the string with setenv
|
|
putenv(HostEnv.data());
|
|
}
|
|
|
|
ELFCodeLoader2 Loader{Program, LDPath(), Args, ParsedArgs, envp, &Environment};
|
|
//FEX::HarnessHelper::ELFCodeLoader Loader{Program, LDPath(), Args, ParsedArgs, envp, &Environment};
|
|
|
|
if (!Loader.ELFWasLoaded()) {
|
|
// Loader couldn't load this program for some reason
|
|
fmt::print(stderr, "Invalid or Unsupported elf file.\n");
|
|
#ifdef _M_ARM_64
|
|
fmt::print(stderr, "This is likely due to a misconfigured x86-64 RootFS\n");
|
|
fmt::print(stderr, "Current RootFS path set to '{}'\n", LDPath());
|
|
std::error_code ec;
|
|
if (LDPath().empty() ||
|
|
std::filesystem::exists(LDPath(), ec) == false) {
|
|
fmt::print(stderr, "RootFS path doesn't exist. This is required on AArch64 hosts\n");
|
|
}
|
|
#endif
|
|
return -ENOEXEC;
|
|
}
|
|
|
|
FEXCore::Config::Set(FEXCore::Config::CONFIG_APP_FILENAME, std::filesystem::canonical(Program).string());
|
|
FEXCore::Config::Set(FEXCore::Config::CONFIG_IS64BIT_MODE, Loader.Is64BitMode() ? "1" : "0");
|
|
|
|
std::unique_ptr<FEX::HLE::MemAllocator> Allocator;
|
|
FEXCore::Allocator::PtrCache *Base48Bit{};
|
|
|
|
if (Loader.Is64BitMode()) {
|
|
// Destroy the 48th bit if it exists
|
|
Base48Bit = FEXCore::Allocator::Steal48BitVA();
|
|
if (!Loader.MapMemory([](void *addr, size_t length, int prot, int flags, int fd, off_t offset) {
|
|
return FEXCore::Allocator::mmap(addr, length, prot, flags, fd, offset);
|
|
}, [](void *addr, size_t length) {
|
|
return FEXCore::Allocator::munmap(addr, length);
|
|
})) {
|
|
// failed to map
|
|
LogMan::Msg::EFmt("Failed to map 64-bit elf file.");
|
|
return -ENOEXEC;
|
|
}
|
|
} else {
|
|
FEX_CONFIG_OPT(Use32BitAllocator, FORCE32BITALLOCATOR);
|
|
if (KernelVersion < FEX::HLE::SyscallHandler::KernelVersion(4, 17)) {
|
|
Use32BitAllocator = true;
|
|
}
|
|
|
|
// Setup our userspace allocator
|
|
if (!Use32BitAllocator &&
|
|
KernelVersion >= FEX::HLE::SyscallHandler::KernelVersion(4, 17)) {
|
|
FEXCore::Allocator::SetupHooks();
|
|
}
|
|
|
|
if (Use32BitAllocator) {
|
|
Allocator = FEX::HLE::Create32BitAllocator();
|
|
}
|
|
else {
|
|
Allocator = FEX::HLE::CreatePassthroughAllocator();
|
|
}
|
|
|
|
if (!Loader.MapMemory([&Allocator](void *addr, size_t length, int prot, int flags, int fd, off_t offset) {
|
|
return Allocator->mmap(addr, length, prot, flags, fd, offset);
|
|
}, [&Allocator](void *addr, size_t length) {
|
|
return Allocator->munmap(addr, length);
|
|
})) {
|
|
// failed to map
|
|
LogMan::Msg::EFmt("Failed to map 32-bit elf file.");
|
|
return -ENOEXEC;
|
|
}
|
|
}
|
|
|
|
// System allocator is now system allocator or FEX
|
|
FEXCore::Context::InitializeStaticTables(Loader.Is64BitMode() ? FEXCore::Context::MODE_64BIT : FEXCore::Context::MODE_32BIT);
|
|
|
|
auto CTX = FEXCore::Context::CreateNewContext();
|
|
FEXCore::Context::InitializeContext(CTX);
|
|
|
|
auto SignalDelegation = std::make_unique<FEX::HLE::SignalDelegator>();
|
|
|
|
SignalDelegation->RegisterFrontendHostSignalHandler(SIGILL, [&SignalDelegation](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext) -> bool {
|
|
ucontext_t* _context = (ucontext_t*)ucontext;
|
|
auto &mcontext = _context->uc_mcontext;
|
|
uint64_t PC{};
|
|
#ifdef _M_ARM_64
|
|
PC = mcontext.pc;
|
|
#else
|
|
PC = mcontext.gregs[REG_RIP];
|
|
#endif
|
|
if (PC == reinterpret_cast<uint64_t>(&FEXCore::Assert::ForcedAssert)) {
|
|
// This is a host side assert. Don't deliver this to the guest
|
|
// We want to actually break here
|
|
SignalDelegation->UninstallHostHandler(Signal);
|
|
return true;
|
|
}
|
|
return false;
|
|
}, true);
|
|
|
|
auto SyscallHandler = Loader.Is64BitMode() ? FEX::HLE::x64::CreateHandler(CTX, SignalDelegation.get())
|
|
: FEX::HLE::x32::CreateHandler(CTX, SignalDelegation.get(), std::move(Allocator));
|
|
|
|
SyscallHandler->SetCodeLoader(&Loader);
|
|
|
|
auto BRKInfo = Loader.GetBRKInfo();
|
|
|
|
SyscallHandler->DefaultProgramBreak(BRKInfo.Base, BRKInfo.Size);
|
|
|
|
FEXCore::Context::SetSignalDelegator(CTX, SignalDelegation.get());
|
|
FEXCore::Context::SetSyscallHandler(CTX, SyscallHandler.get());
|
|
FEXCore::Context::InitCore(CTX, &Loader);
|
|
|
|
FEXCore::Context::ExitReason ShutdownReason = FEXCore::Context::ExitReason::EXIT_SHUTDOWN;
|
|
|
|
// There might already be an exit handler, leave it installed
|
|
if(!FEXCore::Context::GetExitHandler(CTX)) {
|
|
FEXCore::Context::SetExitHandler(CTX, [&](uint64_t thread, FEXCore::Context::ExitReason reason) {
|
|
if (reason != FEXCore::Context::ExitReason::EXIT_DEBUG) {
|
|
ShutdownReason = reason;
|
|
FEXCore::Context::Stop(CTX);
|
|
}
|
|
});
|
|
}
|
|
|
|
if (AOTIRLoad() || AOTIRCapture() || AOTIRGenerate()) {
|
|
LogMan::Msg::IFmt("Warning: AOTIR is experimental, and might lead to crashes. "
|
|
"Capture doesn't work with programs that fork.");
|
|
}
|
|
|
|
FEXCore::Context::SetAOTIRLoader(CTX, [](const std::string &fileid) -> int {
|
|
auto filepath = std::filesystem::path(FEXCore::Config::GetDataDirectory()) / "aotir" / (fileid + ".aotir");
|
|
|
|
return open(filepath.c_str(), O_RDONLY);
|
|
});
|
|
|
|
FEXCore::Context::SetAOTIRWriter(CTX, [](const std::string& fileid) -> std::unique_ptr<std::ostream> {
|
|
auto filepath = std::filesystem::path(FEXCore::Config::GetDataDirectory()) / "aotir" / (fileid + ".aotir");
|
|
auto AOTWrite = std::make_unique<std::ofstream>(filepath, std::ios::out | std::ios::binary);
|
|
if (*AOTWrite) {
|
|
std::filesystem::resize_file(filepath, 0);
|
|
AOTWrite->seekp(0);
|
|
LogMan::Msg::IFmt("AOTIR: Storing {}", fileid);
|
|
} else {
|
|
LogMan::Msg::IFmt("AOTIR: Failed to store {}", fileid);
|
|
}
|
|
return AOTWrite;
|
|
});
|
|
|
|
for(const auto &Section: *Loader.Sections) {
|
|
FEXCore::Context::AddNamedRegion(CTX, Section.Base, Section.Size, Section.Offs, Section.Filename);
|
|
}
|
|
|
|
if (AOTIRGenerate()) {
|
|
for(auto &Section: *Loader.Sections) {
|
|
AOTGenSection(CTX, Section);
|
|
}
|
|
} else {
|
|
FEXCore::Context::RunUntilExit(CTX);
|
|
}
|
|
|
|
if (std::filesystem::create_directories(std::filesystem::path(FEXCore::Config::GetDataDirectory()) / "aotir", ec)) {
|
|
FEXCore::Context::WriteFilesWithCode(CTX, [](const std::string& fileid, const std::string& filename) {
|
|
auto filepath = std::filesystem::path(FEXCore::Config::GetDataDirectory()) / "aotir" / (fileid + ".path");
|
|
int fd = open(filepath.c_str(), O_CREAT | O_EXCL | O_WRONLY, 0644);
|
|
if (fd != -1) {
|
|
write(fd, filename.c_str(), filename.size());
|
|
close(fd);
|
|
}
|
|
});
|
|
}
|
|
|
|
if (AOTIRCapture() || AOTIRGenerate()) {
|
|
FEXCore::Context::FinalizeAOTIRCache(CTX);
|
|
LogMan::Msg::IFmt("AOTIR Cache Stored");
|
|
}
|
|
|
|
auto ProgramStatus = FEXCore::Context::GetProgramStatus(CTX);
|
|
|
|
SyscallHandler.reset();
|
|
SignalDelegation.reset();
|
|
FEXCore::Context::DestroyContext(CTX);
|
|
|
|
FEXCore::Context::ShutdownStaticTables();
|
|
FEXCore::Threads::Shutdown();
|
|
|
|
Loader.FreeSections();
|
|
|
|
FEX::RootFS::Shutdown();
|
|
|
|
FEXCore::Config::Shutdown();
|
|
|
|
LogMan::Throw::UnInstallHandlers();
|
|
LogMan::Msg::UnInstallHandlers();
|
|
|
|
FEXCore::Allocator::ClearHooks();
|
|
FEXCore::Allocator::ReclaimMemoryRegion(Base48Bit);
|
|
// Allocator is now original system allocator
|
|
|
|
FEXCore::Telemetry::Shutdown(ProgramName);
|
|
if (ShutdownReason == FEXCore::Context::ExitReason::EXIT_SHUTDOWN) {
|
|
return ProgramStatus;
|
|
}
|
|
else {
|
|
return -64 | ShutdownReason;
|
|
}
|
|
}
|