227 lines
5.6 KiB
C
227 lines
5.6 KiB
C
// REFERENCES:
|
|
// http://ref.x86asm.net/index.html (geek64-abc)
|
|
// https://wiki.osdev.org/X86-64_Instruction_Encoding
|
|
// https://defuse.ca/online-x86-assembler.htm
|
|
|
|
#include "asm.h"
|
|
#include "io.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
ip here;
|
|
|
|
#define REX 0x40
|
|
// REX prefix with 64-bit operands set
|
|
#define REX_W 0x48
|
|
#define REX_R 0x44
|
|
#define REX_X 0x42
|
|
#define REX_B 0x41
|
|
#define REXQ_R(r) (REX | ((r >= R8) ? REX_R : 0))
|
|
#define REXQ_B(r) (REX | ((r >= R8) ? REX_B : 0))
|
|
// REX prefix including upper bit of register
|
|
#define REXQ_WR(r) (REX_W | REXQ_R(r))
|
|
#define REXQ_WRB(r, b) (REX_W | REXQ_R(r) | REXQ_B(b))
|
|
// lower 3 bits of register (not including part encoded in REX)
|
|
#define REG(r) (r & 7)
|
|
|
|
#define MODRM_RR (3 << 6)
|
|
#define MODRM_RM (0 << 6)
|
|
#define MODRM_RD8 (1 << 6)
|
|
#define MODRM_RD32 (2 << 6)
|
|
#define MODRMQ(reg, rm) ((REG(reg) << 3) | REG(rm))
|
|
#define MODRMQ_RR(reg, rm) (MODRM_RR | MODRMQ(reg, rm))
|
|
#define MODRMQ_RM(reg, base) (MODRM_RM | MODRMQ(reg, base))
|
|
#define MODRMQ_RD8(reg, base) (MODRM_RD8 | MODRMQ(reg, base))
|
|
#define MODRMQ_RD32(reg, base) (MODRM_RD32 | MODRMQ(reg, base))
|
|
|
|
static int32_t rel_offs(ip from, ip to) {
|
|
// jumping through hoops to hopefully avoid UB
|
|
int64_t off = (int64_t) to - (int64_t) from;
|
|
if (off > INT32_MAX || off < INT32_MIN) {
|
|
fprintf(stderr, "displacement greater than 32 bits!\n");
|
|
fprintf(stderr, "if you ever encounter this error, let me know and I'll implement it.\n");
|
|
exit(1);
|
|
}
|
|
return (int32_t) off;
|
|
}
|
|
|
|
static int32_t rip_rel(ip there) {
|
|
return rel_offs(here, there);
|
|
}
|
|
|
|
#define JUMP_DISP8_SIZE 2
|
|
static void inst_jump_disp8(uint8_t disp) {
|
|
uint8_t inst[JUMP_DISP8_SIZE] = {
|
|
0xeb, // jmp Jbs
|
|
disp
|
|
};
|
|
emit(&inst, JUMP_DISP8_SIZE);
|
|
}
|
|
|
|
#define JUMP_DISP32_SIZE 5
|
|
static void inst_jump_disp32(uint32_t disp) {
|
|
emit_u8(0xe9); // jmp Jvds
|
|
emit_u32(disp);
|
|
}
|
|
|
|
void inst_jump(ip there) {
|
|
int32_t disp8 = rel_offs(here + JUMP_DISP8_SIZE, there);
|
|
if (disp8 < INT8_MAX && disp8 > INT8_MIN) {
|
|
inst_jump_disp8(disp8);
|
|
} else {
|
|
inst_jump_disp32(rel_offs(here + JUMP_DISP32_SIZE, there));
|
|
}
|
|
}
|
|
|
|
ip inst_jump_unresolved(void) {
|
|
inst_jump_disp32(0);
|
|
return here;
|
|
}
|
|
|
|
void inst_jump_resolve(ip disp, ip there) {
|
|
patch_i32(disp - 4, rel_offs(disp, there));
|
|
}
|
|
|
|
static void inst_mov_imm32(reg reg, uint32_t imm) {
|
|
// mov Zvqp Ivqp
|
|
if (reg >= R8) {
|
|
emit_u8(REXQ_R(reg));
|
|
}
|
|
emit_u8(0xb8 + REG(reg));
|
|
emit_u32(imm);
|
|
}
|
|
|
|
static void inst_mov_imm64(reg reg, uint64_t imm) {
|
|
// mov Zvqp Ivqp
|
|
uint8_t buf[10] = { REXQ_WR(reg), 0xb8 + REG(reg), 0, 0, 0, 0, 0, 0, 0, 0 };
|
|
memcpy(&buf[2], &imm, sizeof(uint64_t));
|
|
emit(buf, 10);
|
|
}
|
|
|
|
void inst_mov_imm(reg reg, uint64_t imm) {
|
|
// TODO: emit `mov ax`, `mov al`, xor, xor+inc, xor+neg
|
|
if (imm <= UINT32_MAX) {
|
|
inst_mov_imm32(reg, (uint32_t) imm);
|
|
} else {
|
|
inst_mov_imm64(reg, imm);
|
|
}
|
|
}
|
|
|
|
void inst_mov_imm_i64(reg reg, int64_t imm) {
|
|
// TODO: emit sign extensions
|
|
if (imm >= 0 && imm <= UINT32_MAX) {
|
|
inst_mov_imm32(reg, (uint32_t) imm);
|
|
} else {
|
|
inst_mov_imm64(reg, (uint64_t) imm);
|
|
}
|
|
}
|
|
|
|
static void check_base(reg base) {
|
|
if (base == SP || base == BP || base == R12 || base == R13) {
|
|
fprintf(stderr, "indirect addressing not implemented for sp & co\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
void inst_mov(reg dest, reg src) {
|
|
// mov Evqp Gvqp
|
|
emit_u8(REXQ_WRB(dest, src));
|
|
emit_u8(0x89);
|
|
emit_u8(MODRMQ_RR(src, dest));
|
|
}
|
|
|
|
void inst_mov_from(reg dest, reg base) {
|
|
check_base(base);
|
|
// mov Gvqp Evqp
|
|
emit_u8(REXQ_WRB(base, dest));
|
|
emit_u8(0x8B);
|
|
emit_u8(MODRMQ_RM(dest, base));
|
|
}
|
|
|
|
static void inst_mov_from_disp8(reg dest, reg base, int8_t disp) {
|
|
check_base(base);
|
|
// mov Gvqp Evqp
|
|
emit_u8(REXQ_WRB(base, dest));
|
|
emit_u8(0x8B);
|
|
emit_u8(MODRMQ_RD8(dest, base));
|
|
emit_u8(disp);
|
|
}
|
|
|
|
static void inst_mov_from_disp32(reg dest, reg base, int32_t disp) {
|
|
check_base(base);
|
|
// mov Gvqp Evqp
|
|
emit_u8(REXQ_WRB(base, dest));
|
|
emit_u8(0x8B);
|
|
emit_u8(MODRMQ_RD32(dest, base));
|
|
emit_u32((int32_t) disp);
|
|
}
|
|
|
|
void inst_mov_from_disp(reg dest, reg base, int32_t disp) {
|
|
if (disp == 0) {
|
|
inst_mov_from(dest, base);
|
|
} else if (disp <= INT8_MAX && disp >= INT8_MIN) {
|
|
inst_mov_from_disp8(dest, base, (int8_t) disp);
|
|
} else {
|
|
inst_mov_from_disp32(dest, base, disp);
|
|
}
|
|
}
|
|
|
|
void inst_mov_to(reg base, reg src) {
|
|
check_base(base);
|
|
// mov Evqp Gvqp
|
|
emit_u8(REXQ_WRB(base, src));
|
|
emit_u8(0x89);
|
|
emit_u8(MODRMQ_RR(base, src));
|
|
}
|
|
|
|
static void inst_mov_to_disp8(reg base, reg src, int8_t disp) {
|
|
check_base(base);
|
|
// mov Evqp Gvqp
|
|
emit_u8(REXQ_WRB(base, src));
|
|
emit_u8(0x89);
|
|
emit_u8(MODRMQ_RD8(base, src));
|
|
emit_u8(disp);
|
|
}
|
|
|
|
static void inst_mov_to_disp32(reg base, reg src, int32_t disp) {
|
|
check_base(base);
|
|
// mov Evqp Gvqp
|
|
emit_u8(REXQ_WRB(base, src));
|
|
emit_u8(0x89);
|
|
emit_u8(MODRMQ_RD32(base, src));
|
|
emit_u32((uint32_t) disp);
|
|
}
|
|
|
|
void inst_mov_to_disp(reg base, reg src, int32_t disp) {
|
|
if (disp == 0) {
|
|
inst_mov_to(base, src);
|
|
} else if (disp <= INT8_MAX && disp >= INT8_MIN) {
|
|
inst_mov_to_disp8(base, src, (int8_t) disp);
|
|
} else {
|
|
inst_mov_to_disp32(base, src, disp);
|
|
}
|
|
}
|
|
|
|
|
|
void inst_syscall(void) {
|
|
const uint8_t buf[2] = { 0x0f, 0x05 };
|
|
emit(&buf, 2);
|
|
}
|
|
|
|
void inst_push(reg reg) {
|
|
// push Zvq
|
|
if (reg >= R8) {
|
|
emit_u8(REX_B);
|
|
}
|
|
emit_u8(0x50 + REG(reg));
|
|
}
|
|
|
|
void inst_pop(reg reg) {
|
|
// pop Zvq
|
|
if (reg >= R8) {
|
|
emit_u8(REX_B);
|
|
}
|
|
emit_u8(0x58 + REG(reg));
|
|
}
|