#include #include namespace pslang::jit::linux_x86_64 { namespace { // Construct the REX prefix: // W (0/1) - enable 64-bit operand // R (0/1) - extend ModR/M reg field // X (0/1) - extend the SIB index field // B (0/1) - extend the base or opcode register field std::uint8_t rex(std::uint8_t W, std::uint8_t R, std::uint8_t X, std::uint8_t B) { check_bits(W, 1, "REX prefix W"); check_bits(R, 1, "REX prefix R"); check_bits(X, 1, "REX prefix X"); check_bits(B, 1, "REX prefix B"); return 0x40u | (W << 3) | (R << 2) | (X << 1) | B; } // Same, but using `reg` enum instead - passes the highest (#3) bit of // the register number to rex() std::uint8_t rex(std::uint8_t W, reg R, reg X, reg B) { return rex(W, std::uint8_t(R) >> 3, std::uint8_t(X) >> 3, std::uint8_t(B) >> 3); } bool is_rex(std::uint8_t value) { return (value & 0xf0) == 0x40; } // Memory static constexpr std::uint8_t MOD_MEM = 0; // Memory + 8-bit offset static constexpr std::uint8_t MOD_MEM8 = 1; // Memory + 32-bit offset static constexpr std::uint8_t MOD_MEM32 = 2; // Register static constexpr std::uint8_t MOD_REG = 3; std::uint8_t modrm(std::uint8_t mod, std::uint8_t reg, std::uint8_t rm) { check_bits(mod, 2, "ModR/M mod"); check_bits(reg, 3, "ModR/M reg"); check_bits(rm, 3, "ModR/M rm"); return (mod << 6) | (reg << 3) | rm; } std::uint8_t modrm(std::uint8_t mod, reg reg, std::uint8_t rm) { return modrm(mod, (std::uint8_t)(reg) & 0b111, rm); } std::uint8_t modrm(std::uint8_t mod, std::uint8_t reg_, reg rm) { return modrm(mod, reg_, (std::uint8_t)(rm) & 0b111); } std::uint8_t modrm(std::uint8_t mod, reg reg_, reg rm) { return modrm(mod, (std::uint8_t)(reg_) & 0b111, (std::uint8_t)(rm) & 0b111); } static constexpr std::uint8_t SIB_NOINDEX = 4; std::uint8_t sib(std::uint8_t scale, std::uint8_t index, std::uint8_t base) { check_bits(scale, 2, "SIB scale"); check_bits(index, 3, "SIB index"); check_bits(base, 3, "SIB base"); return (scale << 6) | (index << 3) | base; } std::uint8_t sib(std::uint8_t scale, reg reg_, std::uint8_t base) { return sib(scale, (std::uint8_t)(reg_) & 0b111, base); } std::uint8_t sib(std::uint8_t scale, std::uint8_t reg_, reg base) { return sib(scale, reg_, (std::uint8_t)(base) & 0b111); } std::uint8_t sib(std::uint8_t scale, reg reg_, reg base) { return sib(scale, (std::uint8_t)(reg_) & 0b111, (std::uint8_t)(base) & 0b111); } std::uint8_t opcode_add_reg(std::uint8_t opcode, reg reg) { return opcode + ((std::uint8_t)(reg) & 0b111); } bool need_sib(reg reg) { return ((std::uint8_t)(reg) & 0b111) == 4; } std::uint8_t vex(std::uint8_t R, std::uint8_t X, std::uint8_t B, std::uint8_t MMMMM) { check_bits(R, 1, "VEX prefix R"); check_bits(X, 1, "VEX prefix X"); check_bits(B, 1, "VEX prefix B"); check_bits(MMMMM, 1, "VEX prefix m-mmmm"); return ((1 - R) << 7) | ((1 - X) << 6) | ((1 - B) << 5) | MMMMM; } std::uint8_t vex(reg R, reg X, reg B, std::uint8_t MMMMM) { return rex(std::uint8_t(R) >> 3, std::uint8_t(X) >> 3, std::uint8_t(B) >> 3, MMMMM); } } void instruction_builder::nop() { do_push(0x90_ub); } void instruction_builder::ret(std::uint16_t count) { if (count == 0) do_push(0xc3_ub); else do_push(0xc2_ub, count); } void instruction_builder::add_imm(reg reg, std::int32_t value) { if (value >= -128 && value <= 127) do_push(rex(1, {}, {}, reg), 0x83_ub, modrm(MOD_REG, 0, reg), (std::int8_t)value); else do_push(rex(1, {}, {}, reg), 0x81_ub, modrm(MOD_REG, 0, reg), value); } void instruction_builder::sub_imm(reg reg, std::int32_t value) { if (value >= -128 && value <= 127) do_push(rex(1, {}, {}, reg), 0x83_ub, modrm(MOD_REG, 5, reg), (std::int8_t)value); else do_push(rex(1, {}, {}, reg), 0x81_ub, modrm(MOD_REG, 5, reg), value); } void instruction_builder::movzd(std::uint32_t value, reg reg_dst) { if (((std::uint8_t)(reg_dst) & 0b1000) != 0) do_push(rex(0, {}, {}, reg_dst), opcode_add_reg(0xb8, reg_dst), value); else do_push(opcode_add_reg(0xb8, reg_dst), value); } void instruction_builder::movsd(std::int32_t value, reg reg_dst) { do_push(rex(1, {}, {}, reg_dst), 0xc7_ub, modrm(MOD_REG, 0, reg_dst), value); } void instruction_builder::movzx_8(reg reg_src, reg reg_dst) { do_push(rex(1, reg_dst, {}, reg_src), 0x0f_ub, 0xb6_ub, modrm(MOD_REG, reg_dst, reg_src)); } void instruction_builder::movzx_16(reg reg_src, reg reg_dst) { do_push(rex(1, reg_dst, {}, reg_src), 0x0f_ub, 0xb7_ub, modrm(MOD_REG, reg_dst, reg_src)); } void instruction_builder::movzx_32(reg reg_src, reg reg_dst) { do_push(rex(0, reg_src, {}, reg_dst), 0x89_ub, modrm(MOD_REG, reg_src, reg_dst)); } void instruction_builder::movsx_8(reg reg_src, reg reg_dst) { do_push(rex(1, reg_dst, {}, reg_src), 0x0f_ub, 0xbe_ub, modrm(MOD_REG, reg_dst, reg_src)); } void instruction_builder::movsx_16(reg reg_src, reg reg_dst) { do_push(rex(1, reg_dst, {}, reg_src), 0x0f_ub, 0xbf_ub, modrm(MOD_REG, reg_dst, reg_src)); } void instruction_builder::movsx_32(reg reg_src, reg reg_dst) { do_push(rex(1, reg_dst, {}, reg_src), 0x63_ub, modrm(MOD_REG, reg_dst, reg_src)); } void instruction_builder::mov(std::uint64_t value, reg reg_dst) { do_push(rex(1, {}, {}, reg_dst), opcode_add_reg(0xb8, reg_dst), value); } void instruction_builder::mov(reg reg_src, reg reg_dst) { do_push(rex(1, reg_dst, {}, reg_src), 0x8b_ub, modrm(MOD_REG, reg_dst, reg_src)); } void instruction_builder::mov_gpr_to_xmm(reg reg_src, reg reg_dst) { do_push(0x66_ub, rex(1, reg_dst, {}, reg_src), 0x0f_ub, 0x6e_ub, modrm(MOD_REG, reg_dst, reg_src)); } void instruction_builder::mov_gpr_to_xmm_32(reg reg_src, reg reg_dst) { do_push(0x66_ub, rex(0, reg_dst, {}, reg_src), 0x0f_ub, 0x6e_ub, modrm(MOD_REG, reg_dst, reg_src)); } void instruction_builder::mov_read(reg reg_addr, std::int32_t offset, reg reg_dst) { if (need_sib(reg_addr)) do_push(rex(1, reg_dst, {}, reg_addr), 0x8b_ub, modrm(MOD_MEM32, reg_dst, reg_addr), sib(0, SIB_NOINDEX, reg_addr), offset); else do_push(rex(1, reg_dst, {}, reg_addr), 0x8b_ub, modrm(MOD_MEM32, reg_dst, reg_addr), offset); } void instruction_builder::mov_read_32(reg reg_addr, std::int32_t offset, reg reg_dst) { if (need_sib(reg_addr)) do_push(rex(0, reg_dst, {}, reg_addr), 0x8b_ub, modrm(MOD_MEM32, reg_dst, reg_addr), sib(0, SIB_NOINDEX, reg_addr), offset); else do_push(rex(0, reg_dst, {}, reg_addr), 0x8b_ub, modrm(MOD_MEM32, reg_dst, reg_addr), offset); } void instruction_builder::mov_read_16(reg reg_addr, std::int32_t offset, reg reg_dst) { if (need_sib(reg_addr)) do_push(0x66_ub, rex(0, reg_dst, {}, reg_addr), 0x8b_ub, modrm(MOD_MEM32, reg_dst, reg_addr), sib(0, SIB_NOINDEX, reg_addr), offset); else do_push(0x66_ub, rex(0, reg_dst, {}, reg_addr), 0x8b_ub, modrm(MOD_MEM32, reg_dst, reg_addr), offset); } void instruction_builder::mov_read_8(reg reg_addr, std::int32_t offset, reg reg_dst) { if (need_sib(reg_addr)) do_push(rex(0, reg_dst, {}, reg_addr), 0x8a_ub, modrm(MOD_MEM32, reg_dst, reg_addr), sib(0, SIB_NOINDEX, reg_addr), offset); else do_push(rex(0, reg_dst, {}, reg_addr), 0x8a_ub, modrm(MOD_MEM32, reg_dst, reg_addr), offset); } void instruction_builder::mov_read_rip(std::int32_t offset, reg reg_dst) { do_push(rex(1, reg_dst, {}, {}), 0x8b_ub, modrm(0, reg_dst, 0b101), offset); } void instruction_builder::mov_read_rip_prev(std::int32_t offset, reg reg_dst) { mov_read_rip(offset - 7, reg_dst); } void instruction_builder::mov_write(reg reg_src, reg reg_addr, std::int32_t offset) { if (need_sib(reg_addr)) do_push(rex(1, reg_src, {}, reg_addr), 0x89_ub, modrm(MOD_MEM32, reg_src, reg_addr), sib(0, SIB_NOINDEX, reg_addr), offset); else do_push(rex(1, reg_src, {}, reg_addr), 0x89_ub, modrm(MOD_MEM32, reg_src, reg_addr), offset); } void instruction_builder::mov_write_32(reg reg_src, reg reg_addr, std::int32_t offset) { if (need_sib(reg_addr)) do_push(rex(0, reg_src, {}, reg_addr), 0x89_ub, modrm(MOD_MEM32, reg_src, reg_addr), sib(0, SIB_NOINDEX, reg_addr), offset); else do_push(rex(0, reg_src, {}, reg_addr), 0x89_ub, modrm(MOD_MEM32, reg_src, reg_addr), offset); } void instruction_builder::mov_write_16(reg reg_src, reg reg_addr, std::int32_t offset) { if (need_sib(reg_addr)) do_push(0x66_ub, rex(0, reg_src, {}, reg_addr), 0x89_ub, modrm(MOD_MEM32, reg_src, reg_addr), sib(0, SIB_NOINDEX, reg_addr), offset); else do_push(0x66_ub, rex(0, reg_src, {}, reg_addr), 0x89_ub, modrm(MOD_MEM32, reg_src, reg_addr), offset); } void instruction_builder::mov_write_8(reg reg_src, reg reg_addr, std::int32_t offset) { if (need_sib(reg_addr)) do_push(rex(0, reg_src, {}, reg_addr), 0x88_ub, modrm(MOD_MEM32, reg_src, reg_addr), sib(0, SIB_NOINDEX, reg_addr), offset); else do_push(rex(0, reg_src, {}, reg_addr), 0x88_ub, modrm(MOD_MEM32, reg_src, reg_addr), offset); } void instruction_builder::mov_write_rip(reg reg_src, std::int32_t offset) { do_push(rex(1, reg_src, {}, {}), 0x89_ub, modrm(0, reg_src, 0b101), offset); } void instruction_builder::mov_write_rip_prev(reg reg_src, std::int32_t offset) { mov_write_rip(reg_src, offset - 7); } void instruction_builder::mov_read_xmm(reg reg_addr, std::int32_t offset, reg reg_dst) { if (need_sib(reg_addr)) do_push(0x66_ub, rex(1, reg_dst, {}, reg_addr), 0x0f_ub, 0x6e_ub, modrm(MOD_MEM32, reg_dst, reg_addr), sib(0, SIB_NOINDEX, reg_addr), offset); else do_push(0x66_ub, rex(1, reg_dst, {}, reg_addr), 0x0f_ub, 0x6e_ub, modrm(MOD_MEM32, reg_dst, reg_addr), offset); } void instruction_builder::mov_write_xmm(reg reg_src, reg reg_addr, std::int32_t offset) { if (need_sib(reg_addr)) do_push(0x66_ub, rex(0, reg_src, {}, reg_addr), 0x0f_ub, 0xd6_ub, modrm(MOD_MEM32, reg_src, reg_addr), sib(0, SIB_NOINDEX, reg_addr), offset); else do_push(0x66_ub, rex(0, reg_src, {}, reg_addr), 0x0f_ub, 0xd6_ub, modrm(MOD_MEM32, reg_src, reg_addr), offset); } void instruction_builder::mov_read_xmm_32(reg reg_addr, std::int32_t offset, reg reg_dst) { if (need_sib(reg_addr)) do_push(0xf3_ub, rex(0, reg_dst, {}, reg_addr), 0x0f_ub, 0x10_ub, modrm(MOD_MEM32, reg_dst, reg_addr), sib(0, SIB_NOINDEX, reg_addr), offset); else do_push(0xf3_ub, rex(0, reg_dst, {}, reg_addr), 0x0f_ub, 0x10_ub, modrm(MOD_MEM32, reg_dst, reg_addr), offset); } void instruction_builder::mov_write_xmm_32(reg reg_src, reg reg_addr, std::int32_t offset) { if (need_sib(reg_addr)) do_push(0xf3_ub, rex(0, reg_src, {}, reg_addr), 0x0f_ub, 0x11_ub, modrm(MOD_MEM32, reg_src, reg_addr), sib(0, SIB_NOINDEX, reg_addr), offset); else do_push(0xf3_ub, rex(0, reg_src, {}, reg_addr), 0x0f_ub, 0x11_ub, modrm(MOD_MEM32, reg_src, reg_addr), offset); } void instruction_builder::lea(reg reg_addr, std::int32_t offset, reg reg_dst) { if (need_sib(reg_addr)) do_push(rex(1, reg_dst, {}, reg_addr), 0x8d_ub, modrm(MOD_MEM32, reg_dst, reg_addr), sib(0, SIB_NOINDEX, reg_addr), offset); else do_push(rex(1, reg_dst, {}, reg_addr), 0x8d_ub, modrm(MOD_MEM32, reg_dst, reg_addr), offset); } void instruction_builder::lea_rip(std::int32_t offset, reg reg_dst) { do_push(rex(1, reg_dst, {}, {}), 0x8d_ub, modrm(0, reg_dst, 0b101), offset); } void instruction_builder::lea_rip_prev(std::int32_t offset, reg reg_dst) { lea_rip(offset - 7, reg_dst); } void instruction_builder::push(reg reg) { do_push(rex(0, {}, {}, reg), opcode_add_reg(0x50, reg)); } void instruction_builder::pop(reg reg) { do_push(rex(0, {}, {}, reg), opcode_add_reg(0x58, reg)); } void instruction_builder::neg(reg reg) { do_push(rex(1, {}, {}, reg), 0xf7_ub, modrm(MOD_REG, 3, reg)); } void instruction_builder::not_(reg reg) { do_push(rex(1, {}, {}, reg), 0xf7_ub, modrm(MOD_REG, 2, reg)); } void instruction_builder::test(reg reg1, reg reg2) { do_push(rex(1, reg1, {}, reg2), 0x85_ub, modrm(MOD_REG, reg1, reg2)); } void instruction_builder::add(reg reg_src, reg reg_dst) { do_push(rex(1, reg_dst, {}, reg_src), 0x03_ub, modrm(MOD_REG, reg_dst, reg_src)); } void instruction_builder::sub(reg reg_src, reg reg_dst) { do_push(rex(1, reg_dst, {}, reg_src), 0x2b_ub, modrm(MOD_REG, reg_dst, reg_src)); } void instruction_builder::mul(reg reg_src, reg reg_dst) { do_push(rex(1, reg_dst, {}, reg_src), 0x0f_ub, 0xaf_ub, modrm(MOD_REG, reg_dst, reg_src)); } void instruction_builder::udiv(reg reg_src) { do_push(rex(1, {}, {}, reg_src), 0xf7_ub, modrm(MOD_REG, 0b110, reg_src)); } void instruction_builder::idiv(reg reg_src) { do_push(rex(1, {}, {}, reg_src), 0xf7_ub, modrm(MOD_REG, 0b111, reg_src)); } void instruction_builder::cqo() { do_push(0x48_ub, 0x99_ub); } void instruction_builder::and_(reg reg_src, reg reg_dst) { do_push(rex(1, reg_dst, {}, reg_src), 0x23_ub, modrm(MOD_REG, reg_dst, reg_src)); } void instruction_builder::and_imm8(reg reg_src, std::int8_t value) { do_push(rex(1, {}, {}, reg_src), 0x83_ub, modrm(MOD_REG, 0b100, reg_src), value); } void instruction_builder::or_(reg reg_src, reg reg_dst) { do_push(rex(1, reg_dst, {}, reg_src), 0x0b_ub, modrm(MOD_REG, reg_dst, reg_src)); } void instruction_builder::xor_(reg reg_src, reg reg_dst) { do_push(rex(1, reg_dst, {}, reg_src), 0x33_ub, modrm(MOD_REG, reg_dst, reg_src)); } void instruction_builder::shl(reg reg_dst) { do_push(rex(1, {}, {}, reg_dst), 0xd3_ub, modrm(MOD_REG, 0b100, reg_dst)); } void instruction_builder::shr(reg reg_dst) { do_push(rex(1, {}, {}, reg_dst), 0xd3_ub, modrm(MOD_REG, 0b101, reg_dst)); } void instruction_builder::shr1(reg reg_dst) { do_push(rex(1, {}, {}, reg_dst), 0xd1_ub, modrm(MOD_REG, 0b101, reg_dst)); } void instruction_builder::sar(reg reg_dst) { do_push(rex(1, {}, {}, reg_dst), 0xd3_ub, modrm(MOD_REG, 0b111, reg_dst)); } void instruction_builder::cmp(reg reg1, reg reg2) { do_push(rex(1, reg1, {}, reg2), 0x3b_ub, modrm(MOD_REG, reg1, reg2)); } void instruction_builder::cmovz(reg reg_src, reg reg_dst) { do_push(rex(1, reg_dst, {}, reg_src), 0x0f_ub, 0x44_ub, modrm(MOD_REG, reg_dst, reg_src)); } void instruction_builder::cmovnz(reg reg_src, reg reg_dst) { do_push(rex(1, reg_dst, {}, reg_src), 0x0f_ub, 0x45_ub, modrm(MOD_REG, reg_dst, reg_src)); } void instruction_builder::cmovb(reg reg_src, reg reg_dst) { do_push(rex(1, reg_dst, {}, reg_src), 0x0f_ub, 0x42_ub, modrm(MOD_REG, reg_dst, reg_src)); } void instruction_builder::cmovnb(reg reg_src, reg reg_dst) { do_push(rex(1, reg_dst, {}, reg_src), 0x0f_ub, 0x43_ub, modrm(MOD_REG, reg_dst, reg_src)); } void instruction_builder::cmovl(reg reg_src, reg reg_dst) { do_push(rex(1, reg_dst, {}, reg_src), 0x0f_ub, 0x4c_ub, modrm(MOD_REG, reg_dst, reg_src)); } void instruction_builder::cmovnl(reg reg_src, reg reg_dst) { do_push(rex(1, reg_dst, {}, reg_src), 0x0f_ub, 0x4d_ub, modrm(MOD_REG, reg_dst, reg_src)); } void instruction_builder::add_xmm(reg reg_src, reg reg_dst) { do_push(0xf2_ub, rex(0, reg_dst, {}, reg_src), 0x0f_ub, 0x58_ub, modrm(MOD_REG, reg_dst, reg_src)); } void instruction_builder::add_xmm_32(reg reg_src, reg reg_dst) { do_push(0xf3_ub, rex(0, reg_dst, {}, reg_src), 0x0f_ub, 0x58_ub, modrm(MOD_REG, reg_dst, reg_src)); } void instruction_builder::sub_xmm(reg reg_src, reg reg_dst) { do_push(0xf2_ub, rex(0, reg_dst, {}, reg_src), 0x0f_ub, 0x5c_ub, modrm(MOD_REG, reg_dst, reg_src)); } void instruction_builder::sub_xmm_32(reg reg_src, reg reg_dst) { do_push(0xf3_ub, rex(0, reg_dst, {}, reg_src), 0x0f_ub, 0x5c_ub, modrm(MOD_REG, reg_dst, reg_src)); } void instruction_builder::mul_xmm(reg reg_src, reg reg_dst) { do_push(0xf2_ub, rex(0, reg_dst, {}, reg_src), 0x0f_ub, 0x59_ub, modrm(MOD_REG, reg_dst, reg_src)); } void instruction_builder::mul_xmm_32(reg reg_src, reg reg_dst) { do_push(0xf3_ub, rex(0, reg_dst, {}, reg_src), 0x0f_ub, 0x59_ub, modrm(MOD_REG, reg_dst, reg_src)); } void instruction_builder::div_xmm(reg reg_src, reg reg_dst) { do_push(0xf2_ub, rex(0, reg_dst, {}, reg_src), 0x0f_ub, 0x5e_ub, modrm(MOD_REG, reg_dst, reg_src)); } void instruction_builder::div_xmm_32(reg reg_src, reg reg_dst) { do_push(0xf3_ub, rex(0, reg_dst, {}, reg_src), 0x0f_ub, 0x5e_ub, modrm(MOD_REG, reg_dst, reg_src)); } void instruction_builder::xor_xmm(reg reg_src, reg reg_dst) { do_push(0x66_ub, rex(0, reg_dst, {}, reg_src), 0x0f_ub, 0x57_ub, modrm(MOD_REG, reg_dst, reg_src)); } void instruction_builder::xor_xmm_32(reg reg_src, reg reg_dst) { do_push(rex(0, reg_dst, {}, reg_src), 0x0f_ub, 0x57_ub, modrm(MOD_REG, reg_dst, reg_src)); } void instruction_builder::cmp_xmm(reg reg1, reg reg2) { do_push(0x66_ub, rex(0, reg1, {}, reg2), 0x0f_ub, 0x2e_ub, modrm(MOD_REG, reg1, reg2)); } void instruction_builder::cmp_xmm_32(reg reg1, reg reg2) { do_push(rex(0, reg1, {}, reg2), 0x0f_ub, 0x2e_ub, modrm(MOD_REG, reg1, reg2)); } void instruction_builder::gpr_to_xmm(reg reg_src, reg reg_dst) { do_push(0xf2_ub, rex(1, reg_dst, {}, reg_src), 0x0f_ub, 0x2a_ub, modrm(MOD_REG, reg_dst, reg_src)); } void instruction_builder::gpr_to_xmm_32(reg reg_src, reg reg_dst) { do_push(0xf3_ub, rex(1, reg_dst, {}, reg_src), 0x0f_ub, 0x2a_ub, modrm(MOD_REG, reg_dst, reg_src)); } void instruction_builder::xmm_to_gpr(reg reg_src, reg reg_dst) { do_push(0xf2_ub, rex(1, reg_dst, {}, reg_src), 0x0f_ub, 0x2d_ub, modrm(MOD_REG, reg_dst, reg_src)); } void instruction_builder::xmm_32_to_gpr(reg reg_src, reg reg_dst) { do_push(0xf3_ub, rex(1, reg_dst, {}, reg_src), 0x0f_ub, 0x2d_ub, modrm(MOD_REG, reg_dst, reg_src)); } void instruction_builder::xmm_16_to_32(reg reg_src, reg reg_dst) { do_push(0xc4_ub, vex(reg_dst, {}, reg_src, 0b00010), 0b01111001_ub, 0x13_ub, modrm(MOD_REG, reg_dst, reg_src)); } void instruction_builder::xmm_32_to_16(reg reg_src, reg reg_dst) { do_push(0xc4_ub, vex(reg_dst, {}, reg_src, 0b00011), std::uint8_t(1 | ~(std::uint8_t(reg_src) << 3)), 0x1d_ub, modrm(MOD_REG, reg_dst, 0)); } void instruction_builder::xmm_32_to_64(reg reg_src, reg reg_dst) { do_push(0xf3_ub, rex(0, reg_dst, {}, reg_src), 0x0f_ub, 0x5a_ub, modrm(MOD_REG, reg_dst, reg_src)); } void instruction_builder::xmm_64_to_32(reg reg_src, reg reg_dst) { do_push(0xf2_ub, rex(0, reg_dst, {}, reg_src), 0x0f_ub, 0x5a_ub, modrm(MOD_REG, reg_dst, reg_src)); } void instruction_builder::jump(std::int32_t offset) { do_push(0xe9_ub, offset); } void instruction_builder::jump_if_zero(std::int32_t offset) { do_push(0x0f_ub, 0x84_ub, offset); } void instruction_builder::jump_if_nonzero(std::int32_t offset) { do_push(0x0f_ub, 0x85_ub, offset); } void instruction_builder::jump_if_sign(std::int32_t offset) { do_push(0x0f_ub, 0x88_ub, offset); } void instruction_builder::jump_inject(std::uint8_t * opcode, std::int32_t offset) { auto src = (std::uint8_t const *)(&offset); std::copy(src, src + 4, opcode + 1); } void instruction_builder::jump_inject_prev(std::uint8_t * opcode, std::int32_t offset) { jump_inject(opcode, offset - 5); } void instruction_builder::cjump_inject(std::uint8_t * opcode, std::int32_t offset) { auto src = (std::uint8_t const *)(&offset); std::copy(src, src + 4, opcode + 2); } void instruction_builder::cjump_inject_prev(std::uint8_t * opcode, std::int32_t offset) { cjump_inject(opcode, offset - 6); } void instruction_builder::mov_rip_inject(std::uint8_t * opcode, std::int32_t offset) { auto src = (std::uint8_t const *)(&offset); std::copy(src, src + 4, opcode + 3); } void instruction_builder::mov_rip_inject_prev(std::uint8_t * opcode, std::int32_t offset) { mov_rip_inject(opcode, offset - 7); } void instruction_builder::lea_rip_inject(std::uint8_t * opcode, std::int32_t offset) { auto src = (std::uint8_t const *)(&offset); std::copy(src, src + 4, opcode + 3); } void instruction_builder::lea_rip_inject_prev(std::uint8_t * opcode, std::int32_t offset) { lea_rip_inject(opcode, offset - 7); } void instruction_builder::call_imm(std::int32_t offset) { do_push(0xe8_ub, offset); } void instruction_builder::call_imm_inject(std::uint8_t * opcode, std::int32_t offset) { auto src = (std::uint8_t const *)(&offset); std::copy(src, src + 4, opcode + 1); } void instruction_builder::call_imm_inject_prev(std::uint8_t * opcode, std::int32_t offset) { call_imm_inject(opcode, offset - 5); } void instruction_builder::call_reg(reg reg_addr) { if ((std::uint8_t(reg_addr) & 0x8) == 0) do_push(0xff_ub, modrm(MOD_REG, 0b010, reg_addr)); else do_push(rex(0, {}, {}, reg_addr), 0xff_ub, modrm(MOD_REG, 0b010, reg_addr)); } void instruction_builder::resolve_offset(std::uint8_t * opcode, std::int32_t offset) { if (is_rex(*opcode)) ++opcode; if (*opcode == 0x8d) { // lea rip prev opcode += 2; } else if (*opcode == 0x8b) { // mov read rip prev opcode += 2; } else if (*opcode == 0x89) { // mov write rip prev opcode += 2; } else throw std::runtime_error("Internal error: unknown opcode to patch"); std::int32_t current_offset; std::copy(opcode, opcode + 4, (std::uint8_t *)¤t_offset); current_offset += offset; auto begin = (std::uint8_t const *)¤t_offset; std::copy(begin, begin + 4, opcode); } template void instruction_builder::do_push(Args ... values) { push_bytes(code, values...); } }