feat(code): add spec amd instructions to intel convert infrastructure
This commit is contained in:
parent
fca8b8d7eb
commit
c4e4b6112a
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue