diff --git a/.gitmodules b/.gitmodules index 35c6317dc8..c059448c6f 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 6c2e4bddb3..f109824281 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ option(ENABLE_LIBCXX "Enables LLVM libc++" FALSE) option(ENABLE_CCACHE "Enables ccache for compile caching" TRUE) option(ENABLE_VIXL_SIMULATOR "Enable use of VIXL simulator for emulation (only useful for CI testing)" FALSE) option(ENABLE_VIXL_DISASSEMBLER "Enables debug disassembler output with VIXL" FALSE) +option(ENABLE_ZYDIS "Enables x86/x86-64 guest disassembler output with Zydis" FALSE) option(USE_LEGACY_BINFMTMISC "Uses legacy method of setting up binfmt_misc" FALSE) option(ENABLE_FEXCORE_PROFILER "Enables use of the FEXCore timeline profiling capabilities" FALSE) set (FEXCORE_PROFILER_BACKEND "gpuvis" CACHE STRING "Set which backend to use for the FEXCore profiler (gpuvis, tracy)") @@ -315,6 +316,18 @@ if (BUILD_TESTING OR ENABLE_VIXL_DISASSEMBLER OR ENABLE_VIXL_SIMULATOR) include_directories(SYSTEM External/vixl/src/) endif() +if (ENABLE_ZYDIS) + find_package(Zydis QUIET) + if (Zydis_FOUND) + 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() diff --git a/External/zydis b/External/zydis new file mode 160000 index 0000000000..9bfadd6a55 --- /dev/null +++ b/External/zydis @@ -0,0 +1 @@ +Subproject commit 9bfadd6a55fc92dbd37fa3ba089bf8b36622df4f diff --git a/FEXCore/Source/CMakeLists.txt b/FEXCore/Source/CMakeLists.txt index de1de9bdec..f5cfa73f53 100644 --- a/FEXCore/Source/CMakeLists.txt +++ b/FEXCore/Source/CMakeLists.txt @@ -96,6 +96,10 @@ if (ENABLE_VIXL_DISASSEMBLER) list(APPEND DEFINES -DVIXL_DISASSEMBLER=1) endif() +if (ENABLE_ZYDIS) + list(APPEND DEFINES -DZYDIS_DISASSEMBLER=1) +endif() + if (_M_ARM_64 AND HAS_CLANG_PRESERVE_ALL) list(APPEND DEFINES "-DFEXCORE_PRESERVE_ALL_ATTR=__attribute__((preserve_all));-DFEXCORE_HAS_PRESERVE_ALL_ATTR=1") else() @@ -108,6 +112,10 @@ if (ENABLE_VIXL_DISASSEMBLER OR ENABLE_VIXL_SIMULATOR) list (APPEND LIBS vixl) endif() +if (ENABLE_ZYDIS) + list (APPEND LIBS Zydis) +endif() + if (NOT MINGW_BUILD) list (APPEND LIBS dl) else() diff --git a/FEXCore/Source/Interface/Config/Config.json.in b/FEXCore/Source/Interface/Config/Config.json.in index 710d52b392..94d2693572 100644 --- a/FEXCore/Source/Interface/Config/Config.json.in +++ b/FEXCore/Source/Interface/Config/Config.json.in @@ -334,6 +334,14 @@ "\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 fa929b7b7e..f5132a4415 100644 --- a/FEXCore/Source/Interface/Core/Core.cpp +++ b/FEXCore/Source/Interface/Core/Core.cpp @@ -9,6 +9,9 @@ desc: Glues Frontend, OpDispatcher and IR Opts & Compilation, LookupCache, Dispa */ #include +#ifdef ZYDIS_DISASSEMBLER +#include +#endif #include "Interface/Core/ArchHelpers/Arm64Emitter.h" #include "Interface/Core/LookupCache.h" #include "Interface/Core/CPUBackend.h" @@ -536,9 +539,25 @@ ContextImpl::GenerateIR(FEXCore::Core::InternalThreadState* Thread, uint64_t Gue const auto GPRSize = Thread->OpDispatcher->GetGPROpSize(); +#ifdef ZYDIS_DISASSEMBLER + FEX_CONFIG_OPT(X86Disassemble, X86DISASSEMBLE); + const auto ZydisMachineMode = Config.Is64BitMode ? ZYDIS_MACHINE_MODE_LONG_64 : ZYDIS_MACHINE_MODE_LEGACY_32; + if (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 (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})) { @@ -570,6 +589,19 @@ ContextImpl::GenerateIR(FEXCore::Core::InternalThreadState* Thread, uint64_t Gue TableInfo = Block.DecodedInstructions[i].TableInfo; DecodedInfo = &Block.DecodedInstructions[i]; + +#ifdef ZYDIS_DISASSEMBLER + if (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 @@ -689,6 +721,12 @@ ContextImpl::GenerateIR(FEXCore::Core::InternalThreadState* Thread, uint64_t Gue } } +#ifdef ZYDIS_DISASSEMBLER + if (X86Disassemble()) { + LogMan::Msg::IFmt("Guest x86 End"); + } +#endif + Thread->OpDispatcher->Finalize(); Thread->FrontendDecoder->DelayedDisownBuffer();