diff --git a/.gitmodules b/.gitmodules index 35c6317dc..c059448c6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -49,3 +49,7 @@ [submodule "External/range-v3"] path = External/range-v3 url = https://github.com/ericniebler/range-v3.git +[submodule "External/zydis"] + shallow = true + path = External/zydis + url = https://github.com/zyantific/zydis.git diff --git a/CMakeLists.txt b/CMakeLists.txt index da898b096..b786ef19f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ option(ENABLE_LIBCXX "Use LLVM's libc++ instead of the GNU libstdc++" FALSE) option(ENABLE_CCACHE "Enable ccache for build caching" TRUE) option(ENABLE_VIXL_SIMULATOR "Use the VIXL simulator for emulation (only useful for CI testing)" FALSE) option(ENABLE_VIXL_DISASSEMBLER "Enable debug disassembler output with VIXL" FALSE) +option(ENABLE_ZYDIS "Enable x86/x86-64 guest disassembler output with Zydis" FALSE) option(USE_LEGACY_BINFMTMISC "Use legacy method of setting up binfmt_misc" FALSE) option(ENABLE_FEXCORE_PROFILER "Enable FEXCore's timeline profiling capabilities" FALSE) set(FEXCORE_PROFILER_BACKEND "gpuvis" CACHE STRING "Set which backend to use for FEXCore's profiler") @@ -344,11 +345,27 @@ if (BUILD_TESTING OR ENABLE_VIXL_DISASSEMBLER OR ENABLE_VIXL_SIMULATOR) include_directories(SYSTEM External/vixl/src/) endif() +find_package(PkgConfig REQUIRED) + +if (ENABLE_ZYDIS) + pkg_search_module(Zydis QUIET IMPORTED_TARGET zydis) + pkg_search_module(Zycore QUIET IMPORTED_TARGET zycore) + if (TARGET PkgConfig::Zydis AND TARGET PkgConfig::Zycore AND NOT CMAKE_CROSSCOMPILING) + add_library(Zydis::Zydis ALIAS PkgConfig::Zydis) + add_library(Zycore::Zycore ALIAS PkgConfig::Zycore) + message(STATUS "Using system Zydis") + else() + message(STATUS "Using bundled Zydis") + set(ZYDIS_BUILD_TOOLS OFF CACHE BOOL "" FORCE) + set(ZYDIS_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) + add_subdirectory(External/zydis/) + endif() +endif() + if (ENABLE_FEXCORE_PROFILER AND FEXCORE_PROFILER_BACKEND STREQUAL "TRACY") add_subdirectory(External/tracy) endif() -find_package(PkgConfig REQUIRED) find_package(Python 3.9 REQUIRED COMPONENTS Interpreter) set(BUILD_SHARED_LIBS OFF) diff --git a/External/zydis b/External/zydis new file mode 160000 index 000000000..9bfadd6a5 --- /dev/null +++ b/External/zydis @@ -0,0 +1 @@ +Subproject commit 9bfadd6a55fc92dbd37fa3ba089bf8b36622df4f diff --git a/FEXCore/Source/CMakeLists.txt b/FEXCore/Source/CMakeLists.txt index d1bc94211..bb27f62fb 100644 --- a/FEXCore/Source/CMakeLists.txt +++ b/FEXCore/Source/CMakeLists.txt @@ -94,6 +94,10 @@ if (ENABLE_VIXL_DISASSEMBLER) list(APPEND DEFINES -DVIXL_DISASSEMBLER=1) endif() +if (ENABLE_ZYDIS) + list(APPEND DEFINES -DZYDIS_DISASSEMBLER=1) +endif() + if (ARCHITECTURE_arm64 AND HAS_CLANG_PRESERVE_ALL) list(APPEND DEFINES "-DFEXCORE_PRESERVE_ALL_ATTR=__attribute__((preserve_all));-DFEXCORE_HAS_PRESERVE_ALL_ATTR=1") else() @@ -106,6 +110,10 @@ if (ENABLE_VIXL_DISASSEMBLER OR ENABLE_VIXL_SIMULATOR) list(APPEND LIBS vixl) endif() +if (ENABLE_ZYDIS) + list(APPEND LIBS Zydis::Zydis) +endif() + if (NOT MINGW) list(APPEND LIBS dl) else() diff --git a/FEXCore/Source/Interface/Config/Config.json.in b/FEXCore/Source/Interface/Config/Config.json.in index 273479cec..5ea2bc7b7 100644 --- a/FEXCore/Source/Interface/Config/Config.json.in +++ b/FEXCore/Source/Interface/Config/Config.json.in @@ -334,13 +334,21 @@ "STATS": "stats" }, "Desc": [ - "Allows controlling of the vixl disassembler.", + "Allows controlling of the vixl disassembler for generated ARM code.", "\toff: No disassembly will be output", "\tdispatcher: Will enable disassembly of the JIT dispatcher loop", "\tblocks: Will enable disassembly of the translated instruction code blocks", "\tstats: Will print stats when disassembling the code" ] }, + "X86Disassemble": { + "Type": "bool", + "Default": "false", + "Desc": [ + "Enables x86/x86-64 guest disassembly output for compiled blocks.", + "Requires FEX to be built with -DENABLE_ZYDIS=TRUE" + ] + }, "ForceSVEWidth": { "Type": "uint32", "Default": "0", diff --git a/FEXCore/Source/Interface/Core/Core.cpp b/FEXCore/Source/Interface/Core/Core.cpp index 012470665..9aaa7595e 100644 --- a/FEXCore/Source/Interface/Core/Core.cpp +++ b/FEXCore/Source/Interface/Core/Core.cpp @@ -9,6 +9,9 @@ $end_info$ */ #include +#ifdef ZYDIS_DISASSEMBLER +#include +#endif #include "Interface/Core/ArchHelpers/Arm64Emitter.h" #include "Interface/Core/LookupCache.h" #include "Interface/Core/CPUBackend.h" @@ -539,9 +542,24 @@ ContextImpl::GenerateIR(FEXCore::Core::InternalThreadState* Thread, uint64_t Gue const auto GPRSize = Thread->OpDispatcher->GetGPROpSize(); +#ifdef ZYDIS_DISASSEMBLER + const auto ZydisMachineMode = Config.Is64BitMode ? ZYDIS_MACHINE_MODE_LONG_64 : ZYDIS_MACHINE_MODE_LEGACY_32; + if (FEXCore::Config::Get_X86DISASSEMBLE()) { + const uint64_t DecodedMin = Thread->FrontendDecoder->DecodedMinAddress; + const uint64_t DecodedMax = Thread->FrontendDecoder->DecodedMaxAddress; + LogMan::Msg::IFmt("Guest x86 Begin (RIP={:#x}, {:#x}-{:#x})", GuestRIP, DecodedMin, DecodedMax); + } +#endif + for (size_t j = 0; j < CodeBlocks->size(); ++j) { const FEXCore::Frontend::Decoder::DecodedBlocks& Block = CodeBlocks->at(j); +#ifdef ZYDIS_DISASSEMBLER + if (FEXCore::Config::Get_X86DISASSEMBLE() && CodeBlocks->size() > 1) { + LogMan::Msg::IFmt(" Block {} Entry={:#x} NumInsts={}", j, Block.Entry, Block.NumInstructions); + } +#endif + bool BlockInForceTSOValidRange = false; auto InstForceTSOIt = ForceTSOInstructions.end(); if (ForceTSOValidRanges.Contains({Block.Entry, Block.Entry + Block.Size})) { @@ -573,6 +591,19 @@ ContextImpl::GenerateIR(FEXCore::Core::InternalThreadState* Thread, uint64_t Gue TableInfo = Block.DecodedInstructions[i].TableInfo; DecodedInfo = &Block.DecodedInstructions[i]; + +#ifdef ZYDIS_DISASSEMBLER + if (FEXCore::Config::Get_X86DISASSEMBLE()) { + const uint8_t* InstBytes = reinterpret_cast(InstAddress); + ZydisDisassembledInstruction ZydisInst; + if (ZYAN_SUCCESS(ZydisDisassembleIntel(ZydisMachineMode, InstAddress, InstBytes, DecodedInfo->InstSize, &ZydisInst))) { + LogMan::Msg::IFmt(" {:#x}: {}", InstAddress, ZydisInst.text); + } else { + LogMan::Msg::IFmt(" {:#x}: (decode failed, {} bytes)", InstAddress, DecodedInfo->InstSize); + } + } +#endif + bool IsLocked = DecodedInfo->Flags & FEXCore::X86Tables::DecodeFlags::FLAG_LOCK; // Do a partial register cache flush before every instruction. This @@ -693,6 +724,12 @@ ContextImpl::GenerateIR(FEXCore::Core::InternalThreadState* Thread, uint64_t Gue } } +#ifdef ZYDIS_DISASSEMBLER + if (FEXCore::Config::Get_X86DISASSEMBLE()) { + LogMan::Msg::IFmt("Guest x86 End"); + } +#endif + Thread->OpDispatcher->Finalize(); Thread->FrontendDecoder->DelayedDisownBuffer();