aboutsummaryrefslogtreecommitdiff
path: root/executor/x86_64/asm.c
diff options
context:
space:
mode:
Diffstat (limited to 'executor/x86_64/asm.c')
-rw-r--r--executor/x86_64/asm.c203
1 files changed, 116 insertions, 87 deletions
diff --git a/executor/x86_64/asm.c b/executor/x86_64/asm.c
index c633db8..60b1752 100644
--- a/executor/x86_64/asm.c
+++ b/executor/x86_64/asm.c
@@ -8,7 +8,31 @@
#include <sys/mman.h>
-#define REX_W 0x48
+/* REX documentation: https://wiki.osdev.org/X86-64_Instruction_Encoding#Encoding */
+#define REX_W 0x48 /* 0 = 32-bit operand size, 1 = 64-bit operand size - For most operations... */
+
+static u8 rex_rr(Reg64 dst, Reg64 src) {
+ u8 rex = REX_W;
+ rex |= ((dst & REG64_EXTENDED_REG_BIT) >> 3); /* REX.B */
+ rex |= ((src & REG64_EXTENDED_REG_BIT) >> 1); /* REX.R */
+ assert(REG64_EXTENDED_REG_BIT == (1<<3));
+ return rex;
+}
+
+static u8 rex_sib(Reg64 dst, Reg64 src) {
+ u8 rex = REX_W;
+ rex |= ((src & REG64_EXTENDED_REG_BIT) >> 3); /* REX.B */
+ rex |= ((dst & REG64_EXTENDED_REG_BIT) >> 2); /* REX.X */
+ assert(REG64_EXTENDED_REG_BIT == (1<<3));
+ return rex;
+}
+
+static u8 rex_rm(AsmPtr *dst, Reg64 src) {
+ assert(REG64_EXTENDED_REG_BIT == (1<<3));
+ if(dst->index == 0 && (dst->base & REG64_REG_BITS) != RBP)
+ return rex_rr(dst->base, src);
+ return rex_sib(dst->base, src);
+}
#ifdef DEBUG
#include <stdarg.h>
@@ -84,6 +108,14 @@ static const char* reg64_to_str(Reg64 reg) {
case RBP: return "rbp";
case RSI: return "rsi";
case RDI: return "rdi";
+ case R8: return "r8";
+ case R9: return "r9";
+ case R10: return "r10";
+ case R11: return "r11";
+ case R12: return "r12";
+ case R13: return "r13";
+ case R14: return "r14";
+ case R15: return "r15";
}
assert(bool_false);
return NULL;
@@ -270,7 +302,7 @@ static void asm_rm(Asm *self, AsmPtr *mem, Reg64 reg) {
u8 rm_byte;
u8 disp_bytes;
assert(asm_get_capacity_left(self) >= 6);
- if((int)mem->index != -1) {
+ if((int)mem->index != -1) { /* SIB */
u8 sib_offset;
if(mem->disp == 0) {
rm_byte = 0x04;
@@ -293,31 +325,33 @@ static void asm_rm(Asm *self, AsmPtr *mem, Reg64 reg) {
}
#endif
assert(mem->base != RBP && "TODO: Implement RBP base for sib byte. RBP is special and requires different logic");
- sib_offset = (mem->scale << 5) + 8*mem->index + mem->base;
+ assert(mem->index != R13 && "TODO: Implement R13 base for sib byte. R13 is special and requires different logic");
+ sib_offset = (mem->scale << 5) + 8*(mem->index & REG64_REG_BITS) + (mem->base & REG64_REG_BITS);
*self->code_it++ = rm_byte;
*self->code_it++ = sib_offset;
} else {
+ u8 base = (mem->base & REG64_REG_BITS);
assert(mem->scale == 0); /* Scale isn't valid without index reg */
if(mem->disp == 0) {
- if(mem->base == RBP) {
+ if(base == RBP) {
rm_byte = 0x45;
- disp_bytes = 1;
+ disp_bytes = 1; /* RBP requires use of disp byte, even if it's not used */
} else {
- rm_byte = mem->base;
+ rm_byte = base;
disp_bytes = 0;
}
} else if(abs_i32(mem->disp) <= INT8_MAX) {
- rm_byte = 0x40 + mem->base;
+ rm_byte = 0x40 + base;
disp_bytes = 1;
} else {
- rm_byte = 0x80 + mem->base;
+ rm_byte = 0x80 + base;
disp_bytes = 4;
}
- *self->code_it++ = (reg << 3) | rm_byte;
+ *self->code_it++ = ((reg & REG64_REG_BITS) << 3) | rm_byte;
/* RSP requires SIB byte */
- if(mem->base == RSP)
+ if(base == RSP)
*self->code_it++ = 0x24;
}
@@ -328,13 +362,13 @@ static void asm_rm(Asm *self, AsmPtr *mem, Reg64 reg) {
/* There has to be at least 1 byte left in the asm buffer before calling this function. */
static void asm_rr(Asm *self, Reg64 dst, Reg64 src) {
assert(asm_get_capacity_left(self) >= 1);
- *self->code_it++ = 0xC0 + dst + 8*src;
+ *self->code_it++ = 0xC0 + (dst & REG64_REG_BITS) + 8*(src & REG64_REG_BITS);
}
/* TODO: Implement 1 and 2 byte immediate? */
void asm_mov_mi(Asm *self, AsmPtr *dst, i32 immediate) {
ins_start(self);
- *self->code_it++ = REX_W;
+ *self->code_it++ = rex_rm(dst, 0);
*self->code_it++ = 0xC7;
asm_rm(self, dst, 0);
am_memcpy(self->code_it, &immediate, sizeof(immediate));
@@ -344,7 +378,7 @@ void asm_mov_mi(Asm *self, AsmPtr *dst, i32 immediate) {
void asm_mov_mr(Asm *self, AsmPtr *dst, Reg64 src) {
ins_start(self);
- *self->code_it++ = REX_W;
+ *self->code_it++ = rex_rm(dst, src);
*self->code_it++ = 0x89;
asm_rm(self, dst, src);
ins_end(self, "mov %s, %s", asm_ptr_to_string(dst), reg64_to_str(src));
@@ -352,7 +386,7 @@ void asm_mov_mr(Asm *self, AsmPtr *dst, Reg64 src) {
void asm_mov_rm(Asm *self, Reg64 dst, AsmPtr *src) {
ins_start(self);
- *self->code_it++ = REX_W;
+ *self->code_it++ = rex_rm(src, dst);
*self->code_it++ = 0x8B;
asm_rm(self, src, dst);
ins_end(self, "mov %s, %s", reg64_to_str(dst), asm_ptr_to_string(src));
@@ -361,7 +395,7 @@ void asm_mov_rm(Asm *self, Reg64 dst, AsmPtr *src) {
/* Note: This shows as instruction movabs in intel assembly format */
void asm_mov_ri(Asm *self, Reg64 dst, i64 immediate) {
ins_start(self);
- *self->code_it++ = REX_W;
+ *self->code_it++ = rex_rr(dst, 0);
*self->code_it++ = 0xB8 + dst;
am_memcpy(self->code_it, &immediate, sizeof(immediate));
self->code_it += sizeof(immediate);
@@ -370,7 +404,7 @@ void asm_mov_ri(Asm *self, Reg64 dst, i64 immediate) {
void asm_mov_rr(Asm *self, Reg64 dst, Reg64 src) {
ins_start(self);
- *self->code_it++ = REX_W;
+ *self->code_it++ = rex_rr(dst, src);
*self->code_it++ = 0x89;
asm_rr(self, dst, src);
ins_end(self, "mov %s, %s", reg64_to_str(dst), reg64_to_str(src));
@@ -378,7 +412,7 @@ void asm_mov_rr(Asm *self, Reg64 dst, Reg64 src) {
void asm_add_rr(Asm *self, Reg64 dst, Reg64 src) {
ins_start(self);
- *self->code_it++ = REX_W;
+ *self->code_it++ = rex_rr(dst, src);
*self->code_it++ = 0x01;
asm_rr(self, dst, src);
ins_end(self, "add %s, %s", reg64_to_str(dst), reg64_to_str(src));
@@ -386,7 +420,7 @@ void asm_add_rr(Asm *self, Reg64 dst, Reg64 src) {
void asm_sub_rr(Asm *self, Reg64 dst, Reg64 src) {
ins_start(self);
- *self->code_it++ = REX_W;
+ *self->code_it++ = rex_rr(dst, src);
*self->code_it++ = 0x29;
asm_rr(self, dst, src);
ins_end(self, "sub %s, %s", reg64_to_str(dst), reg64_to_str(src));
@@ -394,7 +428,7 @@ void asm_sub_rr(Asm *self, Reg64 dst, Reg64 src) {
void asm_imul_rr(Asm *self, Reg64 dst, Reg64 src) {
ins_start(self);
- *self->code_it++ = REX_W;
+ *self->code_it++ = rex_rr(dst, src);
*self->code_it++ = 0x0F;
*self->code_it++ = 0xAF;
asm_rr(self, dst, src);
@@ -410,7 +444,7 @@ void asm_cqo(Asm *self) {
void asm_idiv_rr(Asm *self, Reg64 src) {
ins_start(self);
- *self->code_it++ = REX_W;
+ *self->code_it++ = rex_rr(src, 0);
*self->code_it++ = 0xF7;
asm_rr(self, src, 0x7);
ins_end(self, "idiv %s", reg64_to_str(src));
@@ -430,7 +464,7 @@ void asm_popr(Asm *self, Reg64 reg) {
void asm_callr(Asm *self, Reg64 reg) {
ins_start(self);
- *self->code_it++ = REX_W;
+ *self->code_it++ = rex_rr(reg, 0);
*self->code_it++ = 0xFF;
asm_rr(self, reg, 0x2);
ins_end(self, "call %s", reg64_to_str(reg));
@@ -457,7 +491,7 @@ void asm_overwrite_call_rel32(Asm *self, u32 asm_index, i32 new_relative) {
void asm_cmp_rm(Asm *self, Reg64 reg1, AsmPtr *reg2) {
ins_start(self);
- *self->code_it++ = REX_W;
+ *self->code_it++ = rex_rm(reg2, reg1);
*self->code_it++ = 0x3B;
asm_rm(self, reg2, reg1);
ins_end(self, "cmp %s, %s", reg64_to_str(reg1), asm_ptr_to_string(reg2));
@@ -546,25 +580,20 @@ void asm_overwrite_jmp_rel32(Asm *self, u32 asm_index, i32 new_relative) {
}
/* TODO: Remove these !*/
-
/* /r */
-#define DEFINE_INS_RM(mnemonic, opcode) \
-void asm_##mnemonic##_rmb(Asm *self, Reg32 dst, Reg32 src) { \
- *self->code_it++ = opcode; \
- *self->code_it++ = 0xC0 + 8*dst + src; \
-} \
- \
-void asm_##mnemonic##_rm32(Asm *self, Reg32 dst, Reg32 src) { \
- ins_start(self); \
- asm_##mnemonic##_rmb(self, (Reg32)dst, (Reg32)src); \
- ins_end(self, #mnemonic" %s, %s", reg32_to_str(dst), reg32_to_str(src)); \
-} \
- \
-void asm_##mnemonic##_rm64(Asm *self, Reg64 dst, Reg64 src) { \
- ins_start(self); \
- *self->code_it++ = REX_W; \
- asm_##mnemonic##_rmb(self, (Reg32)dst, (Reg32)src); \
- ins_end(self, #mnemonic" %s, %s", reg64_to_str(dst), reg64_to_str(src)); \
+#define DEFINE_INS_RM(mnemonic, opcode) \
+void asm_##mnemonic##_rm32(Asm *self, Reg32 dst, Reg32 src) { \
+ ins_start(self); \
+ *self->code_it++ = opcode; \
+ asm_rr(self, (Reg64)src, (Reg64)dst); \
+ ins_end(self, #mnemonic" %s, %s", reg32_to_str(dst), reg32_to_str(src)); \
+} \
+ \
+void asm_##mnemonic##_rm64(Asm *self, Reg64 dst, Reg64 src) { \
+ ins_start(self); \
+ *self->code_it++ = opcode; \
+ asm_rr(self, src, dst); \
+ ins_end(self, #mnemonic" %s, %s", reg64_to_str(dst), reg64_to_str(src)); \
}
DEFINE_INS_RM(mov, 0x8B)
@@ -581,31 +610,31 @@ DEFINE_INS_RM(cmp, 0x3B)
It's a number used to extend the opcode type, since the instruction only uses
one register the other register can be encoded for that.
*/
-#define DEFINE_INS_EXT_IMM(mnemonic, extension) \
-void asm_##mnemonic##_rmb_imm(Asm *self, Reg32 reg, i32 immediate) { \
- if(abs_i32(immediate) <= INT8_MAX) { \
- *self->code_it++ = 0x83; \
- *self->code_it++ = 0xC0 + 8*extension + reg; \
- *self->code_it++ = (u8)immediate; \
- } else { \
- *self->code_it++ = 0x81; \
- *self->code_it++ = 0xC0 + 8*extension + reg; \
- am_memcpy(self->code_it, &immediate, sizeof(immediate)); \
- self->code_it += sizeof(immediate); \
- } \
-} \
- \
-void asm_##mnemonic##_rm32_imm(Asm *self, Reg32 reg, i32 immediate) { \
- ins_start(self); \
- asm_##mnemonic##_rmb_imm(self, (Reg32)reg, immediate); \
- ins_end(self, #mnemonic" %s, 0x%x", reg32_to_str(reg), immediate); \
-}\
- \
-void asm_##mnemonic##_rm64_imm(Asm *self, Reg64 reg, i32 immediate) { \
- ins_start(self); \
- *self->code_it++ = REX_W; \
- asm_##mnemonic##_rmb_imm(self, (Reg32)reg, immediate); \
- ins_end(self, #mnemonic" %s, 0x%x", reg64_to_str(reg), immediate); \
+#define DEFINE_INS_EXT_IMM(mnemonic, extension) \
+void asm_##mnemonic##_rmb_imm(Asm *self, Reg32 reg, i32 immediate) { \
+ if(abs_i32(immediate) <= INT8_MAX) { \
+ *self->code_it++ = 0x83; \
+ asm_rr(self, (Reg64)reg, (Reg64)(extension)); \
+ *self->code_it++ = (u8)immediate; \
+ } else { \
+ *self->code_it++ = 0x81; \
+ asm_rr(self, (Reg64)reg, (Reg64)(extension)); \
+ am_memcpy(self->code_it, &immediate, sizeof(immediate)); \
+ self->code_it += sizeof(immediate); \
+ } \
+} \
+ \
+void asm_##mnemonic##_rm32_imm(Asm *self, Reg32 reg, i32 immediate) { \
+ ins_start(self); \
+ asm_##mnemonic##_rmb_imm(self, (Reg32)reg, immediate); \
+ ins_end(self, #mnemonic" %s, 0x%x", reg32_to_str(reg), immediate); \
+} \
+ \
+void asm_##mnemonic##_rm64_imm(Asm *self, Reg64 reg, i32 immediate) { \
+ ins_start(self); \
+ *self->code_it++ = rex_rr(reg, 0); \
+ asm_##mnemonic##_rmb_imm(self, (Reg32)reg, immediate); \
+ ins_end(self, #mnemonic" %s, 0x%x", reg64_to_str(reg), immediate); \
}
DEFINE_INS_EXT_IMM(add, 0)
@@ -623,28 +652,28 @@ DEFINE_INS_EXT_IMM(cmp, 7)
It's a number used to extend the opcode type, since the instruction only uses
one register the other register can be encoded for that.
*/
-#define DEFINE_INS_SHIFT_IMM8(mnemonic, extension) \
-void asm_##mnemonic##_rmb_imm(Asm *self, Reg32 reg, i8 immediate) { \
- if(immediate == 1) { \
- *self->code_it++ = 0xC1; \
- *self->code_it++ = 0xC0 + 8*reg + extension; \
- } else { \
- *self->code_it++ = 0xD1; \
- *self->code_it++ = 0xC0 + 8*reg + extension; \
- *self->code_it++ = immediate; \
- } \
-} \
- \
-void asm_##mnemonic##_rm32_imm(Asm *self, Reg32 reg, i8 immediate) { \
- ins_start(self); \
- ins_end(self, #mnemonic" %s, 0x%x", reg32_to_str(reg), immediate); \
-} \
- \
-void asm_##mnemonic##_rm64_imm(Asm *self, Reg64 reg, i8 immediate) { \
- ins_start(self); \
- *self->code_it++ = REX_W; \
- asm_##mnemonic##_rmb_imm(self, (Reg32)reg, immediate); \
- ins_end(self, #mnemonic" %s, 0x%x", reg64_to_str(reg), immediate); \
+#define DEFINE_INS_SHIFT_IMM8(mnemonic, extension) \
+void asm_##mnemonic##_rmb_imm(Asm *self, Reg32 reg, i8 immediate) { \
+ if(immediate == 1) { \
+ *self->code_it++ = 0xC1; \
+ asm_rr(self, (Reg64)reg, (Reg64)(extension)); \
+ } else { \
+ *self->code_it++ = 0xD1; \
+ asm_rr(self, (Reg64)reg, (Reg64)(extension)); \
+ *self->code_it++ = immediate; \
+ } \
+} \
+ \
+void asm_##mnemonic##_rm32_imm(Asm *self, Reg32 reg, i8 immediate) { \
+ ins_start(self); \
+ ins_end(self, #mnemonic" %s, 0x%x", reg32_to_str(reg), immediate); \
+} \
+ \
+void asm_##mnemonic##_rm64_imm(Asm *self, Reg64 reg, i8 immediate) { \
+ ins_start(self); \
+ *self->code_it++ = rex_rr(reg, 0); \
+ asm_##mnemonic##_rmb_imm(self, (Reg32)reg, immediate); \
+ ins_end(self, #mnemonic" %s, 0x%x", reg64_to_str(reg), immediate); \
}
DEFINE_INS_SHIFT_IMM8(rol, 0)