Files
FEX-Emu--FEX/Source/Tests/ELFLoader.cpp
T
Ryan Houdek db49ab4149 Changes the frontend FEXLoader argument loading path
If the interpreter was executed then don't even try handling arguments.
They will all get passed to the guest application
2020-12-08 07:28:50 -08:00

289 lines
9.5 KiB
C++

#include "Common/ArgumentLoader.h"
#include "Common/EnvironmentLoader.h"
#include "Common/Config.h"
#include "HarnessHelpers.h"
#include <FEXCore/Config/Config.h>
#include <FEXCore/Core/CodeLoader.h>
#include <FEXCore/Core/Context.h>
#include <FEXCore/Memory/SharedMem.h>
#include <FEXCore/Utils/ELFLoader.h>
#include <FEXCore/Utils/LogManager.h>
#include <cstdint>
#include <filesystem>
#include <string>
#include <unistd.h>
#include <vector>
namespace {
static bool SilentLog;
static FILE *OutputFD {stdout};
void MsgHandler(LogMan::DebugLevels Level, char const *Message) {
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;
}
if (!SilentLog) {
fprintf(OutputFD, "[%s] %s\n", CharLevel, Message);
fflush(OutputFD);
}
}
void AssertHandler(char const *Message) {
if (!SilentLog) {
fprintf(OutputFD, "[ASSERT] %s\n", Message);
fflush(OutputFD);
}
}
bool CheckMemMapping() {
std::fstream fs;
fs.open("/proc/self/maps", std::fstream::in | std::fstream::binary);
std::string Line;
while (std::getline(fs, Line)) {
if (fs.eof()) break;
uint64_t Begin, 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;
}
}
}
fs.close();
return true;
}
}
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;
size_t FileSize{0};
File.open(*Filename, std::fstream::in | std::fstream::binary);
if (!File.is_open())
return;
File.seekg(0, File.end);
FileSize = File.tellg();
File.seekg(0, File.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);
// Shebang line can have a single argument
std::istringstream InterpreterSS(InterpreterLine);
std::string Argument;
if (std::getline(InterpreterSS, Argument, ' ')) {
// If the filename is absolute then prepend the rootfs
// If it is relative then don't append the rootfs
if (Argument[0] == '/') {
*Filename = RootFS + Argument;
}
else {
*Filename = Argument;
}
// Push the filename in to the argument
args->emplace(args->begin(), *Filename);
}
// Now check for argument
if (std::getline(InterpreterSS, Argument, ' ')) {
// Insert after the interpreter filename
args->emplace(args->begin() + 1, Argument);
}
// Done here
return;
}
}
bool RanAsInterpreter(char *Program) {
return strstr(Program, "FEXInterpreter") != nullptr;
}
int main(int argc, char **argv, char **const envp) {
bool IsInterpreter = RanAsInterpreter(argv[0]);
LogMan::Throw::InstallHandler(AssertHandler);
LogMan::Msg::InstallHandler(MsgHandler);
if (!CheckMemMapping()) {
LogMan::Msg::E("[Unsupported] FEX mapped to lower 32bits! Exiting!");
return -1;
}
FEXCore::Config::Initialize();
FEXCore::Config::AddLayer(std::make_unique<FEX::Config::MainLoader>());
if (IsInterpreter) {
FEX::ArgLoader::LoadWithoutArguments(argc, argv);
}
else {
FEXCore::Config::AddLayer(std::make_unique<FEX::ArgLoader::ArgLoader>(argc, argv));
}
FEXCore::Config::AddLayer(std::make_unique<FEX::Config::EnvLoader>(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
FEXCore::Config::AddLayer(std::make_unique<FEX::Config::AppLoader>(std::filesystem::path(Program).filename(), true));
FEXCore::Config::AddLayer(std::make_unique<FEX::Config::AppLoader>(std::filesystem::path(Program).filename(), false));
// Reload the meta layer
FEXCore::Config::ReloadMetaLayer();
FEXCore::Config::Set(FEXCore::Config::CONFIG_IS_INTERPRETER, IsInterpreter ? "1" : "0");
FEXCore::Config::Value<uint8_t> CoreConfig{FEXCore::Config::CONFIG_DEFAULTCORE, 0};
FEXCore::Config::Value<uint64_t> BlockSizeConfig{FEXCore::Config::CONFIG_MAXBLOCKINST, 1};
FEXCore::Config::Value<bool> SingleStepConfig{FEXCore::Config::CONFIG_SINGLESTEP, false};
FEXCore::Config::Value<bool> MultiblockConfig{FEXCore::Config::CONFIG_MULTIBLOCK, false};
FEXCore::Config::Value<bool> GdbServerConfig{FEXCore::Config::CONFIG_GDBSERVER, false};
FEXCore::Config::Value<uint64_t> ThreadsConfig{FEXCore::Config::CONFIG_EMULATED_CPU_CORES, 1};
FEXCore::Config::Value<std::string> LDPath{FEXCore::Config::CONFIG_ROOTFSPATH, ""};
FEXCore::Config::Value<std::string> ThunkLibsPath{FEXCore::Config::CONFIG_THUNKLIBSPATH, ""};
FEXCore::Config::Value<bool> SilentLog{FEXCore::Config::CONFIG_SILENTLOGS, false};
FEXCore::Config::Value<std::string> Environment{FEXCore::Config::CONFIG_ENVIRONMENT, ""};
FEXCore::Config::Value<std::string> OutputLog{FEXCore::Config::CONFIG_OUTPUTLOG, "stderr"};
FEXCore::Config::Value<std::string> DumpIR{FEXCore::Config::CONFIG_DUMPIR, "no"};
FEXCore::Config::Value<bool> TSOEnabledConfig{FEXCore::Config::CONFIG_TSO_ENABLED, true};
FEXCore::Config::Value<bool> SMCChecksConfig{FEXCore::Config::CONFIG_SMC_CHECKS, false};
FEXCore::Config::Value<bool> ABILocalFlags{FEXCore::Config::CONFIG_ABI_LOCAL_FLAGS, false};
FEXCore::Config::Value<bool> AbiNoPF{FEXCore::Config::CONFIG_ABI_NO_PF, false};
::SilentLog = SilentLog();
if (!::SilentLog) {
auto LogFile = OutputLog();
if (LogFile == "stderr") {
OutputFD = stderr;
}
else if (LogFile == "stdout") {
OutputFD = stdout;
}
else if (!LogFile.empty()) {
OutputFD = fopen(LogFile.c_str(), "wb");
}
}
InterpreterHandler(&Program, LDPath(), &Args);
if (!std::filesystem::exists(Program)) {
// Early exit if the program passed in doesn't exist
// Will prevent a crash later
LogMan::Msg::E("%s: command not found", Program.c_str());
return 0;
}
FEX::HarnessHelper::ELFCodeLoader Loader{Program, LDPath(), Args, ParsedArgs, envp, &Environment};
FEXCore::Context::InitializeStaticTables(Loader.Is64BitMode() ? FEXCore::Context::MODE_64BIT : FEXCore::Context::MODE_32BIT);
uint64_t VMemSize = 1ULL << 36;
if (!Loader.Is64BitMode()) {
VMemSize = 1ULL << 32;
}
auto SHM = FEXCore::SHM::AllocateSHMRegion(VMemSize);
auto CTX = FEXCore::Context::CreateNewContext();
FEXCore::Context::InitializeContext(CTX);
FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_DEFAULTCORE, CoreConfig() > 3 ? FEXCore::Config::CONFIG_CUSTOM : CoreConfig());
FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_MULTIBLOCK, MultiblockConfig());
FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_SINGLESTEP, SingleStepConfig());
FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_MAXBLOCKINST, BlockSizeConfig());
FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_GDBSERVER, GdbServerConfig());
FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_ROOTFSPATH, LDPath());
FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_THUNKLIBSPATH, ThunkLibsPath());
FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_UNIFIED_MEMORY, true);
FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_IS64BIT_MODE, Loader.Is64BitMode());
FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_EMULATED_CPU_CORES, ThreadsConfig());
FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_TSO_ENABLED, TSOEnabledConfig());
FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_SMC_CHECKS, SMCChecksConfig());
FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_ABI_LOCAL_FLAGS, ABILocalFlags());
FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_ABI_NO_PF, AbiNoPF());
FEXCore::Config::SetConfig(CTX, FEXCore::Config::CONFIG_DUMPIR, DumpIR());
FEXCore::Context::AddGuestMemoryRegion(CTX, SHM);
FEXCore::Context::InitCore(CTX, &Loader);
FEXCore::Context::SetApplicationFile(CTX, std::filesystem::canonical(Program));
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);
}
});
}
FEXCore::Context::RunUntilExit(CTX);
auto ProgramStatus = FEXCore::Context::GetProgramStatus(CTX);
FEXCore::Context::DestroyContext(CTX);
FEXCore::SHM::DestroyRegion(SHM);
FEXCore::Config::Shutdown();
if (OutputFD != stderr &&
OutputFD != stdout &&
OutputFD != nullptr) {
fclose(OutputFD);
}
if (ShutdownReason == FEXCore::Context::ExitReason::EXIT_SHUTDOWN) {
return ProgramStatus;
}
else {
return -64 | ShutdownReason;
}
}