@@ -5,6 +5,7 @@
#ifndef AZM_AMD64_ASSEMBLER_H_
#define AZM_AMD64_ASSEMBLER_H_
#include <cstddef>
#include <cstdint>
#include <iostream>
#include <optional>
@@ -42,12 +43,32 @@ struct Label {
std::size_t offset{};
};
struct UnlinkedLabel {
std::vector<std::size_t> patch_offsets{};
};
// https://www.felixcloutier.com/x86/
class Assembler {
public:
[[nodiscard]] std::vector<std::uint8_t> take_assembled() { return std::exchange(assembled_, {}); }
Label label() const { return Label{assembled_.size()}; }
UnlinkedLabel unlinked_label() const { return UnlinkedLabel{}; }
Label link(UnlinkedLabel const &label) {
static constexpr int kInstructionSize = 4;
std::size_t const jmp_target_offset = assembled_.size();
for (std::size_t patch_offset : label.patch_offsets) {
auto const rel32 = static_cast<std::uint32_t>(jmp_target_offset - patch_offset - kInstructionSize);
assembled_[patch_offset + 0] = rel32 & 0xff;
assembled_[patch_offset + 1] = (rel32 >> 8) & 0xff;
assembled_[patch_offset + 2] = (rel32 >> 16) & 0xff;
assembled_[patch_offset + 3] = (rel32 >> 24) & 0xff;
}
return Label{jmp_target_offset};
}
// Instructions
void add(Reg32 dst, Imm32 imm32) {
@@ -68,6 +89,13 @@ public:
emit(Imm32{static_cast<std::uint32_t>(label.offset - assembled_.size() - kInstructionSize)});
}
void jmp(UnlinkedLabel &label) {
// JMP rel32
emit(0xe9);
label.patch_offsets.push_back(assembled_.size());
emit(Imm32{0xdeadbeef});
}
void mov(Reg32 dst, Imm32 imm32) {
emit(0xb8 + register_index(dst).value());
emit(imm32);