From c4e4b6112adaecec3162f69110b0883503d91bc2 Mon Sep 17 00:00:00 2001 From: boykopovar Date: Wed, 5 Aug 2026 13:18:09 +0300 Subject: [PATCH] feat(code): add spec amd instructions to intel convert infrastructure --- core/CMakeLists.txt | 2 + .../x86/Amd64OnlySubstitutionTable.hpp | 31 +++ .../x86/Amd64OnlySubstitutionTypes.hpp | 26 ++ .../codegen/x86/DecodedInstruction.hpp | 30 +++ .../x86/IAmd64OnlyInstructionMatcher.hpp | 25 ++ .../src/x86/Amd64OnlyInstructionMatcher.cpp | 58 ++++ core/codegen/src/x86/DecodedInstruction.cpp | 248 ++++++++++++++++++ 7 files changed, 420 insertions(+) create mode 100644 core/codegen/include/codegen/x86/Amd64OnlySubstitutionTable.hpp create mode 100644 core/codegen/include/codegen/x86/Amd64OnlySubstitutionTypes.hpp create mode 100644 core/codegen/include/codegen/x86/DecodedInstruction.hpp create mode 100644 core/codegen/include/codegen/x86/IAmd64OnlyInstructionMatcher.hpp create mode 100644 core/codegen/src/x86/Amd64OnlyInstructionMatcher.cpp create mode 100644 core/codegen/src/x86/DecodedInstruction.cpp diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index a73e1d5bc..d6663c6c8 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -12,6 +12,8 @@ add_executable(relinker ${CMAKE_CURRENT_SOURCE_DIR}/codegen/src/InstructionScanner.cpp ${CMAKE_CURRENT_SOURCE_DIR}/codegen/src/x86/X64InstructionDecoder.cpp ${CMAKE_CURRENT_SOURCE_DIR}/codegen/src/x86/X64InstructionRewriter.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/codegen/src/x86/DecodedInstruction.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/codegen/src/x86/Amd64OnlyInstructionMatcher.cpp ${CMAKE_CURRENT_SOURCE_DIR}/relinker/src/parsing/ElfReader.cpp ${CMAKE_CURRENT_SOURCE_DIR}/relinker/src/analysis/SyscallScanner.cpp ${CMAKE_CURRENT_SOURCE_DIR}/relinker/src/analysis/CallSiteResolver.cpp diff --git a/core/codegen/include/codegen/x86/Amd64OnlySubstitutionTable.hpp b/core/codegen/include/codegen/x86/Amd64OnlySubstitutionTable.hpp new file mode 100644 index 000000000..2881b934d --- /dev/null +++ b/core/codegen/include/codegen/x86/Amd64OnlySubstitutionTable.hpp @@ -0,0 +1,31 @@ +#ifndef CODEGEN_X86_AMD64ONLYSUBSTITUTIONTABLE_HPP +#define CODEGEN_X86_AMD64ONLYSUBSTITUTIONTABLE_HPP + +#include +#include + +namespace Codegen { +namespace Amd64OnlySubstitutionTable { + +struct Entry { + const char* Name; + const std::uint8_t* Bytes; + std::size_t Size; +}; + +#define AMD64_STUB(name, ...) \ + inline constexpr std::uint8_t k##name##Bytes[] = {__VA_ARGS__}; \ + inline constexpr Entry k##name = {#name, k##name##Bytes, sizeof(k##name##Bytes)}; + +AMD64_STUB(Monitorx, 0x0F, 0x01, 0xFA) +AMD64_STUB(Mwaitx, 0x0F, 0x01, 0xFB) +AMD64_STUB(Clzero, 0x0F, 0x01, 0xFC) +AMD64_STUB(Rdpru, 0x0F, 0x01, 0xFD) +AMD64_STUB(Mcommit, 0xF3, 0x0F, 0x01, 0xFA) + +#undef AMD64_STUB + +} // namespace Amd64OnlySubstitutionTable +} // namespace Codegen + +#endif diff --git a/core/codegen/include/codegen/x86/Amd64OnlySubstitutionTypes.hpp b/core/codegen/include/codegen/x86/Amd64OnlySubstitutionTypes.hpp new file mode 100644 index 000000000..aa062cc8e --- /dev/null +++ b/core/codegen/include/codegen/x86/Amd64OnlySubstitutionTypes.hpp @@ -0,0 +1,26 @@ +#ifndef CODEGEN_X86_AMD64ONLYSUBSTITUTIONTYPES_HPP +#define CODEGEN_X86_AMD64ONLYSUBSTITUTIONTYPES_HPP + +#include +#include +#include +#include + +namespace Codegen { + +struct Amd64OnlyMatch { + std::string InstructionName; + std::size_t Length; + std::vector ReplacementBytes; +}; + +struct Amd64OnlySubstitutionReport { + std::string InstructionName; + Domain::FileByteOffset Offset; + std::size_t OriginalLength; + std::size_t ReplacementLength; +}; + +} + +#endif diff --git a/core/codegen/include/codegen/x86/DecodedInstruction.hpp b/core/codegen/include/codegen/x86/DecodedInstruction.hpp new file mode 100644 index 000000000..ac42a23d2 --- /dev/null +++ b/core/codegen/include/codegen/x86/DecodedInstruction.hpp @@ -0,0 +1,30 @@ +#ifndef CODEGEN_X86_DECODEDINSTRUCTION_HPP +#define CODEGEN_X86_DECODEDINSTRUCTION_HPP + +#include +#include + +namespace Codegen { + +struct DecodedInstruction { + const std::uint8_t* Data; + std::size_t Length; + + [[nodiscard]] bool IsShaNi() const; + [[nodiscard]] bool IsExtrq() const; + [[nodiscard]] bool IsInsertq() const; + [[nodiscard]] bool IsMonitorx() const; + [[nodiscard]] bool IsMwaitx() const; + [[nodiscard]] bool IsClzero() const; + [[nodiscard]] bool IsRdpru() const; + [[nodiscard]] bool IsMcommit() const; + [[nodiscard]] bool IsMovntss() const; + [[nodiscard]] bool IsMovntsd() const; + +private: + [[nodiscard]] std::size_t _skipPrefixesAndRex(bool* outHasOperandSizePrefix, bool* outHasRepnePrefix, bool* outHasRepPrefix) const; +}; + +} + +#endif diff --git a/core/codegen/include/codegen/x86/IAmd64OnlyInstructionMatcher.hpp b/core/codegen/include/codegen/x86/IAmd64OnlyInstructionMatcher.hpp new file mode 100644 index 000000000..d001f34ae --- /dev/null +++ b/core/codegen/include/codegen/x86/IAmd64OnlyInstructionMatcher.hpp @@ -0,0 +1,25 @@ +#ifndef CODEGEN_X86_IAMD64ONLYINSTRUCTIONMATCHER_HPP +#define CODEGEN_X86_IAMD64ONLYINSTRUCTIONMATCHER_HPP + +#include +#include +#include +#include + +namespace Codegen { + +class IAmd64OnlyInstructionMatcher { +public: + virtual ~IAmd64OnlyInstructionMatcher() = default; + + [[nodiscard]] virtual std::optional Match( + const std::uint8_t* data, + std::size_t length + ) const = 0; +}; + +std::unique_ptr MakeAmd64OnlyInstructionMatcher(); + +} + +#endif diff --git a/core/codegen/src/x86/Amd64OnlyInstructionMatcher.cpp b/core/codegen/src/x86/Amd64OnlyInstructionMatcher.cpp new file mode 100644 index 000000000..70ecb7e3d --- /dev/null +++ b/core/codegen/src/x86/Amd64OnlyInstructionMatcher.cpp @@ -0,0 +1,58 @@ +#include +#include +#include +#include + +namespace Codegen { + +namespace { + +std::vector _bytesOf(const Amd64OnlySubstitutionTable::Entry& e) { + return std::vector(e.Bytes, e.Bytes + e.Size); +} + +class Amd64OnlyInstructionMatcher : public IAmd64OnlyInstructionMatcher { +public: + [[nodiscard]] std::optional Match( + const std::uint8_t* data, + std::size_t length + ) const override; +}; + +std::optional Amd64OnlyInstructionMatcher::Match( + const std::uint8_t* data, + std::size_t length +) const { + using namespace Amd64OnlySubstitutionTable; + const DecodedInstruction instr{data, length}; + + if (instr.IsMonitorx()) { + return Amd64OnlyMatch{kMonitorx.Name, length, _bytesOf(kMonitorx)}; + } + + if (instr.IsMwaitx()) { + return Amd64OnlyMatch{kMwaitx.Name, length, _bytesOf(kMwaitx)}; + } + + if (instr.IsClzero()) { + return Amd64OnlyMatch{kClzero.Name, length, _bytesOf(kClzero)}; + } + + if (instr.IsRdpru()) { + return Amd64OnlyMatch{kRdpru.Name, length, _bytesOf(kRdpru)}; + } + + if (instr.IsMcommit()) { + return Amd64OnlyMatch{kMcommit.Name, length, _bytesOf(kMcommit)}; + } + + return std::nullopt; +} + +} + +std::unique_ptr MakeAmd64OnlyInstructionMatcher() { + return std::make_unique(); +} + +} diff --git a/core/codegen/src/x86/DecodedInstruction.cpp b/core/codegen/src/x86/DecodedInstruction.cpp new file mode 100644 index 000000000..3ee2a1285 --- /dev/null +++ b/core/codegen/src/x86/DecodedInstruction.cpp @@ -0,0 +1,248 @@ +#include +#include + +namespace Codegen { + +using namespace X64OpcodeConstants; + +std::size_t DecodedInstruction::_skipPrefixesAndRex( + bool* outHasOperandSizePrefix, + bool* outHasRepnePrefix, + bool* outHasRepPrefix +) const { + std::size_t pos = 0; + *outHasOperandSizePrefix = false; + *outHasRepnePrefix = false; + *outHasRepPrefix = false; + + while (pos < Length) { + const std::uint8_t b = Data[pos]; + + if (b == PrefixRepne) { + *outHasRepnePrefix = true; + pos += 1; + continue; + } + + if (b == PrefixRep) { + *outHasRepPrefix = true; + pos += 1; + continue; + } + + if (b == PrefixLock || + b == PrefixSegCs || b == PrefixSegSs || b == PrefixSegDs || b == PrefixSegEs || + b == PrefixSegFs || b == PrefixSegGs) { + pos += 1; + continue; + } + + if (b == PrefixOperandSize) { + *outHasOperandSizePrefix = true; + pos += 1; + continue; + } + + if (b == PrefixAddressSize) { + pos += 1; + continue; + } + + break; + } + + if (pos < Length && Data[pos] >= RexMin && Data[pos] <= RexMax) { + pos += 1; + } + + return pos; +} + +bool DecodedInstruction::IsShaNi() const { + bool operandSizeOverride = false; + bool repnePrefix = false; + bool repPrefix = false; + const std::size_t pos = _skipPrefixesAndRex(&operandSizeOverride, &repnePrefix, &repPrefix); + + if (operandSizeOverride || repnePrefix || repPrefix) { + return false; + } + + if (pos + 2 >= Length) { + return false; + } + + if (Data[pos] != TwoByteOpcodeEscape) { + return false; + } + + if (Data[pos + 1] == ThreeByteEscape38) { + const std::uint8_t opcode = Data[pos + 2]; + return opcode == 0xC8 || opcode == 0xC9 || opcode == 0xCA || + opcode == 0xCC || opcode == 0xCD; + } + + if (Data[pos + 1] == ThreeByteEscape3A) { + const std::uint8_t opcode = Data[pos + 2]; + return opcode == 0xCC; + } + + return false; +} + +bool DecodedInstruction::IsExtrq() const { + bool operandSizeOverride = false; + bool repnePrefix = false; + bool repPrefix = false; + const std::size_t pos = _skipPrefixesAndRex(&operandSizeOverride, &repnePrefix, &repPrefix); + + if (!operandSizeOverride || repnePrefix || repPrefix) { + return false; + } + + if (pos + 1 >= Length) { + return false; + } + + return Data[pos] == TwoByteOpcodeEscape && + (Data[pos + 1] == TwoByteExtrqInsertqImm8Imm8 || Data[pos + 1] == TwoByteExtrqInsertqModRm); +} + +bool DecodedInstruction::IsInsertq() const { + bool operandSizeOverride = false; + bool repnePrefix = false; + bool repPrefix = false; + const std::size_t pos = _skipPrefixesAndRex(&operandSizeOverride, &repnePrefix, &repPrefix); + + if (operandSizeOverride || !repnePrefix || repPrefix) { + return false; + } + + if (pos + 1 >= Length) { + return false; + } + + return Data[pos] == TwoByteOpcodeEscape && + (Data[pos + 1] == TwoByteExtrqInsertqImm8Imm8 || Data[pos + 1] == TwoByteExtrqInsertqModRm); +} + +bool DecodedInstruction::IsMonitorx() const { + bool operandSizeOverride = false; + bool repnePrefix = false; + bool repPrefix = false; + const std::size_t pos = _skipPrefixesAndRex(&operandSizeOverride, &repnePrefix, &repPrefix); + + if (operandSizeOverride || repnePrefix || repPrefix) { + return false; + } + + if (pos + 2 >= Length) { + return false; + } + + return Data[pos] == TwoByteOpcodeEscape && Data[pos + 1] == TwoByteGrp7 && Data[pos + 2] == 0xFA; +} + +bool DecodedInstruction::IsMwaitx() const { + bool operandSizeOverride = false; + bool repnePrefix = false; + bool repPrefix = false; + const std::size_t pos = _skipPrefixesAndRex(&operandSizeOverride, &repnePrefix, &repPrefix); + + if (operandSizeOverride || repnePrefix || repPrefix) { + return false; + } + + if (pos + 2 >= Length) { + return false; + } + + return Data[pos] == TwoByteOpcodeEscape && Data[pos + 1] == TwoByteGrp7 && Data[pos + 2] == 0xFB; +} + +bool DecodedInstruction::IsClzero() const { + bool operandSizeOverride = false; + bool repnePrefix = false; + bool repPrefix = false; + const std::size_t pos = _skipPrefixesAndRex(&operandSizeOverride, &repnePrefix, &repPrefix); + + if (operandSizeOverride || repnePrefix || repPrefix) { + return false; + } + + if (pos + 2 >= Length) { + return false; + } + + return Data[pos] == TwoByteOpcodeEscape && Data[pos + 1] == TwoByteGrp7 && Data[pos + 2] == 0xFC; +} + +bool DecodedInstruction::IsRdpru() const { + bool operandSizeOverride = false; + bool repnePrefix = false; + bool repPrefix = false; + const std::size_t pos = _skipPrefixesAndRex(&operandSizeOverride, &repnePrefix, &repPrefix); + + if (operandSizeOverride || repnePrefix || repPrefix) { + return false; + } + + if (pos + 2 >= Length) { + return false; + } + + return Data[pos] == TwoByteOpcodeEscape && Data[pos + 1] == TwoByteGrp7 && Data[pos + 2] == 0xFD; +} + +bool DecodedInstruction::IsMcommit() const { + bool operandSizeOverride = false; + bool repnePrefix = false; + bool repPrefix = false; + const std::size_t pos = _skipPrefixesAndRex(&operandSizeOverride, &repnePrefix, &repPrefix); + + if (operandSizeOverride || repnePrefix || !repPrefix) { + return false; + } + + if (pos + 2 >= Length) { + return false; + } + + return Data[pos] == TwoByteOpcodeEscape && Data[pos + 1] == TwoByteGrp7 && Data[pos + 2] == 0xFA; +} + +bool DecodedInstruction::IsMovntss() const { + bool operandSizeOverride = false; + bool repnePrefix = false; + bool repPrefix = false; + const std::size_t pos = _skipPrefixesAndRex(&operandSizeOverride, &repnePrefix, &repPrefix); + + if (operandSizeOverride || repnePrefix || !repPrefix) { + return false; + } + + if (pos + 1 >= Length) { + return false; + } + + return Data[pos] == TwoByteOpcodeEscape && Data[pos + 1] == 0x2B; +} + +bool DecodedInstruction::IsMovntsd() const { + bool operandSizeOverride = false; + bool repnePrefix = false; + bool repPrefix = false; + const std::size_t pos = _skipPrefixesAndRex(&operandSizeOverride, &repnePrefix, &repPrefix); + + if (operandSizeOverride || !repnePrefix || repPrefix) { + return false; + } + + if (pos + 1 >= Length) { + return false; + } + + return Data[pos] == TwoByteOpcodeEscape && Data[pos + 1] == 0x2B; +} + +}