srctree

Robin Linden parent 2179c640 97c48f3c
azm: Support JMP to a later offset as well

inlinesplit
azm/amd64/assembler.h added: 66, removed: 4, total 62
@@ -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);
 
azm/amd64/assembler_test.cpp added: 66, removed: 4, total 62
@@ -73,6 +73,38 @@ int main() {
});
});
 
s.add_test("JMP, forwards", [](etest::IActions &a) {
Assembler assembler;
 
auto slot1 = assembler.unlinked_label();
assembler.jmp(slot1);
assembler.ud2();
assembler.jmp(slot1);
auto slot2 = assembler.link(slot1);
assembler.jmp(slot2);
 
a.expect_eq(assembler.take_assembled(),
CodeVec{
0xe9, // jmp rel32
0x07, // 7
0x00,
0x00,
0x00,
0x0f, // ud2
0x0b,
0xe9, // jmp rel32
0x00, // 0
0x00,
0x00,
0x00,
0xe9, // jmp rel32
0xfb, // -5
0xff,
0xff,
0xff,
});
});
 
s.add_test("MOV r32, imm32", [](etest::IActions &a) {
Assembler assembler;
 
 
azm/azm_example.cpp added: 66, removed: 4, total 62
@@ -11,6 +11,8 @@
int main() {
using namespace azm::amd64;
Assembler assembler;
auto forward = assembler.unlinked_label();
assembler.jmp(forward);
assembler.mov(Reg32::Eax, Imm32{3});
assembler.add(Reg32::Eax, Imm32{39});
assembler.mov(Reg32::Ecx, Imm32{0x4321});
@@ -18,7 +20,7 @@ int main() {
auto end = assembler.label();
assembler.ret();
 
// TODO(robinlinden): jump here.
assembler.link(forward);
assembler.mov(Reg32::Edx, Imm32{0x12345678});
assembler.mov(Reg32::Ebx, Imm32{0x1234});
assembler.jmp(end);