refactor(codegen): separated x86 assembler
This commit is contained in:
parent
5d715ee549
commit
04e1bc0a2d
|
|
@ -64,7 +64,7 @@ if(WIN32)
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/elfpatcher/src/windows/WindowsPeFormat.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/io/src/BufferUtils.cpp
|
||||
)
|
||||
target_include_directories(windows_dependency_diagnostics_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/elfpatcher/include ${CMAKE_CURRENT_SOURCE_DIR}/domain/include ${CMAKE_CURRENT_SOURCE_DIR}/io/include)
|
||||
target_include_directories(windows_dependency_diagnostics_tests PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/elfpatcher/include ${CMAKE_CURRENT_SOURCE_DIR}/domain/include ${CMAKE_CURRENT_SOURCE_DIR}/io/include ${CMAKE_CURRENT_SOURCE_DIR}/codegen/include)
|
||||
endif()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,118 @@
|
|||
#ifndef CODEGEN_X86_X64ASSEMBLER_HPP
|
||||
#define CODEGEN_X86_X64ASSEMBLER_HPP
|
||||
|
||||
#include <codegen/CodegenException.hpp>
|
||||
#include <codegen/x86/X64Register.hpp>
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace Codegen {
|
||||
|
||||
template<typename TEmitter> class X64Assembler {
|
||||
public:
|
||||
X64Assembler(TEmitter& code, const std::map<std::string, std::uint32_t>& callTable, const std::map<std::string, std::uint32_t>& storage) : code(code), callTable(callTable), storage(storage) {}
|
||||
|
||||
void Mark(const std::string& label) {
|
||||
if (!labels.emplace(label, code.GetRva()).second)
|
||||
throw CodegenException("Duplicate assembler label: " + label, code.GetRva());
|
||||
}
|
||||
|
||||
void Jump(const std::string& label, const std::uint8_t condition = 0) {
|
||||
const auto branch = condition == 0 ? code.Branch({0xe9}) : code.Branch({0x0f, condition});
|
||||
branches.emplace_back(branch, label);
|
||||
}
|
||||
|
||||
void Call(const std::string& label) { branches.emplace_back(code.Branch({0xe8}), label); }
|
||||
void Api(const std::string& name) { code.Rip({0xff, 0x15}, callTable.at(name)); }
|
||||
|
||||
void Begin(const std::string& name) {
|
||||
Mark(name);
|
||||
const auto start = code.GetRva();
|
||||
functions.emplace_back(start, start);
|
||||
code.Emit({0x53, 0x55, 0x56, 0x57, 0x41, 0x54, 0x41, 0x55, 0x41, 0x56, 0x41, 0x57, 0x48, 0x83, 0xec, 0x48});
|
||||
}
|
||||
|
||||
void End() {
|
||||
code.Emit({0x48, 0x83, 0xc4, 0x48, 0x41, 0x5f, 0x41, 0x5e, 0x41, 0x5d, 0x41, 0x5c, 0x5f, 0x5e, 0x5d, 0x5b, 0xc3});
|
||||
functions.back().second = code.GetRva();
|
||||
}
|
||||
|
||||
void Mov(const X64Register destination, const X64Register source) { binary(0x89, destination, source); }
|
||||
void Add(const X64Register destination, const X64Register source) { binary(0x01, destination, source); }
|
||||
void Sub(const X64Register destination, const X64Register source) { binary(0x29, destination, source); }
|
||||
void Compare(const X64Register left, const X64Register right) { binary(0x39, left, right); }
|
||||
void Test(const X64Register value) { binary(0x85, value, value); }
|
||||
|
||||
void Value(const X64Register destination, const std::uint64_t value) {
|
||||
code.Emit({static_cast<std::uint8_t>(0x48 | (raw(destination) >> 3)), static_cast<std::uint8_t>(0xb8 | (raw(destination) & 7))});
|
||||
code.U64(value);
|
||||
}
|
||||
|
||||
void AddValue(const X64Register destination, const std::uint32_t value) { immediate(destination, 0, value); }
|
||||
void SubValue(const X64Register destination, const std::uint32_t value) { immediate(destination, 5, value); }
|
||||
void CompareValue(const X64Register destination, const std::uint32_t value) { immediate(destination, 7, value); }
|
||||
void Mask(const X64Register destination, const std::uint32_t value) { immediate(destination, 4, value); }
|
||||
|
||||
void Load(const X64Register destination, const X64Register base, const std::uint32_t offset = 0, const std::uint8_t size = 8) { memory(destination, base, offset, size == 1 ? 0xb6 : size == 2 ? 0xb7 : 0x8b, size); }
|
||||
void Store(const X64Register base, const std::uint32_t offset, const X64Register source) { memory(source, base, offset, 0x89, 8); }
|
||||
void StoreByte(const X64Register base, const X64Register source) { memory(source, base, 0, 0x88, 1, false); }
|
||||
void Address(const X64Register destination, const X64Register base, const std::uint32_t offset) { memory(destination, base, offset, 0x8d, 8); }
|
||||
|
||||
void Data(const X64Register destination, const std::string& name) {
|
||||
code.Rip({static_cast<std::uint8_t>(0x48 | ((raw(destination) >> 3) << 2)), 0x8d, static_cast<std::uint8_t>(0x05 | ((raw(destination) & 7) << 3))}, storage.at(name));
|
||||
}
|
||||
|
||||
void Global(const X64Register destination, const std::string& name) {
|
||||
code.Rip({static_cast<std::uint8_t>(0x48 | ((raw(destination) >> 3) << 2)), 0x8b, static_cast<std::uint8_t>(0x05 | ((raw(destination) & 7) << 3))}, storage.at(name));
|
||||
}
|
||||
|
||||
void Save(const std::string& name, const X64Register source) {
|
||||
code.Rip({static_cast<std::uint8_t>(0x48 | ((raw(source) >> 3) << 2)), 0x89, static_cast<std::uint8_t>(0x05 | ((raw(source) & 7) << 3))}, storage.at(name));
|
||||
}
|
||||
|
||||
void Text(const std::string& name) { Data(X64Register::Cx, name); Call("write"); }
|
||||
|
||||
void Finish() {
|
||||
for (const auto& [offset, label] : branches)
|
||||
code.PatchBranch(offset, labels.at(label));
|
||||
}
|
||||
|
||||
std::uint32_t Label(const std::string& name) const { return labels.at(name); }
|
||||
const std::vector<std::pair<std::uint32_t, std::uint32_t>>& Functions() const { return functions; }
|
||||
|
||||
private:
|
||||
static constexpr std::uint8_t raw(const X64Register value) { return static_cast<std::uint8_t>(value); }
|
||||
|
||||
void binary(const std::uint8_t opcode, const X64Register destination, const X64Register source) {
|
||||
code.Emit({static_cast<std::uint8_t>(0x48 | ((raw(source) >> 3) << 2) | (raw(destination) >> 3)), opcode, static_cast<std::uint8_t>(0xc0 | ((raw(source) & 7) << 3) | (raw(destination) & 7))});
|
||||
}
|
||||
|
||||
void immediate(const X64Register destination, const std::uint8_t operation, const std::uint32_t value) {
|
||||
code.Emit({static_cast<std::uint8_t>(0x48 | (raw(destination) >> 3)), 0x81, static_cast<std::uint8_t>(0xc0 | (operation << 3) | (raw(destination) & 7))});
|
||||
code.U32(value);
|
||||
}
|
||||
|
||||
void memory(const X64Register value, const X64Register base, const std::uint32_t offset, const std::uint8_t opcode, const std::uint8_t size, const bool extend = true) {
|
||||
code.Emit({static_cast<std::uint8_t>((size == 8 ? 0x48 : 0x40) | ((raw(value) >> 3) << 2) | (raw(base) >> 3))});
|
||||
if (size < 4 && extend)
|
||||
code.Emit({0x0f});
|
||||
code.Emit({opcode, static_cast<std::uint8_t>(0x80 | ((raw(value) & 7) << 3) | (raw(base) & 7))});
|
||||
if ((raw(base) & 7) == raw(X64Register::Sp))
|
||||
code.Emit({0x24});
|
||||
code.U32(offset);
|
||||
}
|
||||
|
||||
TEmitter& code;
|
||||
const std::map<std::string, std::uint32_t>& callTable;
|
||||
const std::map<std::string, std::uint32_t>& storage;
|
||||
std::map<std::string, std::uint32_t> labels;
|
||||
std::vector<std::pair<std::size_t, std::string>> branches;
|
||||
std::vector<std::pair<std::uint32_t, std::uint32_t>> functions;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef CODEGEN_X86_X64REGISTER_HPP
|
||||
#define CODEGEN_X86_X64REGISTER_HPP
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace Codegen {
|
||||
|
||||
enum class X64Register : std::uint8_t { Ax, Cx, Dx, Bx, Sp, Bp, Si, Di, R8, R9, R10, R11, R12, R13, R14, R15 };
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,119 +1,17 @@
|
|||
#include <elfpatcher/windows/WindowsDependencyStubBuilder.hpp>
|
||||
#include <codegen/x86/X64Assembler.hpp>
|
||||
#include <io/BufferUtils.hpp>
|
||||
|
||||
namespace Elfpatcher::Windows {
|
||||
|
||||
namespace {
|
||||
|
||||
enum Register : std::uint8_t { Ax, Cx, Dx, Bx, Sp, Bp, Si, Di, R8, R9, R10, R11, R12, R13, R14, R15 };
|
||||
using enum Codegen::X64Register;
|
||||
using Assembler = Codegen::X64Assembler<WindowsStubEmitter>;
|
||||
constexpr std::uint32_t PathCapacity = 32768;
|
||||
constexpr std::uint32_t NodeSize = PathCapacity + 64;
|
||||
constexpr std::uint32_t NodeLimit = 128;
|
||||
|
||||
class Assembler {
|
||||
public:
|
||||
Assembler(WindowsStubEmitter& code, const WindowsImports& imports, const std::map<std::string, std::uint32_t>& storage) : code(code), imports(imports), storage(storage) {}
|
||||
|
||||
void Mark(const std::string& label) {
|
||||
if (!labels.emplace(label, code.GetRva()).second)
|
||||
throw Domain::RelinkerException("Duplicate dependency stub label: " + label);
|
||||
}
|
||||
|
||||
void Jump(const std::string& label, const std::uint8_t condition = 0) {
|
||||
const auto branch = condition == 0 ? code.Branch({0xe9}) : code.Branch({0x0f, condition});
|
||||
branches.emplace_back(branch, label);
|
||||
}
|
||||
|
||||
void Call(const std::string& label) { branches.emplace_back(code.Branch({0xe8}), label); }
|
||||
void Api(const std::string& name) { code.Rip({0xff, 0x15}, imports.Functions.at(name)); }
|
||||
|
||||
void Begin(const std::string& name) {
|
||||
Mark(name);
|
||||
starts.push_back(code.GetRva());
|
||||
code.Emit({0x53, 0x55, 0x56, 0x57, 0x41, 0x54, 0x41, 0x55, 0x41, 0x56, 0x41, 0x57, 0x48, 0x83, 0xec, 0x48});
|
||||
}
|
||||
|
||||
void End() {
|
||||
code.Emit({0x48, 0x83, 0xc4, 0x48, 0x41, 0x5f, 0x41, 0x5e, 0x41, 0x5d, 0x41, 0x5c, 0x5f, 0x5e, 0x5d, 0x5b, 0xc3});
|
||||
ends.push_back(code.GetRva());
|
||||
}
|
||||
|
||||
void Mov(const Register destination, const Register source) { binary(0x89, destination, source); }
|
||||
void Add(const Register destination, const Register source) { binary(0x01, destination, source); }
|
||||
void Sub(const Register destination, const Register source) { binary(0x29, destination, source); }
|
||||
void Compare(const Register left, const Register right) { binary(0x39, left, right); }
|
||||
void Test(const Register value) { binary(0x85, value, value); }
|
||||
|
||||
void Value(const Register destination, const std::uint64_t value) {
|
||||
code.Emit({static_cast<std::uint8_t>(0x48 | (destination >> 3)), static_cast<std::uint8_t>(0xb8 | (destination & 7))});
|
||||
code.U64(value);
|
||||
}
|
||||
|
||||
void AddValue(const Register destination, const std::uint32_t value) { immediate(destination, 0, value); }
|
||||
void SubValue(const Register destination, const std::uint32_t value) { immediate(destination, 5, value); }
|
||||
void CompareValue(const Register destination, const std::uint32_t value) { immediate(destination, 7, value); }
|
||||
void Mask(const Register destination, const std::uint32_t value) { immediate(destination, 4, value); }
|
||||
|
||||
void Load(const Register destination, const Register base, const std::uint32_t offset = 0, const std::uint8_t size = 8) { memory(destination, base, offset, size == 1 ? 0xb6 : size == 2 ? 0xb7 : 0x8b, size); }
|
||||
void Store(const Register base, const std::uint32_t offset, const Register source) { memory(source, base, offset, 0x89, 8); }
|
||||
void StoreByte(const Register base, const Register source) { memory(source, base, 0, 0x88, 1, false); }
|
||||
void Address(const Register destination, const Register base, const std::uint32_t offset) { memory(destination, base, offset, 0x8d, 8); }
|
||||
|
||||
void Data(const Register destination, const std::string& name) {
|
||||
code.Rip({static_cast<std::uint8_t>(0x48 | ((destination >> 3) << 2)), 0x8d, static_cast<std::uint8_t>(0x05 | ((destination & 7) << 3))}, storage.at(name));
|
||||
}
|
||||
|
||||
void Global(const Register destination, const std::string& name) {
|
||||
code.Rip({static_cast<std::uint8_t>(0x48 | ((destination >> 3) << 2)), 0x8b, static_cast<std::uint8_t>(0x05 | ((destination & 7) << 3))}, storage.at(name));
|
||||
}
|
||||
|
||||
void Save(const std::string& name, const Register source) {
|
||||
code.Rip({static_cast<std::uint8_t>(0x48 | ((source >> 3) << 2)), 0x89, static_cast<std::uint8_t>(0x05 | ((source & 7) << 3))}, storage.at(name));
|
||||
}
|
||||
|
||||
void Text(const std::string& name) { Data(Cx, name); Call("write"); }
|
||||
|
||||
void Finish() {
|
||||
for (const auto& [offset, label] : branches)
|
||||
code.PatchBranch(offset, labels.at(label));
|
||||
}
|
||||
|
||||
WindowsDependencyStub Result(const std::uint32_t unwind) const {
|
||||
WindowsDependencyStub result{labels.at("diagnose"), {}};
|
||||
for (std::size_t index = 0; index < starts.size(); ++index)
|
||||
result.Functions.push_back({starts[index], ends.at(index), unwind});
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
void binary(const std::uint8_t opcode, const Register destination, const Register source) {
|
||||
code.Emit({static_cast<std::uint8_t>(0x48 | ((source >> 3) << 2) | (destination >> 3)), opcode, static_cast<std::uint8_t>(0xc0 | ((source & 7) << 3) | (destination & 7))});
|
||||
}
|
||||
|
||||
void immediate(const Register destination, const std::uint8_t operation, const std::uint32_t value) {
|
||||
code.Emit({static_cast<std::uint8_t>(0x48 | (destination >> 3)), 0x81, static_cast<std::uint8_t>(0xc0 | (operation << 3) | (destination & 7))});
|
||||
code.U32(value);
|
||||
}
|
||||
|
||||
void memory(const Register value, const Register base, const std::uint32_t offset, const std::uint8_t opcode, const std::uint8_t size, const bool extend = true) {
|
||||
code.Emit({static_cast<std::uint8_t>((size == 8 ? 0x48 : 0x40) | ((value >> 3) << 2) | (base >> 3))});
|
||||
if (size < 4 && extend)
|
||||
code.Emit({0x0f});
|
||||
code.Emit({opcode, static_cast<std::uint8_t>(0x80 | ((value & 7) << 3) | (base & 7))});
|
||||
if ((base & 7) == Sp)
|
||||
code.Emit({0x24});
|
||||
code.U32(offset);
|
||||
}
|
||||
|
||||
WindowsStubEmitter& code;
|
||||
const WindowsImports& imports;
|
||||
const std::map<std::string, std::uint32_t>& storage;
|
||||
std::map<std::string, std::uint32_t> labels;
|
||||
std::vector<std::pair<std::size_t, std::string>> branches;
|
||||
std::vector<std::uint32_t> starts;
|
||||
std::vector<std::uint32_t> ends;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
WindowsDependencyStubBuilder::WindowsDependencyStubBuilder(PeSection& data) {
|
||||
|
|
@ -151,7 +49,7 @@ WindowsDependencyStubBuilder::WindowsDependencyStubBuilder(PeSection& data) {
|
|||
}
|
||||
|
||||
WindowsDependencyStub WindowsDependencyStubBuilder::Build(WindowsStubEmitter& code, const WindowsImports& imports) const {
|
||||
Assembler a(code, imports, storage);
|
||||
Assembler a(code, imports.Functions, storage);
|
||||
|
||||
a.Begin("write");
|
||||
a.Mov(Si, Cx);
|
||||
|
|
@ -757,7 +655,10 @@ WindowsDependencyStub WindowsDependencyStubBuilder::Build(WindowsStubEmitter& co
|
|||
a.End();
|
||||
|
||||
a.Finish();
|
||||
return a.Result(unwindRva);
|
||||
WindowsDependencyStub result{a.Label("diagnose"), {}};
|
||||
for (const auto& [start, end] : a.Functions())
|
||||
result.Functions.push_back({start, end, unwindRva});
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue