From d052e87d4761527318baf0eba9e869342e87181d Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Mon, 2 Aug 2021 17:04:39 -0700 Subject: [PATCH 1/4] AArch64: Workaround static-pie crashing Static-pie can only work with GNU ld instead of lld on AArch64 hosts. This is a known problem on the llvm side: https://bugs.llvm.org/show_bug.cgi?id=49672 --- CMakeLists.txt | 93 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 71 insertions(+), 22 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0fb9eeb87..970e702a9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,6 +46,25 @@ else() set(CMAKE_INTERPROCEDURAL_OPTIMIZATION FALSE) endif() +if (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") + option(ENABLE_X86_HOST_DEBUG "Enables compiling on x86_64 host" FALSE) + if (NOT ENABLE_X86_HOST_DEBUG) + message(FATAL_ERROR + " Be warned: FEX isn't optimized for x86_64 hosts!\n" + " Support for x86_64 hosts is only for debugging and convenience!\n" + " Don't expect amazing performance or optimal code generation!\n" + " Pass -DENABLE_X86_HOST_DEBUG=True to bypass this message!") + endif() + set(_M_X86_64 1) + add_definitions(-D_M_X86_64=1) + set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcx16") +endif() + +if (CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64") + set(_M_ARM_64 1) + add_definitions(-D_M_ARM_64=1) +endif() + find_program(CCACHE_PROGRAM ccache) if(CCACHE_PROGRAM) message(STATUS "CCache enabled") @@ -64,8 +83,8 @@ if (ENABLE_LLD) endif() if (ENABLE_STATIC_PIE) - if (NOT ENABLE_LLD) - message (FATAL_ERROR "static-pie requires linking with lld") + if (_M_ARM_64 AND ENABLE_LLD) + message (FATAL_ERROR "Static linking does not currently work with AArch64+LLD. Use GNU ld for now.") endif() file(WRITE ${PROJECT_BINARY_DIR}/CMakeFiles/CMakeTmp/Determine_iplt.c @@ -129,7 +148,56 @@ if (ENABLE_STATIC_PIE) set (STATIC_PIE_OPTIONS "-static-pie") # Pthreads has an issue with exposing symbols # We need to make some concessions to the pthread gods - set (PTHREAD_LIB -Wl,--undefined-glob=pthread_* pthread) + if (ENABLE_LLD) + set (PTHREAD_LIB + -Wl,--undefined-glob=pthread_* + -Wl,--undefined=__cxa_finalize + -Wl,--undefined=_pthread_cleanup_push_defer + -Wl,--undefined=_pthread_cleanup_pop_restore + -Wl,--undefined=__pthread_cleanup_upto + pthread) + else() + set (PTHREAD_LIB + -Wl,--undefined=pthread_join + -Wl,--undefined=pthread_attr_getdetachstate + -Wl,--undefined=pthread_sigmask + -Wl,--undefined=pthread_mutex_lock + -Wl,--undefined=pthread_cond_init + -Wl,--undefined=pthread_attr_init + -Wl,--undefined=pthread_mutex_unlock + -Wl,--undefined=pthread_mutexattr_destroy + -Wl,--undefined=pthread_detach + -Wl,--undefined=pthread_mutex_init + -Wl,--undefined=pthread_getattr_np + -Wl,--undefined=pthread_cond_timedwait + -Wl,--undefined=pthread_attr_destroy + -Wl,--undefined=pthread_mutexattr_settype + -Wl,--undefined=pthread_rwlock_unlock + -Wl,--undefined=pthread_rwlock_wrlock + -Wl,--undefined=pthread_setspecific + -Wl,--undefined=pthread_create + -Wl,--undefined=pthread_cond_clockwait + -Wl,--undefined=pthread_key_create + -Wl,--undefined=pthread_rwlock_rdlock + -Wl,--undefined=pthread_setname_np + -Wl,--undefined=pthread_cond_signal + -Wl,--undefined=pthread_mutexattr_init + -Wl,--undefined=pthread_attr_setstack + -Wl,--undefined=pthread_self + -Wl,--undefined=pthread_getaffinity_np + -Wl,--undefined=pthread_cond_wait + -Wl,--undefined=pthread_mutex_trylock + -Wl,--undefined=pthread_cond_broadcast + -Wl,--undefined=pthread_cond_destroy + -Wl,--undefined=pthread_getspecific + -Wl,--undefined=pthread_key_delete + -Wl,--undefined=pthread_once + -Wl,--undefined=__cxa_finalize + -Wl,--undefined=_pthread_cleanup_push_defer + -Wl,--undefined=_pthread_cleanup_pop_restore + -Wl,--undefined=__pthread_cleanup_upto + pthread) + endif() else() message (FATAL_ERROR "Application has __rela_iplt_{start,end} symbols. Which means static-pie can't be enabled") endif() @@ -165,25 +233,6 @@ set (CMAKE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_LINKER_FLAGS_RELWITHDEBINFO} -fn set (CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fomit-frame-pointer") set (CMAKE_LINKER_FLAGS_RELEASE "${CMAKE_LINKER_FLAGS_RELEASE} -fomit-frame-pointer") -if (CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64") - option(ENABLE_X86_HOST_DEBUG "Enables compiling on x86_64 host" FALSE) - if (NOT ENABLE_X86_HOST_DEBUG) - message(FATAL_ERROR - " Be warned: FEX isn't optimized for x86_64 hosts!\n" - " Support for x86_64 hosts is only for debugging and convenience!\n" - " Don't expect amazing performance or optimal code generation!\n" - " Pass -DENABLE_X86_HOST_DEBUG=True to bypass this message!") - endif() - set(_M_X86_64 1) - add_definitions(-D_M_X86_64=1) - set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mcx16") -endif() - -if (CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64") - set(_M_ARM_64 1) - add_definitions(-D_M_ARM_64=1) -endif() - add_subdirectory(External/vixl/) include_directories(External/vixl/src/) From c49fa5a64f4f9ea8de0d1033f7ce50258657d028 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Mon, 2 Aug 2021 22:47:42 -0700 Subject: [PATCH 2/4] Safely check if we can create paths rather than crashing on failure. --- External/FEXCore/Source/Common/Paths.cpp | 3 ++- .../Source/Interface/Config/Config.cpp | 11 +++++----- Source/Tests/FEXLoader.cpp | 21 ++++++++++--------- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/External/FEXCore/Source/Common/Paths.cpp b/External/FEXCore/Source/Common/Paths.cpp index 54e5f0440..598bf245c 100644 --- a/External/FEXCore/Source/Common/Paths.cpp +++ b/External/FEXCore/Source/Common/Paths.cpp @@ -36,9 +36,10 @@ namespace FEXCore::Paths { *CachePath += "/.fex-emu/"; *EntryCache = *CachePath + "/EntryCache/"; + std::error_code ec{}; // Ensure the folder structure is created for our Data if (!std::filesystem::exists(*EntryCache) && - !std::filesystem::create_directories(*EntryCache)) { + !std::filesystem::create_directories(*EntryCache, ec)) { LogMan::Msg::D("Couldn't create EntryCache directory: '%s'", EntryCache->c_str()); } } diff --git a/External/FEXCore/Source/Interface/Config/Config.cpp b/External/FEXCore/Source/Interface/Config/Config.cpp index 989938b37..7b8d8eea4 100644 --- a/External/FEXCore/Source/Interface/Config/Config.cpp +++ b/External/FEXCore/Source/Interface/Config/Config.cpp @@ -59,9 +59,9 @@ namespace FEXCore::Config { } // Ensure the folder structure is created for our configuration + std::error_code ec{}; if (!std::filesystem::exists(ConfigDir) && - !std::filesystem::create_directories(ConfigDir)) { - LogMan::Msg::D("Couldn't create config directory: '%s'", ConfigDir.c_str()); + !std::filesystem::create_directories(ConfigDir, ec)) { // Let's go local in this case return "./"; } @@ -85,9 +85,11 @@ namespace FEXCore::Config { std::string GetApplicationConfig(const std::string &Filename, bool Global) { std::string ConfigFile = GetConfigDirectory(Global); + + std::error_code ec{}; if (!Global && !std::filesystem::exists(ConfigFile) && - !std::filesystem::create_directories(ConfigFile)) { + !std::filesystem::create_directories(ConfigFile, ec)) { LogMan::Msg::D("Couldn't create config directory: '%s'", ConfigFile.c_str()); // Let's go local in this case return "./" + Filename + ".json"; @@ -98,8 +100,7 @@ namespace FEXCore::Config { // Attempt to create the local folder if it doesn't exist if (!Global && !std::filesystem::exists(ConfigFile) && - !std::filesystem::create_directories(ConfigFile)) { - LogMan::Msg::D("Couldn't create AppConfig directory: '%s'", ConfigFile.c_str()); + !std::filesystem::create_directories(ConfigFile, ec)) { // Let's go local in this case return "./" + Filename + ".json"; } diff --git a/Source/Tests/FEXLoader.cpp b/Source/Tests/FEXLoader.cpp index d8f059cc5..6e024609b 100644 --- a/Source/Tests/FEXLoader.cpp +++ b/Source/Tests/FEXLoader.cpp @@ -542,16 +542,17 @@ int main(int argc, char **argv, char **const envp) { FEXCore::Context::RunUntilExit(CTX); } - std::filesystem::create_directories(std::filesystem::path(FEXCore::Config::GetDataDirectory()) / "aotir"); - - 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); - } - }); + std::error_code ec{}; + 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()) { From 83bdbc8392c134963d457f7f5b7956b213401d28 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Mon, 2 Aug 2021 22:47:49 -0700 Subject: [PATCH 3/4] AArch64: Check if we launched with FD argument This will give us another way to check if the interpreter is installed rather than file checks. The binfmt_misc files won't exist inside of a rootfs --- Source/Tests/FEXLoader.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Source/Tests/FEXLoader.cpp b/Source/Tests/FEXLoader.cpp index 6e024609b..e7f0a4caf 100644 --- a/Source/Tests/FEXLoader.cpp +++ b/Source/Tests/FEXLoader.cpp @@ -39,6 +39,7 @@ $end_info$ namespace { static bool SilentLog; static int OutputFD {STDERR_FILENO}; +static bool ExecutedWithFD {false}; void MsgHandler(LogMan::DebugLevels Level, char const *Message) { const char *CharLevel{nullptr}; @@ -163,8 +164,10 @@ bool RanAsInterpreter(char *Program) { bool IsInterpreterInstalled() { // The interpreter is installed if both the binfmt_misc handlers are available - return std::filesystem::exists("/proc/sys/fs/binfmt_misc/FEX-x86") && - std::filesystem::exists("/proc/sys/fs/binfmt_misc/FEX-x86_64"); + // Or if we were originally executed with FD. Which means the interpreter is installed + return ExecutedWithFD || + (std::filesystem::exists("/proc/sys/fs/binfmt_misc/FEX-x86") && + std::filesystem::exists("/proc/sys/fs/binfmt_misc/FEX-x86_64")); } void AOTGenSection(FEXCore::Context::Context *CTX, ELFCodeLoader2::LoadedSection &Section) { @@ -317,6 +320,9 @@ void AOTGenSection(FEXCore::Context::Context *CTX, ELFCodeLoader2::LoadedSection int main(int argc, char **argv, char **const envp) { bool IsInterpreter = RanAsInterpreter(argv[0]); + + ExecutedWithFD = getauxval(AT_EXECFD) != 0; + LogMan::Throw::InstallHandler(AssertHandler); LogMan::Msg::InstallHandler(MsgHandler); From b586592643c444aa8020b0f303721a2fdddf2fd5 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Mon, 2 Aug 2021 23:10:23 -0700 Subject: [PATCH 4/4] Make sure our std::filesystem users use the std::error_code versions Otherwise these crash out. --- External/FEXCore/Source/Common/Paths.cpp | 2 +- .../FEXCore/Source/Interface/Config/Config.cpp | 15 +++++++++------ Source/Common/RootFSSetup.cpp | 6 ++++-- Source/Linux/Utils/ELFContainer.cpp | 5 +++-- Source/Tests/FEXLoader.cpp | 10 ++++++---- Source/Tools/FEXConfig/Main.cpp | 8 +++++--- 6 files changed, 28 insertions(+), 18 deletions(-) diff --git a/External/FEXCore/Source/Common/Paths.cpp b/External/FEXCore/Source/Common/Paths.cpp index 598bf245c..9e5125110 100644 --- a/External/FEXCore/Source/Common/Paths.cpp +++ b/External/FEXCore/Source/Common/Paths.cpp @@ -38,7 +38,7 @@ namespace FEXCore::Paths { std::error_code ec{}; // Ensure the folder structure is created for our Data - if (!std::filesystem::exists(*EntryCache) && + if (!std::filesystem::exists(*EntryCache, ec) && !std::filesystem::create_directories(*EntryCache, ec)) { LogMan::Msg::D("Couldn't create EntryCache directory: '%s'", EntryCache->c_str()); } diff --git a/External/FEXCore/Source/Interface/Config/Config.cpp b/External/FEXCore/Source/Interface/Config/Config.cpp index 7b8d8eea4..e02ff8776 100644 --- a/External/FEXCore/Source/Interface/Config/Config.cpp +++ b/External/FEXCore/Source/Interface/Config/Config.cpp @@ -60,7 +60,7 @@ namespace FEXCore::Config { // Ensure the folder structure is created for our configuration std::error_code ec{}; - if (!std::filesystem::exists(ConfigDir) && + if (!std::filesystem::exists(ConfigDir, ec) && !std::filesystem::create_directories(ConfigDir, ec)) { // Let's go local in this case return "./"; @@ -88,7 +88,7 @@ namespace FEXCore::Config { std::error_code ec{}; if (!Global && - !std::filesystem::exists(ConfigFile) && + !std::filesystem::exists(ConfigFile, ec) && !std::filesystem::create_directories(ConfigFile, ec)) { LogMan::Msg::D("Couldn't create config directory: '%s'", ConfigFile.c_str()); // Let's go local in this case @@ -99,7 +99,7 @@ namespace FEXCore::Config { // Attempt to create the local folder if it doesn't exist if (!Global && - !std::filesystem::exists(ConfigFile) && + !std::filesystem::exists(ConfigFile, ec) && !std::filesystem::create_directories(ConfigFile, ec)) { // Let's go local in this case return "./" + Filename + ".json"; @@ -274,7 +274,8 @@ namespace FEXCore::Config { Path = std::filesystem::absolute(Path); // Only return if it exists - if (std::filesystem::exists(Path)) { + std::error_code ec{}; + if (std::filesystem::exists(Path, ec)) { return Path; } } @@ -310,7 +311,8 @@ namespace FEXCore::Config { else if (!PathName().empty()) { // If the filesystem doesn't exist then let's see if it exists in the fex-emu folder std::string NamedRootFS = GetDataDirectory() + "RootFS/" + PathName(); - if (std::filesystem::exists(NamedRootFS)) { + std::error_code ec{}; + if (std::filesystem::exists(NamedRootFS, ec)) { FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_ROOTFS, NamedRootFS); } } @@ -333,7 +335,8 @@ namespace FEXCore::Config { else if (!PathName().empty()) { // If the filesystem doesn't exist then let's see if it exists in the fex-emu folder std::string NamedConfig = GetDataDirectory() + "ThunkConfigs/" + PathName(); - if (std::filesystem::exists(NamedConfig)) { + std::error_code ec{}; + if (std::filesystem::exists(NamedConfig, ec)) { FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_THUNKCONFIG, NamedConfig); } } diff --git a/Source/Common/RootFSSetup.cpp b/Source/Common/RootFSSetup.cpp index 9b4e6ce9d..0049b0aa8 100644 --- a/Source/Common/RootFSSetup.cpp +++ b/Source/Common/RootFSSetup.cpp @@ -21,7 +21,8 @@ static std::fstream SquashFSLock{}; bool SanityCheckPath(std::string const &LDPath) { // Check if we have an directory inside our temp folder std::string PathUser = LDPath + "/usr"; - if (!std::filesystem::exists(PathUser)) { + std::error_code ec{}; + if (!std::filesystem::exists(PathUser, ec)) { LogMan::Msg::D("Child couldn't mount rootfs, /usr doesn't exist"); rmdir(LDPath.c_str()); return false; @@ -33,7 +34,8 @@ bool SanityCheckPath(std::string const &LDPath) { bool CheckLockExists(std::string const LockPath) { // If the lock file for a squashfs path exists the we can try // to open it and ref counting will keep it alive - if (std::filesystem::exists(LockPath)) { + std::error_code ec{}; + if (std::filesystem::exists(LockPath, ec)) { SquashFSLock.open(LockPath, std::ios_base::in | std::ios_base::binary); if (SquashFSLock.is_open()) { // We managed to open the file. Which means the mount application has now refcounted our interaction with it diff --git a/Source/Linux/Utils/ELFContainer.cpp b/Source/Linux/Utils/ELFContainer.cpp index aa9330b7b..f57b1cfc0 100644 --- a/Source/Linux/Utils/ELFContainer.cpp +++ b/Source/Linux/Utils/ELFContainer.cpp @@ -95,11 +95,12 @@ ELFContainer::ELFContainer(std::string const &Filename, std::string const &RootF RawString = &RawFile.at(InterpreterHeader._64->p_offset); } std::string RootFSLink = RootFS + RawString; - while (std::filesystem::is_symlink(RootFSLink)) { + std::error_code ec{}; + while (std::filesystem::is_symlink(RootFSLink, ec)) { // Do some special handling if the RootFS's linker is a symlink // Ubuntu's rootFS by default provides an absolute location symlink to the linker // Resolve this around back to the rootfs - auto SymlinkTarget = std::filesystem::read_symlink(RootFSLink); + auto SymlinkTarget = std::filesystem::read_symlink(RootFSLink, ec); if (SymlinkTarget.is_absolute()) { RootFSLink = RootFS + SymlinkTarget.string(); } diff --git a/Source/Tests/FEXLoader.cpp b/Source/Tests/FEXLoader.cpp index e7f0a4caf..3d457b5b3 100644 --- a/Source/Tests/FEXLoader.cpp +++ b/Source/Tests/FEXLoader.cpp @@ -165,9 +165,11 @@ bool RanAsInterpreter(char *Program) { 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") && - std::filesystem::exists("/proc/sys/fs/binfmt_misc/FEX-x86_64")); + (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) { @@ -409,7 +411,8 @@ int main(int argc, char **argv, char **const envp) { InterpreterHandler(&Program, LDPath(), &Args); - if (!std::filesystem::exists(Program)) { + 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 fprintf(stderr, "%s: command not found\n", Program.c_str()); @@ -548,7 +551,6 @@ int main(int argc, char **argv, char **const envp) { FEXCore::Context::RunUntilExit(CTX); } - std::error_code ec{}; 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"); diff --git a/Source/Tools/FEXConfig/Main.cpp b/Source/Tools/FEXConfig/Main.cpp index 7d53448a2..29142985c 100644 --- a/Source/Tools/FEXConfig/Main.cpp +++ b/Source/Tools/FEXConfig/Main.cpp @@ -77,7 +77,8 @@ namespace { } bool OpenFile(std::string Filename, bool LoadDefault = false) { - if (!std::filesystem::exists(Filename)) { + std::error_code ec{}; + if (!std::filesystem::exists(Filename, ec)) { if (LoadDefault) { LoadDefaultSettings(); ConfigFilename = Filename; @@ -98,9 +99,10 @@ namespace { std::scoped_lock lk{NamedRootFSUpdator}; NamedRootFS.clear(); std::string RootFS = FEXCore::Config::GetDataDirectory() + "RootFS/"; - if (!std::filesystem::exists(RootFS)) { + std::error_code ec{}; + if (!std::filesystem::exists(RootFS, ec)) { // Doesn't exist, create the the folder as a user convenience - if (!std::filesystem::create_directories(RootFS)) { + if (!std::filesystem::create_directories(RootFS, ec)) { // Well I guess we failed return; }