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.c112
1 files changed, 109 insertions, 3 deletions
diff --git a/executor/x86_64/asm.c b/executor/x86_64/asm.c
index f2bb801..a6bf274 100644
--- a/executor/x86_64/asm.c
+++ b/executor/x86_64/asm.c
@@ -480,6 +480,112 @@ int asm_call_rel32(Asm *self, i32 relative) {
}
void asm_override_call_rel32(Asm *self, u32 asm_index, i32 new_relative) {
+ assert(*(u8*)(self->code + asm_index) == 0xE8);
+ new_relative -= 5; /* In x86, the relative position starts from the next instruction */
+ am_memcpy((u8*)self->code + asm_index + 1, &new_relative, sizeof(new_relative));
+}
+
+int asm_cmp_rm(Asm *self, Reg64 reg1, AsmPtr *reg2) {
+ ins_start(self);
+ /* 8 bytes is the maximum size of the instruction. We don't how how large it will be so we prepare for the largest size */
+ return_if_error(asm_ensure_capacity(self, 8));
+ *self->code_it++ = REX_W;
+ *self->code_it++ = 0x3B;
+ asm_rm(self, reg2, reg1);
+ ins_end(self, "cmp %s, %s", reg64_to_str(reg1), asm_ptr_to_string(reg2));
+ return 0;
+}
+
+int asm_sete_m(Asm *self, AsmPtr *dst) {
+ assert(dst->base != RSP && dst->base != RBP && dst->base != RSI && dst->base != RDI);
+ ins_start(self);
+ /* 8 bytes is the maximum size of the instruction. We don't how how large it will be so we prepare for the largest size */
+ return_if_error(asm_ensure_capacity(self, 8));
+ *self->code_it++ = 0x0F;
+ *self->code_it++ = 0x94;
+ asm_rm(self, dst, 0x0); /* the @src bits are not used */
+ ins_end(self, "sete %s", asm_ptr_to_string(dst));
+ return 0;
+}
+
+int asm_sete_r(Asm *self, Reg64 dst) {
+ assert(dst != RSP && dst != RBP && dst != RSI && dst != RDI);
+ ins_start(self);
+ /* 8 bytes is the maximum size of the instruction. We don't how how large it will be so we prepare for the largest size */
+ return_if_error(asm_ensure_capacity(self, 8));
+ *self->code_it++ = 0x0F;
+ *self->code_it++ = 0x94;
+ asm_rr(self, dst, 0x0); /* the @src bits are not used */
+ ins_end(self, "sete %s", reg64_to_str(dst));
+ return 0;
+}
+
+/*
+ Note: This is sometimes called with @relative INT32_MAX-(2 or 6) (will print jz 0x7ffffff9), in which case it's most likely a dummy
+ jump until the relative position is later changed with @asm_override_jcc_rel32.
+ TODO: Update the ins_end debug print to take that into account somehow
+*/
+int asm_jz(Asm *self, i32 relative) {
+ /*
+ Note: We dont use the 16-bit relative variant, as it will clear the upper two bytes of the EIP registers, resulting
+ in a maximum instruction pointer size of 16 bits
+ */
+ ins_start(self);
+ if(abs(relative - 2) <= INT8_MAX) {
+ relative -= 2;
+ return_if_error(asm_ensure_capacity(self, 2));
+ *self->code_it++ = 0x74;
+ *self->code_it++ = (i8)relative;
+ } else {
+ relative -= 6;
+ return_if_error(asm_ensure_capacity(self, 6));
+ *self->code_it++ = 0x0F;
+ *self->code_it++ = 0x84;
+ am_memcpy(self->code_it, &relative, sizeof(relative));
+ self->code_it += sizeof(relative);
+ }
+ ins_end(self, "jz 0x%x", relative);
+ return 0;
+}
+
+void asm_override_jcc_rel32(Asm *self, u32 asm_index, i32 new_relative) {
+ /* +2 because rel32 variant of the jump instruction opcode is 2 bytes */
+ assert(*(u8*)(self->code + asm_index) == 0x0F);
+ assert(*(u8*)(self->code + asm_index + 1) == 0x84);
+ new_relative -= 6; /* In x86, the relative position starts from the next instruction */
+ am_memcpy((u8*)self->code + asm_index + 2, &new_relative, sizeof(new_relative));
+}
+
+/*
+ Note: This is sometimes called with @relative INT32_MAX-(2 or 5) (will print jmp 0x7ffffffa), in which case it's most likely a dummy
+ jump until the relative position is later changed with @asm_override_jmp_rel32.
+ TODO: Update the ins_end debug print to take that into account somehow
+*/
+int asm_jmp(Asm *self, i32 relative) {
+ /*
+ Note: We dont use the 16-bit relative variant, as it will clear the upper two bytes of the EIP registers, resulting
+ in a maximum instruction pointer size of 16 bits
+ */
+ ins_start(self);
+ if(abs(relative - 2) <= INT8_MAX) {
+ relative -= 2;
+ return_if_error(asm_ensure_capacity(self, 2));
+ *self->code_it++ = 0xEB;
+ *self->code_it++ = (i8)relative;
+ } else {
+ relative -= 5;
+ return_if_error(asm_ensure_capacity(self, 5));
+ *self->code_it++ = 0xE9;
+ am_memcpy(self->code_it, &relative, sizeof(relative));
+ self->code_it += sizeof(relative);
+ }
+ ins_end(self, "jmp 0x%x", relative);
+ return 0;
+}
+
+void asm_override_jmp_rel32(Asm *self, u32 asm_index, i32 new_relative) {
+ /* +1 to skip instruction opcode */
+ assert(*(u8*)(self->code + asm_index) == 0xE9);
new_relative -= 5; /* In x86, the relative position starts from the next instruction */
am_memcpy((u8*)self->code + asm_index + 1, &new_relative, sizeof(new_relative));
}
@@ -488,8 +594,7 @@ void asm_override_call_rel32(Asm *self, u32 asm_index, i32 new_relative) {
/* /r */
#define DEFINE_INS_RM(mnemonic, opcode) \
-int asm_##mnemonic##_rmb(Asm *self, Reg32 dst, Reg32 src) { \
- return_if_error(asm_ensure_capacity(self, 2)); \
+int asm_##mnemonic##_rmb(Asm *self, Reg32 dst, Reg32 src) { \
*self->code_it++ = opcode; \
*self->code_it++ = 0xC0 + 8*dst + src; \
return 0; \
@@ -498,6 +603,7 @@ int asm_##mnemonic##_rmb(Asm *self, Reg32 dst, Reg32 src) { \
int asm_##mnemonic##_rm32(Asm *self, Reg32 dst, Reg32 src) { \
int result; \
ins_start(self); \
+ return_if_error(asm_ensure_capacity(self, 2)); \
result = asm_##mnemonic##_rmb(self, (Reg32)dst, (Reg32)src); \
ins_end(self, #mnemonic" %s, %s", reg32_to_str(dst), reg32_to_str(src)); \
return result; \
@@ -506,7 +612,7 @@ int asm_##mnemonic##_rm32(Asm *self, Reg32 dst, Reg32 src) { \
int asm_##mnemonic##_rm64(Asm *self, Reg64 dst, Reg64 src) { \
int result; \
ins_start(self); \
- return_if_error(asm_ensure_capacity(self, 1)); \
+ return_if_error(asm_ensure_capacity(self, 3)); \
*self->code_it++ = REX_W; \
result = asm_##mnemonic##_rmb(self, (Reg32)dst, (Reg32)src); \
ins_end(self, #mnemonic" %s, %s", reg64_to_str(dst), reg64_to_str(src)); \