diff --git a/core/relinker/CMakeLists.txt b/core/relinker/CMakeLists.txt index 00a281fff..b9e5539ae 100644 --- a/core/relinker/CMakeLists.txt +++ b/core/relinker/CMakeLists.txt @@ -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() diff --git a/core/relinker/codegen/include/codegen/x86/X64Assembler.hpp b/core/relinker/codegen/include/codegen/x86/X64Assembler.hpp new file mode 100644 index 000000000..b3f06e719 --- /dev/null +++ b/core/relinker/codegen/include/codegen/x86/X64Assembler.hpp @@ -0,0 +1,118 @@ +#ifndef CODEGEN_X86_X64ASSEMBLER_HPP +#define CODEGEN_X86_X64ASSEMBLER_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace Codegen { + +template class X64Assembler { +public: + X64Assembler(TEmitter& code, const std::map& callTable, const std::map& 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(0x48 | (raw(destination) >> 3)), static_cast(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(0x48 | ((raw(destination) >> 3) << 2)), 0x8d, static_cast(0x05 | ((raw(destination) & 7) << 3))}, storage.at(name)); + } + + void Global(const X64Register destination, const std::string& name) { + code.Rip({static_cast(0x48 | ((raw(destination) >> 3) << 2)), 0x8b, static_cast(0x05 | ((raw(destination) & 7) << 3))}, storage.at(name)); + } + + void Save(const std::string& name, const X64Register source) { + code.Rip({static_cast(0x48 | ((raw(source) >> 3) << 2)), 0x89, static_cast(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>& Functions() const { return functions; } + +private: + static constexpr std::uint8_t raw(const X64Register value) { return static_cast(value); } + + void binary(const std::uint8_t opcode, const X64Register destination, const X64Register source) { + code.Emit({static_cast(0x48 | ((raw(source) >> 3) << 2) | (raw(destination) >> 3)), opcode, static_cast(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(0x48 | (raw(destination) >> 3)), 0x81, static_cast(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((size == 8 ? 0x48 : 0x40) | ((raw(value) >> 3) << 2) | (raw(base) >> 3))}); + if (size < 4 && extend) + code.Emit({0x0f}); + code.Emit({opcode, static_cast(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& callTable; + const std::map& storage; + std::map labels; + std::vector> branches; + std::vector> functions; +}; + +} + +#endif diff --git a/core/relinker/codegen/include/codegen/x86/X64Register.hpp b/core/relinker/codegen/include/codegen/x86/X64Register.hpp new file mode 100644 index 000000000..cea0ee2a4 --- /dev/null +++ b/core/relinker/codegen/include/codegen/x86/X64Register.hpp @@ -0,0 +1,12 @@ +#ifndef CODEGEN_X86_X64REGISTER_HPP +#define CODEGEN_X86_X64REGISTER_HPP + +#include + +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 diff --git a/core/relinker/elfpatcher/src/windows/WindowsDependencyStubBuilder.cpp b/core/relinker/elfpatcher/src/windows/WindowsDependencyStubBuilder.cpp index 7ca5eff82..a6e4c952d 100644 --- a/core/relinker/elfpatcher/src/windows/WindowsDependencyStubBuilder.cpp +++ b/core/relinker/elfpatcher/src/windows/WindowsDependencyStubBuilder.cpp @@ -1,119 +1,17 @@ #include +#include #include 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; 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& 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(0x48 | (destination >> 3)), static_cast(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(0x48 | ((destination >> 3) << 2)), 0x8d, static_cast(0x05 | ((destination & 7) << 3))}, storage.at(name)); - } - - void Global(const Register destination, const std::string& name) { - code.Rip({static_cast(0x48 | ((destination >> 3) << 2)), 0x8b, static_cast(0x05 | ((destination & 7) << 3))}, storage.at(name)); - } - - void Save(const std::string& name, const Register source) { - code.Rip({static_cast(0x48 | ((source >> 3) << 2)), 0x89, static_cast(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(0x48 | ((source >> 3) << 2) | (destination >> 3)), opcode, static_cast(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(0x48 | (destination >> 3)), 0x81, static_cast(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((size == 8 ? 0x48 : 0x40) | ((value >> 3) << 2) | (base >> 3))}); - if (size < 4 && extend) - code.Emit({0x0f}); - code.Emit({opcode, static_cast(0x80 | ((value & 7) << 3) | (base & 7))}); - if ((base & 7) == Sp) - code.Emit({0x24}); - code.U32(offset); - } - - WindowsStubEmitter& code; - const WindowsImports& imports; - const std::map& storage; - std::map labels; - std::vector> branches; - std::vector starts; - std::vector 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; } }