diff options
Diffstat (limited to 'executor/x86_64/asm.c')
-rw-r--r-- | executor/x86_64/asm.c | 112 |
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)); \ |