// 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 #include 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)); }