feat(code): add spec amd instructions to intel convert infrastructure

This commit is contained in:
boykopovar 2026-08-05 13:18:09 +03:00
parent fca8b8d7eb
commit c4e4b6112a
7 changed files with 420 additions and 0 deletions

View File

@ -12,6 +12,8 @@ add_executable(relinker
${CMAKE_CURRENT_SOURCE_DIR}/codegen/src/InstructionScanner.cpp ${CMAKE_CURRENT_SOURCE_DIR}/codegen/src/InstructionScanner.cpp
${CMAKE_CURRENT_SOURCE_DIR}/codegen/src/x86/X64InstructionDecoder.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/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/parsing/ElfReader.cpp
${CMAKE_CURRENT_SOURCE_DIR}/relinker/src/analysis/SyscallScanner.cpp ${CMAKE_CURRENT_SOURCE_DIR}/relinker/src/analysis/SyscallScanner.cpp
${CMAKE_CURRENT_SOURCE_DIR}/relinker/src/analysis/CallSiteResolver.cpp ${CMAKE_CURRENT_SOURCE_DIR}/relinker/src/analysis/CallSiteResolver.cpp

View File

@ -0,0 +1,31 @@
#ifndef CODEGEN_X86_AMD64ONLYSUBSTITUTIONTABLE_HPP
#define CODEGEN_X86_AMD64ONLYSUBSTITUTIONTABLE_HPP
#include <cstdint>
#include <cstddef>
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

View File

@ -0,0 +1,26 @@
#ifndef CODEGEN_X86_AMD64ONLYSUBSTITUTIONTYPES_HPP
#define CODEGEN_X86_AMD64ONLYSUBSTITUTIONTYPES_HPP
#include <domain/Types.hpp>
#include <cstdint>
#include <string>
#include <vector>
namespace Codegen {
struct Amd64OnlyMatch {
std::string InstructionName;
std::size_t Length;
std::vector<std::uint8_t> ReplacementBytes;
};
struct Amd64OnlySubstitutionReport {
std::string InstructionName;
Domain::FileByteOffset Offset;
std::size_t OriginalLength;
std::size_t ReplacementLength;
};
}
#endif

View File

@ -0,0 +1,30 @@
#ifndef CODEGEN_X86_DECODEDINSTRUCTION_HPP
#define CODEGEN_X86_DECODEDINSTRUCTION_HPP
#include <cstdint>
#include <cstddef>
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

View File

@ -0,0 +1,25 @@
#ifndef CODEGEN_X86_IAMD64ONLYINSTRUCTIONMATCHER_HPP
#define CODEGEN_X86_IAMD64ONLYINSTRUCTIONMATCHER_HPP
#include <codegen/x86/Amd64OnlySubstitutionTypes.hpp>
#include <cstdint>
#include <memory>
#include <optional>
namespace Codegen {
class IAmd64OnlyInstructionMatcher {
public:
virtual ~IAmd64OnlyInstructionMatcher() = default;
[[nodiscard]] virtual std::optional<Amd64OnlyMatch> Match(
const std::uint8_t* data,
std::size_t length
) const = 0;
};
std::unique_ptr<IAmd64OnlyInstructionMatcher> MakeAmd64OnlyInstructionMatcher();
}
#endif

View File

@ -0,0 +1,58 @@
#include <codegen/x86/IAmd64OnlyInstructionMatcher.hpp>
#include <codegen/x86/DecodedInstruction.hpp>
#include <codegen/x86/Amd64OnlySubstitutionTable.hpp>
#include <memory>
namespace Codegen {
namespace {
std::vector<std::uint8_t> _bytesOf(const Amd64OnlySubstitutionTable::Entry& e) {
return std::vector<std::uint8_t>(e.Bytes, e.Bytes + e.Size);
}
class Amd64OnlyInstructionMatcher : public IAmd64OnlyInstructionMatcher {
public:
[[nodiscard]] std::optional<Amd64OnlyMatch> Match(
const std::uint8_t* data,
std::size_t length
) const override;
};
std::optional<Amd64OnlyMatch> 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<IAmd64OnlyInstructionMatcher> MakeAmd64OnlyInstructionMatcher() {
return std::make_unique<Amd64OnlyInstructionMatcher>();
}
}

View File

@ -0,0 +1,248 @@
#include <codegen/x86/DecodedInstruction.hpp>
#include <codegen/x86/X64OpcodeConstants.hpp>
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;
}
}