rizin/librz/arch/isa/x86/common.c
NOT XVilka ba8c170852
Migrate from Capstone to Zydis (x86 architecture) (#5164)
* Added build files [Capstone to Zydis]

* x86 Analysis [Capstone to Zydis]

* Changed x86 RzIL [Capstone to Zydis]

* Changed asm and arch files [Capstone to Zydis]

* Compilation, build error and test fixes [Capstone to Zydis]

* Test changes [Capstone to Zydis]

* Remaining changes [Capstone to Zydis]

* Added BE support [Capstone to Zydis]

---------

Co-authored-by: tushar3q34 <tushar3q34@gmail.com>
2025-05-23 13:03:46 +00:00

1571 lines
47 KiB
C

// SPDX-FileCopyrightText: 2023 Dhruv Maroo <dhruvmaru007@gmail.com>
// SPDX-FileCopyrightText: 2024-2025 tushar3q34 <tushar3q34@gmail.com>
// SPDX-License-Identifier: LGPL-3.0-only
#include "common.h"
#include <rz_il/rz_il_opbuilder_begin.h>
/**
* \brief x86 registers
*/
const char *x86_registers[X86_REG_MAX_VALUE] = {
[X86_REG_AH] = "ah",
[X86_REG_AL] = "al",
[X86_REG_AX] = "ax",
[X86_REG_BH] = "bh",
[X86_REG_BL] = "bl",
[X86_REG_BP] = "bp",
[X86_REG_BPL] = "bpl",
[X86_REG_BX] = "bx",
[X86_REG_CH] = "ch",
[X86_REG_CL] = "cl",
[X86_REG_CS] = "cs",
[X86_REG_CX] = "cx",
[X86_REG_DH] = "dh",
[X86_REG_DI] = "di",
[X86_REG_DIL] = "dil",
[X86_REG_DL] = "dl",
[X86_REG_DS] = "ds",
[X86_REG_DX] = "dx",
[X86_REG_EAX] = "eax",
[X86_REG_EBP] = "ebp",
[X86_REG_EBX] = "ebx",
[X86_REG_ECX] = "ecx",
[X86_REG_EDI] = "edi",
[X86_REG_EDX] = "edx",
[X86_REG_EFLAGS] = "eflags",
[X86_REG_EIP] = "eip",
[X86_REG_ES] = "es",
[X86_REG_ESI] = "esi",
[X86_REG_ESP] = "esp",
[X86_REG_X87STATUS] = "swd",
[X86_REG_FS] = "fs",
[X86_REG_GS] = "gs",
[X86_REG_IP] = "ip",
[X86_REG_RAX] = "rax",
[X86_REG_RBP] = "rbp",
[X86_REG_RBX] = "rbx",
[X86_REG_RCX] = "rcx",
[X86_REG_RDI] = "rdi",
[X86_REG_RDX] = "rdx",
[X86_REG_RIP] = "rip",
[X86_REG_RSI] = "rsi",
[X86_REG_RSP] = "rsp",
[X86_REG_SI] = "si",
[X86_REG_SIL] = "sil",
[X86_REG_SP] = "sp",
[X86_REG_SPL] = "spl",
[X86_REG_SS] = "ss",
[X86_REG_CR0] = "cr0",
[X86_REG_CR1] = "cr1",
[X86_REG_CR2] = "cr2",
[X86_REG_CR3] = "cr3",
[X86_REG_CR4] = "cr4",
[X86_REG_CR5] = "cr5",
[X86_REG_CR6] = "cr6",
[X86_REG_CR7] = "cr7",
[X86_REG_CR8] = "cr8",
[X86_REG_CR9] = "cr9",
[X86_REG_CR10] = "cr10",
[X86_REG_CR11] = "cr11",
[X86_REG_CR12] = "cr12",
[X86_REG_CR13] = "cr13",
[X86_REG_CR14] = "cr14",
[X86_REG_CR15] = "cr15",
[X86_REG_DR0] = "dr0",
[X86_REG_DR1] = "dr1",
[X86_REG_DR2] = "dr2",
[X86_REG_DR3] = "dr3",
[X86_REG_DR4] = "dr4",
[X86_REG_DR5] = "dr5",
[X86_REG_DR6] = "dr6",
[X86_REG_DR7] = "dr7",
[X86_REG_DR8] = "dr8",
[X86_REG_DR9] = "dr9",
[X86_REG_DR10] = "dr10",
[X86_REG_DR11] = "dr11",
[X86_REG_DR12] = "dr12",
[X86_REG_DR13] = "dr13",
[X86_REG_DR14] = "dr14",
[X86_REG_DR15] = "dr15",
[X86_REG_K0] = "k0",
[X86_REG_K1] = "k1",
[X86_REG_K2] = "k2",
[X86_REG_K3] = "k3",
[X86_REG_K4] = "k4",
[X86_REG_K5] = "k5",
[X86_REG_K6] = "k6",
[X86_REG_K7] = "k7",
[X86_REG_MM0] = "mm0",
[X86_REG_MM1] = "mm1",
[X86_REG_MM2] = "mm2",
[X86_REG_MM3] = "mm3",
[X86_REG_MM4] = "mm4",
[X86_REG_MM5] = "mm5",
[X86_REG_MM6] = "mm6",
[X86_REG_MM7] = "mm7",
[X86_REG_R8] = "r8",
[X86_REG_R9] = "r9",
[X86_REG_R10] = "r10",
[X86_REG_R11] = "r11",
[X86_REG_R12] = "r12",
[X86_REG_R13] = "r13",
[X86_REG_R14] = "r14",
[X86_REG_R15] = "r15",
[X86_REG_ST0] = "st0",
[X86_REG_ST1] = "st1",
[X86_REG_ST2] = "st2",
[X86_REG_ST3] = "st3",
[X86_REG_ST4] = "st4",
[X86_REG_ST5] = "st5",
[X86_REG_ST6] = "st6",
[X86_REG_ST7] = "st7",
[X86_REG_XMM0] = "xmm0",
[X86_REG_XMM1] = "xmm1",
[X86_REG_XMM2] = "xmm2",
[X86_REG_XMM3] = "xmm3",
[X86_REG_XMM4] = "xmm4",
[X86_REG_XMM5] = "xmm5",
[X86_REG_XMM6] = "xmm6",
[X86_REG_XMM7] = "xmm7",
[X86_REG_XMM8] = "xmm8",
[X86_REG_XMM9] = "xmm9",
[X86_REG_XMM10] = "xmm10",
[X86_REG_XMM11] = "xmm11",
[X86_REG_XMM12] = "xmm12",
[X86_REG_XMM13] = "xmm13",
[X86_REG_XMM14] = "xmm14",
[X86_REG_XMM15] = "xmm15",
[X86_REG_XMM16] = "xmm16",
[X86_REG_XMM17] = "xmm17",
[X86_REG_XMM18] = "xmm18",
[X86_REG_XMM19] = "xmm19",
[X86_REG_XMM20] = "xmm20",
[X86_REG_XMM21] = "xmm21",
[X86_REG_XMM22] = "xmm22",
[X86_REG_XMM23] = "xmm23",
[X86_REG_XMM24] = "xmm24",
[X86_REG_XMM25] = "xmm25",
[X86_REG_XMM26] = "xmm26",
[X86_REG_XMM27] = "xmm27",
[X86_REG_XMM28] = "xmm28",
[X86_REG_XMM29] = "xmm29",
[X86_REG_XMM30] = "xmm30",
[X86_REG_XMM31] = "xmm31",
[X86_REG_YMM0] = "ymm0",
[X86_REG_YMM1] = "ymm1",
[X86_REG_YMM2] = "ymm2",
[X86_REG_YMM3] = "ymm3",
[X86_REG_YMM4] = "ymm4",
[X86_REG_YMM5] = "ymm5",
[X86_REG_YMM6] = "ymm6",
[X86_REG_YMM7] = "ymm7",
[X86_REG_YMM8] = "ymm8",
[X86_REG_YMM9] = "ymm9",
[X86_REG_YMM10] = "ymm10",
[X86_REG_YMM11] = "ymm11",
[X86_REG_YMM12] = "ymm12",
[X86_REG_YMM13] = "ymm13",
[X86_REG_YMM14] = "ymm14",
[X86_REG_YMM15] = "ymm15",
[X86_REG_YMM16] = "ymm16",
[X86_REG_YMM17] = "ymm17",
[X86_REG_YMM18] = "ymm18",
[X86_REG_YMM19] = "ymm19",
[X86_REG_YMM20] = "ymm20",
[X86_REG_YMM21] = "ymm21",
[X86_REG_YMM22] = "ymm22",
[X86_REG_YMM23] = "ymm23",
[X86_REG_YMM24] = "ymm24",
[X86_REG_YMM25] = "ymm25",
[X86_REG_YMM26] = "ymm26",
[X86_REG_YMM27] = "ymm27",
[X86_REG_YMM28] = "ymm28",
[X86_REG_YMM29] = "ymm29",
[X86_REG_YMM30] = "ymm30",
[X86_REG_YMM31] = "ymm31",
[X86_REG_ZMM0] = "zmm0",
[X86_REG_ZMM1] = "zmm1",
[X86_REG_ZMM2] = "zmm2",
[X86_REG_ZMM3] = "zmm3",
[X86_REG_ZMM4] = "zmm4",
[X86_REG_ZMM5] = "zmm5",
[X86_REG_ZMM6] = "zmm6",
[X86_REG_ZMM7] = "zmm7",
[X86_REG_ZMM8] = "zmm8",
[X86_REG_ZMM9] = "zmm9",
[X86_REG_ZMM10] = "zmm10",
[X86_REG_ZMM11] = "zmm11",
[X86_REG_ZMM12] = "zmm12",
[X86_REG_ZMM13] = "zmm13",
[X86_REG_ZMM14] = "zmm14",
[X86_REG_ZMM15] = "zmm15",
[X86_REG_ZMM16] = "zmm16",
[X86_REG_ZMM17] = "zmm17",
[X86_REG_ZMM18] = "zmm18",
[X86_REG_ZMM19] = "zmm19",
[X86_REG_ZMM20] = "zmm20",
[X86_REG_ZMM21] = "zmm21",
[X86_REG_ZMM22] = "zmm22",
[X86_REG_ZMM23] = "zmm23",
[X86_REG_ZMM24] = "zmm24",
[X86_REG_ZMM25] = "zmm25",
[X86_REG_ZMM26] = "zmm26",
[X86_REG_ZMM27] = "zmm27",
[X86_REG_ZMM28] = "zmm28",
[X86_REG_ZMM29] = "zmm29",
[X86_REG_ZMM30] = "zmm30",
[X86_REG_ZMM31] = "zmm31",
[X86_REG_R8B] = "r8b",
[X86_REG_R9B] = "r9b",
[X86_REG_R10B] = "r10b",
[X86_REG_R11B] = "r11b",
[X86_REG_R12B] = "r12b",
[X86_REG_R13B] = "r13b",
[X86_REG_R14B] = "r14b",
[X86_REG_R15B] = "r15b",
[X86_REG_R8D] = "r8d",
[X86_REG_R9D] = "r9d",
[X86_REG_R10D] = "r10d",
[X86_REG_R11D] = "r11d",
[X86_REG_R12D] = "r12d",
[X86_REG_R13D] = "r13d",
[X86_REG_R14D] = "r14d",
[X86_REG_R15D] = "r15d",
[X86_REG_R8W] = "r8w",
[X86_REG_R9W] = "r9w",
[X86_REG_R10W] = "r10w",
[X86_REG_R11W] = "r11w",
[X86_REG_R12W] = "r12w",
[X86_REG_R13W] = "r13w",
[X86_REG_R14W] = "r14w",
[X86_REG_R15W] = "r15w"
};
const char *x86_eflags_registers[X86_EFLAGS_ENDING] = {
[X86_EFLAGS_CF] = "cf",
[X86_EFLAGS_PF] = "pf",
[X86_EFLAGS_AF] = "af",
[X86_EFLAGS_ZF] = "zf",
[X86_EFLAGS_SF] = "sf",
[X86_EFLAGS_TF] = "tf",
[X86_EFLAGS_IF] = "if",
[X86_EFLAGS_DF] = "df",
[X86_EFLAGS_OF] = "of",
[X86_EFLAGS_NT] = "nt",
[X86_EFLAGS_RF] = "rf",
[X86_EFLAGS_VM] = "vm",
[X86_EFLAGS_AC] = "ac"
};
const X86Reg gpr_hregs[] = {
X86_REG_AH, // rax
X86_REG_BH, // rbx
X86_REG_CH, // rcx
X86_REG_DH, // rdx
X86_REG_NONE, // rbp
X86_REG_NONE, // rdi
X86_REG_NONE, // rip
X86_REG_NONE, // riz
X86_REG_NONE, // rsi
X86_REG_NONE // rsp
};
const X86Reg gpr_lregs[] = {
X86_REG_AL, // rax
X86_REG_BL, // rbx
X86_REG_CL, // rcx
X86_REG_DL, // rdx
X86_REG_BPL, // rbp
X86_REG_DIL, // rdi
X86_REG_NONE, // rip
X86_REG_NONE, // riz
X86_REG_SIL, // rsi
X86_REG_SPL, // rsp
};
const X86Reg gpr_xregs[] = {
X86_REG_AX, // rax
X86_REG_BX, // rbx
X86_REG_CX, // rcx
X86_REG_DX, // rdx
X86_REG_BP, // rbp
X86_REG_DI, // rdi
X86_REG_IP, // rip
X86_REG_NONE, // riz
X86_REG_SI, // rsi
X86_REG_SP, // rsp
};
const X86Reg gpr_eregs[] = {
X86_REG_EAX, // rax
X86_REG_EBX, // rbx
X86_REG_ECX, // rcx
X86_REG_EDX, // rdx
X86_REG_EBP, // rbp
X86_REG_EDI, // rdi
X86_REG_EIP, // rip
X86_REG_NONE, // riz
X86_REG_ESI, // rsi
X86_REG_ESP, // rsp
};
const X86Reg gpr_rregs[] = {
X86_REG_RAX,
X86_REG_RBX,
X86_REG_RCX,
X86_REG_RDX,
X86_REG_RBP,
X86_REG_RDI,
X86_REG_RIP,
X86_REG_NONE,
X86_REG_RSI,
X86_REG_RSP
};
/**
* \brief Check if \p reg is a general purpose register (this term is quite loosely used here)
*
* \param reg
*/
bool x86_il_is_gpr(X86Reg reg) {
for (unsigned int i = 0; i < GPR_FAMILY_COUNT; i++) {
if (reg == gpr_hregs[i] || reg == gpr_lregs[i] || reg == gpr_xregs[i] || reg == gpr_eregs[i] || reg == gpr_rregs[i]) {
return true;
}
}
return false;
}
/**
* \brief Get size of \p reg
*
* \param reg
*/
ut8 x86_il_get_reg_size(X86Reg reg) {
for (unsigned int i = 0; i < GPR_FAMILY_COUNT; i++) {
if (reg == gpr_hregs[i] || reg == gpr_lregs[i]) {
return 8;
} else if (reg == gpr_xregs[i]) {
return 16;
} else if (reg == gpr_eregs[i]) {
return 32;
} else if (reg == gpr_rregs[i]) {
return 64;
}
}
return 0;
}
/**
* \brief Get the higher 8 bits (8-16) of register \p reg
*
* \param reg
* \param bits bitness
*/
RzILOpPure *x86_il_get_gprh(X86Reg reg, int bits) {
return UNSIGNED(8, SHIFTR0(VARG(x86_registers[reg]), U8(8)));
}
/**
* \brief Get the lower 8 bits (0-8) of register \p reg
*
* \param reg
* \param bits bitness
*/
RzILOpPure *x86_il_get_gprl(X86Reg reg, int bits) {
return UNSIGNED(8, VARG(x86_registers[reg]));
}
/**
* \brief Get the lower 16 bits (0-16) of register \p reg
*
* \param reg
* \param bits bitness
*/
RzILOpPure *x86_il_get_gpr16(X86Reg reg, int bits) {
if (bits == 16) {
// Don't perform unnecessary casting
return VARG(x86_registers[reg]);
}
return UNSIGNED(16, VARG(x86_registers[reg]));
}
/**
* \brief Get the lower 32 bits (0-32) of register \p reg
*
* \param reg
* \param bits bitness
*/
RzILOpPure *x86_il_get_gpr32(X86Reg reg, int bits) {
if (bits == 32) {
return VARG(x86_registers[reg]);
}
return UNSIGNED(32, VARG(x86_registers[reg]));
}
/**
* \brief Get 64 bits (0-64) of register \p reg
*
* \param reg
* \param bits bitness
*/
RzILOpPure *x86_il_get_gpr64(X86Reg reg, int bits) {
return VARG(x86_registers[reg]);
}
/**
* \brief Set the higher 8 bits (8-16) of register \p reg to \p val
*
* \param reg
* \param val
* \param bits bitness
*/
RzILOpEffect *x86_il_set_gprh(X86Reg reg, RZ_OWN RzILOpPure *val, int bits) {
RzILOpPure *mask = LOGNOT(UN(bits, 0xff00));
RzILOpPure *masked_reg = LOGAND(VARG(x86_registers[reg]), mask);
RzILOpPure *final_reg = LOGOR(masked_reg, SHIFTL0(UNSIGNED(bits, val), U8(8)));
return SETG(x86_registers[reg], final_reg);
}
/**
* \brief Set the lower 8 bits (0-8) of register \p reg to \p val
*
* \param reg
* \param val
* \param bits bitness
*/
RzILOpEffect *x86_il_set_gprl(X86Reg reg, RZ_OWN RzILOpPure *val, int bits) {
RzILOpPure *mask = LOGNOT(UN(bits, 0xff));
RzILOpPure *masked_reg = LOGAND(VARG(x86_registers[reg]), mask);
RzILOpPure *final_reg = LOGOR(masked_reg, UNSIGNED(bits, val));
return SETG(x86_registers[reg], final_reg);
}
/**
* \brief Set the lower 16 bits (0-16) of register \p reg to \p val
*
* \param reg
* \param val
* \param bits bitness
*/
RzILOpEffect *x86_il_set_gpr16(X86Reg reg, RZ_OWN RzILOpPure *val, int bits) {
if (bits == 16) {
// Don't perform unnecessary casting
return SETG(x86_registers[reg], val);
}
RzILOpPure *mask = LOGNOT(UN(bits, 0xffff));
RzILOpPure *masked_reg = LOGAND(VARG(x86_registers[reg]), mask);
RzILOpPure *final_reg = LOGOR(masked_reg, UNSIGNED(bits, val));
return SETG(x86_registers[reg], final_reg);
}
/**
* \brief Set the lower 32 bits (0-32) of register \p reg to \p val, and zero out the rest
* This is a very specific behavior of x86-64, see https://stackoverflow.com/questions/11177137/why-do-x86-64-instructions-on-32-bit-registers-zero-the-upper-part-of-the-full-6 for details
*
* \param reg
* \param val
* \param bits bitness
*/
RzILOpEffect *x86_il_set_gpr32(X86Reg reg, RZ_OWN RzILOpPure *val, int bits) {
if (bits == 32) {
return SETG(x86_registers[reg], val);
}
return SETG(x86_registers[reg], UNSIGNED(64, val));
}
/**
* \brief Set 64 bits (0-64) of register \p reg to \p val
*
* \param reg
* \param val
* \param bits bitness
*/
RzILOpEffect *x86_il_set_gpr64(X86Reg reg, RzILOpPure *val, int bits) {
return SETG(x86_registers[reg], val);
}
/**
* \brief Get the widest register corresponding to index \p index and bitness \p bits
*
* \param index
* \param bits
*/
X86Reg get_bitness_reg(unsigned int index, int bits) {
if (index >= GPR_FAMILY_COUNT) {
return X86_REG_NONE;
}
if (bits == 16) {
return gpr_xregs[index];
} else if (bits == 32) {
return gpr_eregs[index];
} else {
return gpr_rregs[index];
}
}
struct gpr_lookup_helper_t {
unsigned int index; ///< register index
RzILOpPure *(*get_handler)(X86Reg, int); ///< getter
RzILOpEffect *(*set_handler)(X86Reg, RzILOpPure *, int); ///< setter
};
const struct gpr_lookup_helper_t gpr_lookup_table[] = {
[X86_REG_AH] = { 0, x86_il_get_gprh, x86_il_set_gprh },
[X86_REG_AL] = { 0, x86_il_get_gprl, x86_il_set_gprl },
[X86_REG_AX] = { 0, x86_il_get_gpr16, x86_il_set_gpr16 },
[X86_REG_EAX] = { 0, x86_il_get_gpr32, x86_il_set_gpr32 },
[X86_REG_RAX] = { 0, x86_il_get_gpr64, x86_il_set_gpr64 },
[X86_REG_BH] = { 1, x86_il_get_gprh, x86_il_set_gprh },
[X86_REG_BL] = { 1, x86_il_get_gprl, x86_il_set_gprl },
[X86_REG_BX] = { 1, x86_il_get_gpr16, x86_il_set_gpr16 },
[X86_REG_EBX] = { 1, x86_il_get_gpr32, x86_il_set_gpr32 },
[X86_REG_RBX] = { 1, x86_il_get_gpr64, x86_il_set_gpr64 },
[X86_REG_CH] = { 2, x86_il_get_gprh, x86_il_set_gprh },
[X86_REG_CL] = { 2, x86_il_get_gprl, x86_il_set_gprl },
[X86_REG_CX] = { 2, x86_il_get_gpr16, x86_il_set_gpr16 },
[X86_REG_ECX] = { 2, x86_il_get_gpr32, x86_il_set_gpr32 },
[X86_REG_RCX] = { 2, x86_il_get_gpr64, x86_il_set_gpr64 },
[X86_REG_DH] = { 3, x86_il_get_gprh, x86_il_set_gprh },
[X86_REG_DL] = { 3, x86_il_get_gprl, x86_il_set_gprl },
[X86_REG_DX] = { 3, x86_il_get_gpr16, x86_il_set_gpr16 },
[X86_REG_EDX] = { 3, x86_il_get_gpr32, x86_il_set_gpr32 },
[X86_REG_RDX] = { 3, x86_il_get_gpr64, x86_il_set_gpr64 },
[X86_REG_BPL] = { 4, x86_il_get_gprl, x86_il_set_gprl },
[X86_REG_BP] = { 4, x86_il_get_gpr16, x86_il_set_gpr16 },
[X86_REG_EBP] = { 4, x86_il_get_gpr32, x86_il_set_gpr32 },
[X86_REG_RBP] = { 4, x86_il_get_gpr64, x86_il_set_gpr64 },
[X86_REG_DIL] = { 5, x86_il_get_gprl, x86_il_set_gprl },
[X86_REG_DI] = { 5, x86_il_get_gpr16, x86_il_set_gpr16 },
[X86_REG_EDI] = { 5, x86_il_get_gpr32, x86_il_set_gpr32 },
[X86_REG_RDI] = { 5, x86_il_get_gpr64, x86_il_set_gpr64 },
[X86_REG_SIL] = { 8, x86_il_get_gprl, x86_il_set_gprl },
[X86_REG_SI] = { 8, x86_il_get_gpr16, x86_il_set_gpr16 },
[X86_REG_ESI] = { 8, x86_il_get_gpr32, x86_il_set_gpr32 },
[X86_REG_RSI] = { 8, x86_il_get_gpr64, x86_il_set_gpr64 },
[X86_REG_SPL] = { 9, x86_il_get_gprl, x86_il_set_gprl },
[X86_REG_SP] = { 9, x86_il_get_gpr16, x86_il_set_gpr16 },
[X86_REG_ESP] = { 9, x86_il_get_gpr32, x86_il_set_gpr32 },
[X86_REG_RSP] = { 9, x86_il_get_gpr64, x86_il_set_gpr64 }
};
/**
* \brief Check if the register \p reg is an instruction pointer register
*
* \param reg
*/
bool is_pc_reg(X86Reg reg) {
return (reg == X86_REG_IP || reg == X86_REG_EIP || reg == X86_REG_RIP);
}
struct extreg_lookup_helper_t {
X86Reg curr_reg; ///< register being used
X86Reg base_reg; ///< base register for `curr_reg`
RzILOpPure *(*get_handler)(X86Reg, int); ///< getter
RzILOpEffect *(*set_handler)(X86Reg, RzILOpPure *, int); ///< setter
};
const struct extreg_lookup_helper_t extreg_lookup_table[] = {
// 64-bit wide
extreg_lookup(, x86_il_get_gpr64, x86_il_set_gpr64)
// 8-bit wide (byte)
extreg_lookup(B, x86_il_get_gprl, x86_il_set_gpr64)
// 16-bit wide (word)
extreg_lookup(W, x86_il_get_gpr16, x86_il_set_gpr64)
// 32-bit wide (dword)
extreg_lookup(D, x86_il_get_gpr32, x86_il_set_gpr64)
};
/**
* \brief Get the index for external register \p reg
*
* \param reg
*/
int get_extreg_ind(X86Reg reg) {
for (unsigned int i = 0; i < 8 * 4 /* size of extreg_lookup_table */; i++) {
if (extreg_lookup_table[i].curr_reg == reg) {
return i;
}
}
return -1;
}
/**
* \brief Get the value of a register \p reg, in a "smart" way
* Just use the `x86_il_get_reg` macro whenever you need to get the
* value of a register. This function will take care of all the casting
* and extracting of (smaller) registers.
*
* \param reg
* \param bits bitness
* \param pc Program counter
*/
RZ_IPI RzILOpPure *x86_il_get_reg_bits(X86Reg reg, int bits, uint64_t pc) {
if (is_pc_reg(reg)) {
return UN(bits, pc);
}
int ind = -1;
if (x86_il_is_gpr(reg)) {
struct gpr_lookup_helper_t entry = gpr_lookup_table[reg];
/* Need to use `get_bitness_reg` because not all registers
are available in the IL in any particular bitness
(For example, "rax" is not a valid IL variable in 32-bit mode)
So, we need to use the max width register available */
return entry.get_handler(get_bitness_reg(entry.index, bits), bits);
} else if ((ind = get_extreg_ind(reg)) != -1 && bits == 64) {
struct extreg_lookup_helper_t entry = extreg_lookup_table[ind];
return entry.get_handler(entry.base_reg, bits);
}
return VARG(x86_registers[reg]);
}
/**
* \brief Set the value of a register \p reg, in a "smart" way
* Just use the `x86_il_set_reg` macro whenever you need to set the
* value of a register. This function will take care of all the casting
* and storing and compositing values in case of (smaller) registers.
*
* \param reg
* \param val Value to be stored
* \param bits bitness
*/
RZ_IPI RzILOpEffect *x86_il_set_reg_bits(X86Reg reg, RZ_OWN RZ_NONNULL RzILOpPure *val, int bits) {
rz_return_val_if_fail(val, NULL);
int ind = -1;
if (x86_il_is_gpr(reg)) {
struct gpr_lookup_helper_t entry = gpr_lookup_table[reg];
return entry.set_handler(get_bitness_reg(entry.index, bits), val, bits);
} else if ((ind = get_extreg_ind(reg)) != -1 && bits == 64) {
struct extreg_lookup_helper_t entry = extreg_lookup_table[ind];
return entry.set_handler(entry.base_reg, UNSIGNED(64, val), bits);
}
return SETG(x86_registers[reg], val);
}
/**
* \brief Get the memory address as an `RzILOpPure` from X86Mem \p mem
* You can also optionally provide a custom segment register as \p segment
* This function takes care of all casting and conversion
* This has partial segmentation support as of now
*
* You probably wouldn't need to use it directly, consider using the wrappers
* `x86_il_get_memaddr` and `x86_il_set_memaddr`
*
* \param mem
* \param segment
* \param bits bitness
*/
RZ_IPI RzILOpPure *x86_il_get_memaddr_segment_bits(X86Mem mem, X86Reg segment, int bits, ut64 pc) {
RzILOpPure *offset = NULL;
if (mem.base != X86_REG_NONE) {
offset = x86_il_get_reg_bits(mem.base, bits, pc);
if (x86_il_get_reg_size(mem.base) != bits) {
offset = UNSIGNED(bits, offset);
}
}
if (mem.index != X86_REG_NONE) {
RzILOpPure *reg = x86_il_get_reg_bits(mem.index, bits, pc);
if (x86_il_get_reg_size(mem.index) != bits) {
reg = UNSIGNED(bits, reg);
}
if (!offset) {
offset = MUL(reg, UN(bits, mem.scale));
} else {
offset = ADD(offset, MUL(reg, UN(bits, mem.scale)));
}
}
if (!offset) {
offset = UN(bits, mem.disp.value);
} else {
offset = ADD(offset, UN(bits, mem.disp.value));
}
/* Segmentation not present in x86-64 */
if (bits != 64 && segment != X86_REG_NONE) {
// TODO: Implement segmentation
/* Currently the segmentation is only implemented for real mode
Address = Segment * 0x10 + Offset */
/* Assuming real mode */
if (segment != X86_REG_DS) {
offset = ADD(offset, SHIFTL0(UNSIGNED(bits, x86_il_get_reg_bits(segment, bits, pc)), U8(4)));
}
}
return offset;
}
RZ_IPI RzILOpPure *x86_il_get_memaddr_bits(X86Mem mem, int bits, ut64 pc) {
return x86_il_get_memaddr_segment_bits(mem, mem.segment, bits, pc);
}
RZ_IPI RzILOpEffect *x86_il_set_mem_bits(X86Mem mem, RZ_OWN RZ_NONNULL RzILOpPure *val, int bits, ut64 pc) {
rz_return_val_if_fail(val, NULL);
return STOREW(x86_il_get_memaddr_bits(mem, bits, pc), val);
}
/**
* \brief Get the value of the operand \p op
* This function takes care of everything, like choosing
* the correct type and returning the correct value
* Use the wrapper `x86_il_get_op`
*
* \param op
* \param analysis_bits bitness
*/
RZ_IPI RzILOpPure *x86_il_get_operand_bits(X86Op op, int analysis_bits, ut64 pc, int implicit_size, const X86ILIns *ins) {
switch (op.type) {
case X86_OP_REG:
return x86_il_get_reg_bits(op.reg.value, analysis_bits, pc);
case X86_OP_IMM:
/* Immediate values are always sign extended */
return SN((op.size != 0 ? op.size : implicit_size) * BITS_PER_BYTE, imm_value(op, pc));
case X86_OP_MEM:
return LOADW((op.size != 0 ? op.size : implicit_size) * BITS_PER_BYTE, x86_il_get_memaddr_bits(op.mem, analysis_bits, pc));
case X86_OP_PTR: {
RzILOpPure *offset = UN(analysis_bits, op.ptr.offset);
RzILOpPure *segment = UN(analysis_bits, op.ptr.segment);
offset = ADD(offset, SHIFTL0(UNSIGNED(analysis_bits, segment), U8(4)));
return LOADW((op.size != 0 ? op.size : implicit_size) * BITS_PER_BYTE, offset);
}
default:
return NULL;
}
}
/**
* \brief Get the value of the operand \p op
* This function takes care of everything, like choosing
* the correct type and setting the correct value
* Use the wrapper `x86_il_set_op`
*
* \param op
* \param analysis_bits bitness
*/
RZ_IPI RzILOpEffect *x86_il_set_operand_bits(X86Op op, RZ_OWN RZ_NONNULL RzILOpPure *val, int bits, ut64 pc) {
rz_return_val_if_fail(val, NULL);
RzILOpEffect *ret = NULL;
switch (op.type) {
case X86_OP_REG:
ret = x86_il_set_reg_bits(op.reg.value, val, bits);
break;
case X86_OP_MEM:
ret = x86_il_set_mem_bits(op.mem, val, bits, pc);
break;
case X86_OP_IMM:
RZ_LOG_ERROR("x86: RzIL: Cannot set an immediate operand\n");
break;
default:
RZ_LOG_ERROR("x86: RzIL: Invalid param type encountered\n");
break;
}
return ret;
}
/**
* \brief Return the carry bit when \p x and \p y are added, with result \p res
*
* \param res
* \param x
* \param y
*/
RZ_IPI RzILOpBool *x86_il_is_add_carry(RZ_OWN RZ_NONNULL RzILOpPure *res, RZ_OWN RZ_NONNULL RzILOpPure *x, RZ_OWN RZ_NONNULL RzILOpPure *y) {
rz_return_val_if_fail(res && x && y, NULL);
// res = x + y
RzILOpBool *xmsb = MSB(x);
RzILOpBool *ymsb = MSB(y);
RzILOpBool *resmsb = MSB(res);
// x & y
RzILOpBool *xy = AND(xmsb, ymsb);
RzILOpBool *nres = INV(resmsb);
// !res & y
RzILOpBool *ry = AND(nres, DUP(ymsb));
// x & !res
RzILOpBool *xr = AND(DUP(xmsb), DUP(nres));
// bit = xy | ry | xr
RzILOpBool * or = OR(xy, ry);
or = OR(or, xr);
return or ;
}
/**
* \brief Return the borrow bit when \p y is subtracted from \p x, with result \p res
*
* \param res
* \param x
* \param y
*/
RZ_IPI RzILOpBool *x86_il_is_sub_borrow(RZ_OWN RZ_NONNULL RzILOpPure *res, RZ_OWN RZ_NONNULL RzILOpPure *x, RZ_OWN RZ_NONNULL RzILOpPure *y) {
rz_return_val_if_fail(res && x && y, NULL);
// res = x - y
RzILOpBool *xmsb = MSB(x);
RzILOpBool *ymsb = MSB(y);
RzILOpBool *resmsb = MSB(res);
// !x & y
RzILOpBool *nx = INV(xmsb);
RzILOpBool *nxy = AND(nx, ymsb);
// y & res
RzILOpBool *rny = AND(DUP(ymsb), resmsb);
// res & !x
RzILOpBool *rnx = AND(DUP(resmsb), DUP(nx));
// bit = nxy | rny | rnx
RzILOpBool * or = OR(nxy, rny);
or = OR(or, rnx);
return or ;
}
/**
* \brief Return the overflow bit when \p x and \p y are added, with result \p res
*
* \param res
* \param x
* \param y
*/
RZ_IPI RzILOpBool *x86_il_is_add_overflow(RZ_OWN RZ_NONNULL RzILOpPure *res, RZ_OWN RZ_NONNULL RzILOpPure *x, RZ_OWN RZ_NONNULL RzILOpPure *y) {
rz_return_val_if_fail(res && x && y, NULL);
// res = x + y
RzILOpBool *xmsb = MSB(x);
RzILOpBool *ymsb = MSB(y);
RzILOpBool *resmsb = MSB(res);
// !res & x & y
RzILOpBool *nrxy = AND(AND(INV(resmsb), xmsb), ymsb);
// res & !x & !y
RzILOpBool *rnxny = AND(AND(DUP(resmsb), INV(DUP(xmsb))), INV(DUP(ymsb)));
// or = nrxy | rnxny
RzILOpBool * or = OR(nrxy, rnxny);
return or ;
}
/**
* \brief Return the underflow bit when \p y is subtracted from \p x, with result \p res
*
* \param res
* \param x
* \param y
*/
RZ_IPI RzILOpBool *x86_il_is_sub_underflow(RZ_OWN RZ_NONNULL RzILOpPure *res, RZ_OWN RZ_NONNULL RzILOpPure *x, RZ_OWN RZ_NONNULL RzILOpPure *y) {
rz_return_val_if_fail(res && x && y, NULL);
// res = x - y
RzILOpBool *xmsb = MSB(x);
RzILOpBool *ymsb = MSB(y);
RzILOpBool *resmsb = MSB(res);
// !res & x & !y
RzILOpBool *nrxny = AND(AND(INV(resmsb), xmsb), INV(ymsb));
// res & !x & y
RzILOpBool *rnxy = AND(AND(DUP(resmsb), INV(DUP(xmsb))), DUP(ymsb));
// or = nrxny | rnxy
RzILOpBool * or = OR(nrxny, rnxy);
return or ;
}
/**
* \brief Find the parity of lower 8 bits of \p val
*
* \param val
*/
RzILOpBool *x86_il_get_parity(RZ_OWN RzILOpPure *val) {
// assumed that val is an 8-bit wide value
// A parity calculation can be reduced to nested shift-right and xor operations.
// see https://github.com/BinaryAnalysisPlatform/bap/blob/master/plugins/x86/x86_lifter.ml#L215
RzILOpPure *accumulate = LET("_val", val,
LET("_c4", LOGXOR(VARLP("_val"), SHIFTR0(VARLP("_val"), U8(4))),
LET("_c2", LOGXOR(VARLP("_c4"), SHIFTR0(VARLP("_c4"), U8(2))),
LOGXOR(VARLP("_c2"), SHIFTR0(VARLP("_c2"), U8(1))))));
return INV(LSB(accumulate));
}
/**
* \brief Sets the value of PF, ZF, SF according to the \p result
*/
RZ_IPI RzILOpEffect *x86_il_set_result_flags_bits(RZ_OWN RZ_NONNULL RzILOpPure *result, int bits) {
rz_return_val_if_fail(result, NULL);
RzILOpEffect *set = SETL("_result", result);
RzILOpBool *pf = x86_il_get_parity(UNSIGNED(8, VARL("_result")));
RzILOpBool *zf = IS_ZERO(VARL("_result"));
RzILOpBool *sf = MSB(VARL("_result"));
return SEQ4(set,
SETG(EFLAGS(PF), pf),
SETG(EFLAGS(ZF), zf),
SETG(EFLAGS(SF), sf));
}
/**
* \brief Sets the value of CF, OF, AF according to the \p res
*/
RZ_IPI RzILOpEffect *x86_il_set_arithmetic_flags_bits(RZ_OWN RZ_NONNULL RzILOpPure *res, RZ_OWN RZ_NONNULL RzILOpPure *x, RZ_OWN RZ_NONNULL RzILOpPure *y, bool addition, int bits) {
rz_return_val_if_fail(res && x && y, NULL);
RzILOpBool *cf = NULL;
RzILOpBool *of = NULL;
RzILOpBool *af = NULL;
RzILOpEffect *result_set = SETL("_result", res);
RzILOpEffect *x_set = SETL("_x", x);
RzILOpEffect *y_set = SETL("_y", y);
if (addition) {
cf = x86_il_is_add_carry(VARL("_result"), VARL("_x"), VARL("_y"));
of = x86_il_is_add_overflow(VARL("_result"), VARL("_x"), VARL("_y"));
af = x86_il_is_add_carry(UNSIGNED(4, VARL("_result")), UNSIGNED(4, VARL("_x")), UNSIGNED(4, VARL("_y")));
} else {
cf = x86_il_is_sub_borrow(VARL("_result"), VARL("_x"), VARL("_y"));
of = x86_il_is_sub_underflow(VARL("_result"), VARL("_x"), VARL("_y"));
af = x86_il_is_sub_borrow(UNSIGNED(4, VARL("_result")), UNSIGNED(4, VARL("_x")), UNSIGNED(4, VARL("_y")));
}
return SEQ6(result_set, x_set, y_set,
SETG(EFLAGS(CF), cf),
SETG(EFLAGS(OF), of),
SETG(EFLAGS(AF), af));
}
/**
* \brief Set OF and AF according to \p res
*/
RZ_IPI RzILOpEffect *x86_il_set_arithmetic_flags_except_cf_bits(RZ_OWN RZ_NONNULL RzILOpPure *res, RZ_OWN RZ_NONNULL RzILOpPure *x, RZ_OWN RZ_NONNULL RzILOpPure *y, bool addition, int bits) {
rz_return_val_if_fail(res && x && y, NULL);
RzILOpBool *of = NULL;
RzILOpBool *af = NULL;
RzILOpEffect *result_set = SETL("_result", res);
RzILOpEffect *x_set = SETL("_x", x);
RzILOpEffect *y_set = SETL("_y", y);
if (addition) {
of = x86_il_is_add_overflow(VARL("_result"), VARL("_x"), VARL("_y"));
af = x86_il_is_add_carry(UNSIGNED(4, VARL("_result")), UNSIGNED(4, VARL("_x")), UNSIGNED(4, VARL("_y")));
} else {
of = x86_il_is_sub_underflow(VARL("_result"), VARL("_x"), VARL("_y"));
af = x86_il_is_sub_borrow(UNSIGNED(4, VARL("_result")), UNSIGNED(4, VARL("_x")), UNSIGNED(4, VARL("_y")));
}
return SEQ5(result_set, x_set, y_set,
SETG(EFLAGS(OF), of),
SETG(EFLAGS(AF), af));
}
/**
* \brief Get value of FLAGS register
*
* \param size size of flags needed
*/
RZ_IPI RzILOpPure *x86_il_get_flags(unsigned int size) {
/* We really don't care about bits higher than 16 for now */
RzILOpPure *val;
if (size == 8) {
goto lower_half;
}
/* Bit 15: Reserved,
always 1 on 8086 and 186,
always 0 on later models
Assuming 0 */
val = BOOL_TO_BV(IL_FALSE, size);
val = LOGOR(SHIFTL0(val, UN(size, 1)), BOOL_TO_BV(VARG(EFLAGS(NT)), size));
/** Bit 12-13: IOPL,
I/O privilege level (286+ only),
always 1 on 8086 and 186
Assuming all 1 */
val = LOGOR(SHIFTL0(val, UN(size, 2)), UN(size, 0x3));
val = LOGOR(SHIFTL0(val, UN(size, 1)), BOOL_TO_BV(VARG(EFLAGS(OF)), size));
val = LOGOR(SHIFTL0(val, UN(size, 1)), BOOL_TO_BV(VARG(EFLAGS(DF)), size));
val = LOGOR(SHIFTL0(val, UN(size, 1)), BOOL_TO_BV(VARG(EFLAGS(IF)), size));
val = LOGOR(SHIFTL0(val, UN(size, 1)), BOOL_TO_BV(VARG(EFLAGS(TF)), size));
lower_half:
if (size == 8) {
val = BOOL_TO_BV(VARG(EFLAGS(SF)), size);
} else {
val = LOGOR(SHIFTL0(val, UN(size, 1)), BOOL_TO_BV(VARG(EFLAGS(ZF)), size));
}
val = LOGOR(SHIFTL0(val, UN(size, 1)), BOOL_TO_BV(VARG(EFLAGS(ZF)), size));
val = LOGOR(SHIFTL0(val, UN(size, 2)), BOOL_TO_BV(VARG(EFLAGS(AF)), size));
val = LOGOR(SHIFTL0(val, UN(size, 2)), BOOL_TO_BV(VARG(EFLAGS(PF)), size));
val = LOGOR(SHIFTL0(val, UN(size, 1)), UN(size, 1));
val = LOGOR(SHIFTL0(val, UN(size, 1)), BOOL_TO_BV(VARG(EFLAGS(CF)), size));
return val;
}
/**
* \brief Set the value of flags register
*
* \param val value to set the FLAGS register to
* \param size size of \p val
*/
RZ_IPI RzILOpEffect *x86_il_set_flags(RZ_OWN RZ_NONNULL RzILOpPure *val, unsigned int size) {
rz_return_val_if_fail(val, NULL);
RzILOpEffect *set_val = SETL("_flags", val);
RzILOpEffect *eff = SETG(EFLAGS(CF), LSB(VARL("_flags")));
eff = SEQ2(eff, SETL("_flags", SHIFTR0(VARL("_flags"), U8(2))));
eff = SEQ2(eff, SETG(EFLAGS(PF), LSB(VARL("_flags"))));
eff = SEQ2(eff, SETL("_flags", SHIFTR0(VARL("_flags"), U8(2))));
eff = SEQ2(eff, SETG(EFLAGS(AF), LSB(VARL("_flags"))));
eff = SEQ2(eff, SETL("_flags", SHIFTR0(VARL("_flags"), U8(2))));
eff = SEQ2(eff, SETG(EFLAGS(ZF), LSB(VARL("_flags"))));
eff = SEQ2(eff, SETL("_flags", SHIFTR0(VARL("_flags"), U8(1))));
eff = SEQ2(eff, SETG(EFLAGS(SF), LSB(VARL("_flags"))));
if (size == 8) {
return SEQ2(set_val, eff);
}
eff = SEQ2(eff, SETL("_flags", SHIFTR0(VARL("_flags"), U8(1))));
eff = SEQ2(eff, SETG(EFLAGS(TF), LSB(VARL("_flags"))));
eff = SEQ2(eff, SETL("_flags", SHIFTR0(VARL("_flags"), U8(1))));
eff = SEQ2(eff, SETG(EFLAGS(IF), LSB(VARL("_flags"))));
eff = SEQ2(eff, SETL("_flags", SHIFTR0(VARL("_flags"), U8(1))));
eff = SEQ2(eff, SETG(EFLAGS(DF), LSB(VARL("_flags"))));
eff = SEQ2(eff, SETL("_flags", SHIFTR0(VARL("_flags"), U8(1))));
eff = SEQ2(eff, SETG(EFLAGS(OF), LSB(VARL("_flags"))));
eff = SEQ2(eff, SETL("_flags", SHIFTR0(VARL("_flags"), U8(3))));
eff = SEQ2(eff, SETG(EFLAGS(NT), LSB(VARL("_flags"))));
/* Again, we will be ignoring bits over 16 and also ignore IOPL */
return SEQ2(set_val, eff);
}
/**
* \brief Check whether \p reg is an FPU stack register (ST0 - ST7)
*
* \param reg
*/
RZ_IPI bool x86_il_is_st_reg(X86Reg reg) {
return reg >= X86_REG_ST0 && reg <= X86_REG_ST7;
}
/**
* \brief Get the 11th and 12th bit which stores the rounding mode from the FPU
* control word
*
* \return RzILOpPure* 2 bit rounding mode
*/
RZ_IPI RzILOpPure *x86_il_fpu_get_rmode() {
return UNSIGNED(2, SHIFTR0(VARG(X86_REG_FPU_CW), UN(8, 10)));
}
/**
* \brief Get the float stored in FPU stack \p reg
*
* \param reg
* \return RzILOpFloat* IEEE754 80 bit float
*/
RZ_IPI RzILOpFloat *x86_il_get_st_reg(X86Reg reg) {
rz_return_val_if_fail(x86_il_is_st_reg(reg), NULL);
return BV2F(RZ_FLOAT_IEEE754_BIN_80, VARG(x86_registers[reg]));
}
/**
* \brief Set the local variable to the value of "_rmode" computed using control
* word bits
*
* \return RzILOpEffect*
*/
RZ_IPI RzILOpEffect *init_rmode() {
return SETL("_rmode", x86_il_fpu_get_rmode());
}
/**
* \brief Execute the function \p f with the correct op mode argument
*
* \param f function which takes in the rounding mode as the first argument
*
* 0 -> RNE
* 1 -> RTN
* 2 -> RTP
* 3 -> RTZ
*
* I hate this, but this is the only way to conditionally use the correct rmode.
*/
#define EXEC_WITH_RMODE(f, ...) \
ITE(EQ(VARL("_rmode"), UN(2, 0)), f(RZ_FLOAT_RMODE_RNE, __VA_ARGS__), \
ITE(EQ(VARL("_rmode"), UN(2, 1)), f(RZ_FLOAT_RMODE_RTN, __VA_ARGS__), \
ITE(EQ(VARL("_rmode"), UN(2, 2)), f(RZ_FLOAT_RMODE_RTP, __VA_ARGS__), \
f(RZ_FLOAT_RMODE_RTZ, __VA_ARGS__))))
RzILOpFloat *resize_floating_helper(RzFloatRMode rmode, RzFloatFormat format, RzILOpFloat *val) {
return FCONVERT(format, rmode, val);
}
/**
* \brief Resize the float \p val to \p width
* You need to have initialized a local variable "_rmode" set with the rounding
* mode before you call this function.
*
* \param val Desirable that it is a small expression since it will be duped
* \param format Output float format
* \param ctx use_rmode gets set to true
* \return RzILOpFloat*
*/
RZ_IPI ILPureEffectPair x86_il_resize_floating_ctx(RZ_OWN RZ_NONNULL RzILOpFloat *val, RzFloatFormat format, RZ_BORROW RZ_NONNULL X86ILContext *ctx) {
ILPureEffectPair ret = { .val = NULL, .eff = NULL };
rz_return_val_if_fail(val && ctx, ret);
ctx->use_rmode = true;
ret.eff = SETL("f_val_rm", val);
ret.val = EXEC_WITH_RMODE(resize_floating_helper, format, VARL("f_val_rm"));
return ret;
}
RzILOpFloat *sint2f_floating_helper(RzFloatRMode rmode, RzFloatFormat format, RzILOpBitVector *val) {
return SINT2F(format, rmode, val);
}
/**
* \brief Convert the integer \p int_val to a RzILOpFloat of format \p fmt
* You need to have initialized a local variable "_rmode" set with the rounding
* mode before you call this function.
*
* \param int_val Desirable that it is a small expression since it will be duped
* \param format Output float format
* \param ctx use_rmode gets set to true
* \return RzILOpFloat*
*/
RZ_IPI ILPureEffectPair x86_il_floating_from_int_ctx(RZ_OWN RZ_NONNULL RzILOpBitVector *int_val, RzFloatFormat format, RZ_BORROW RZ_NONNULL X86ILContext *ctx) {
ILPureEffectPair ret = { .val = NULL, .eff = NULL };
rz_return_val_if_fail(int_val && ctx, ret);
ctx->use_rmode = true;
ret.eff = SETL("i_val_rm", int_val);
ret.val = EXEC_WITH_RMODE(sint2f_floating_helper, format, VARL("i_val_rm"));
return ret;
}
RzILOpFloat *f2sint_floating_helper(RzFloatRMode rmode, ut32 width, RzILOpFloat *val) {
return F2SINT(width, rmode, val);
}
/**
* \brief Convert the floating \p float_val to a RzILOpBitVector of size \p width
* You need to have initialized a local variable "_rmode" set with the rounding
* mode before you call this function.
*
* \param float_val Desirable that it is a small expression since it will be duped
* \param width Output bitvector width
* \param ctx use_rmode gets set to true
* \return RzILOpBitVector*
*/
RZ_IPI ILPureEffectPair x86_il_int_from_floating_ctx(RZ_OWN RZ_NONNULL RzILOpFloat *float_val, ut32 width, RZ_BORROW RZ_NONNULL X86ILContext *ctx) {
ILPureEffectPair ret = { .val = NULL, .eff = NULL };
rz_return_val_if_fail(float_val && ctx, ret);
ctx->use_rmode = true;
ret.eff = SETL("f_val_rm", float_val);
ret.val = EXEC_WITH_RMODE(f2sint_floating_helper, width, VARL("f_val_rm"));
return ret;
}
/**
* \brief Add \p x and \p y with the correct rounding mode as determined from
* the FPU control word
*
* \param x Desirable that it is a small expression since it will be duped
* \param y Desirable that it is a small expression since it will be duped
* \param ctx use_rmode gets set to true
* \return RzILOpFloat* sum
*/
RZ_IPI ILPureEffectPair x86_il_fadd_with_rmode_ctx(RZ_OWN RZ_NONNULL RzILOpFloat *x, RZ_OWN RZ_NONNULL RzILOpFloat *y, RZ_BORROW RZ_NONNULL X86ILContext *ctx) {
ILPureEffectPair ret = { .val = NULL, .eff = NULL };
rz_return_val_if_fail(x && y && ctx, ret);
ctx->use_rmode = true;
ret.eff = SEQ2(SETL("x_rm", x), SETL("y_rm", y));
ret.val = EXEC_WITH_RMODE(FADD, VARL("x_rm"), VARL("y_rm"));
return ret;
}
/**
* \brief Multiply \p x and \p y with the correct rounding mode as determined
* from the FPU control word
*
* \param x Desirable that it is a small expression since it will be duped
* \param y Desirable that it is a small expression since it will be duped
* \param ctx use_rmode gets set to true
* \return RzILOpFloat* product
*/
RZ_IPI ILPureEffectPair x86_il_fmul_with_rmode_ctx(RZ_OWN RZ_NONNULL RzILOpFloat *x, RZ_OWN RZ_NONNULL RzILOpFloat *y, RZ_BORROW RZ_NONNULL X86ILContext *ctx) {
ILPureEffectPair ret = { .val = NULL, .eff = NULL };
rz_return_val_if_fail(x && y && ctx, ret);
ctx->use_rmode = true;
ret.eff = SEQ2(SETL("x_rm", x), SETL("y_rm", y));
ret.val = EXEC_WITH_RMODE(FMUL, VARL("x_rm"), VARL("y_rm"));
return ret;
}
/**
* \brief Subtract \p x from \p y with the correct rounding mode as determined
* from the FPU control word
*
* \param x Desirable that it is a small expression since it will be duped
* \param y Desirable that it is a small expression since it will be duped
* \param ctx use_rmode gets set to true
* \return RzILOpFloat* difference
*/
RZ_IPI ILPureEffectPair x86_il_fsub_with_rmode_ctx(RZ_OWN RZ_NONNULL RzILOpFloat *x, RZ_OWN RZ_NONNULL RzILOpFloat *y, RZ_BORROW RZ_NONNULL X86ILContext *ctx) {
ILPureEffectPair ret = { .val = NULL, .eff = NULL };
rz_return_val_if_fail(x && y && ctx, ret);
ctx->use_rmode = true;
ret.eff = SEQ2(SETL("x_rm", x), SETL("y_rm", y));
// y - x, hence y is the first argument
ret.val = EXEC_WITH_RMODE(FSUB, VARL("y_rm"), VARL("x_rm"));
return ret;
}
/**
* \brief Subtract \p y from \p x (reverse of \ref x86_il_fsub_with_rmode)
*/
RZ_IPI ILPureEffectPair x86_il_fsubr_with_rmode_ctx(RZ_OWN RZ_NONNULL RzILOpFloat *x, RZ_OWN RZ_NONNULL RzILOpFloat *y, RZ_BORROW RZ_NONNULL X86ILContext *ctx) {
rz_return_val_if_fail(x && y && ctx, ((ILPureEffectPair){ .val = NULL, .eff = NULL }));
return x86_il_fsub_with_rmode(y, x);
}
/**
* \brief Divide \p x from \p y with the correct rounding mode as determined
* from the FPU control word
*
* \param x Desirable that it is a small expression since it will be duped
* \param y Desirable that it is a small expression since it will be duped
* \param ctx use_rmode gets set to true
* \return RzILOpFloat* division
*/
RZ_IPI ILPureEffectPair x86_il_fdiv_with_rmode_ctx(RZ_OWN RZ_NONNULL RzILOpFloat *x, RZ_OWN RZ_NONNULL RzILOpFloat *y, RZ_BORROW RZ_NONNULL X86ILContext *ctx) {
ILPureEffectPair ret = { .val = NULL, .eff = NULL };
rz_return_val_if_fail(x && y && ctx, ret);
ctx->use_rmode = true;
ret.eff = SEQ2(SETL("x_rm", x), SETL("y_rm", y));
ret.val = EXEC_WITH_RMODE(FDIV, VARL("x_rm"), VARL("y_rm"));
return ret;
}
/**
* \brief Divide \p y from \p x (reverse of \ref x86_il_fdiv_with_rmode)
*/
RZ_IPI ILPureEffectPair x86_il_fdivr_with_rmode_ctx(RZ_OWN RZ_NONNULL RzILOpFloat *x, RZ_OWN RZ_NONNULL RzILOpFloat *y, RZ_BORROW RZ_NONNULL X86ILContext *ctx) {
rz_return_val_if_fail(x && y && ctx, ((ILPureEffectPair){ .val = NULL, .eff = NULL }));
return x86_il_fdiv_with_rmode(y, x);
}
/**
* \brief Calculate the square root of \p x with the correct rounding mode as determined
* from the FPU control word
*
* \param x Desirable that it is a small expression since it will be duped
* \param ctx use_rmode gets set to true
* \return RzILOpFloat* square root
*/
RZ_IPI ILPureEffectPair x86_il_fsqrt_with_rmode_ctx(RZ_OWN RZ_NONNULL RzILOpFloat *x, RZ_BORROW RZ_NONNULL X86ILContext *ctx) {
ILPureEffectPair ret = { .val = NULL, .eff = NULL };
rz_return_val_if_fail(x && ctx, ret);
ctx->use_rmode = true;
ret.eff = SETL("x_rm", x);
ret.val = EXEC_WITH_RMODE(FSQRT, VARL("x_rm"));
return ret;
}
/**
* \brief Store a float \p val at FPU stack \p reg
*
* \param reg
* \param val
* \param val_format Format of \p val
* \param ctx use_rmode gets set to true if any resizing of \p val is required
* \return RzILOpFloat*
*/
RZ_IPI RzILOpEffect *x86_il_set_st_reg_ctx(X86Reg reg, RZ_OWN RZ_NONNULL RzILOpFloat *val, RzFloatFormat val_format, RZ_BORROW X86ILContext *ctx) {
rz_return_val_if_fail(val && x86_il_is_st_reg(reg), NULL);
if (val_format == RZ_FLOAT_IEEE754_BIN_80) {
return SETG(x86_registers[reg], F2BV(val));
} else {
ILPureEffectPair converted_val = x86_il_resize_floating(val, RZ_FLOAT_IEEE754_BIN_80);
return SEQ2(converted_val.eff, SETG(x86_registers[reg], F2BV(converted_val.val)));
}
}
/**
* \brief Get the stack TOP stored in the FPU status word.
* TOP = FPU[12:15] (bits 12, 13 & 14)
* 12th bit is the least significant bit.
*
* \return RzILOpPure* Bitvector of length 3
*/
RZ_IPI RzILOpPure *x86_il_get_fpu_stack_top() {
RzILOpPure *status_word = x86_il_get_reg_bits(X86_REG_X87STATUS, 0, 0);
return UNSIGNED(3, SHIFTR0(status_word, UN(8, 11)));
}
/**
* \brief Set the value of FPU status word.
* See \ref x86_il_get_fpu_stack_top() for the structure of FPU status word and
* stack TOP.
*
* \param top Value to be stored as the new TOP (bitvector length = 3)
* \return RzILOpEffect*
*/
RZ_IPI RzILOpEffect *x86_il_set_fpu_stack_top(RZ_OWN RZ_NONNULL RzILOpPure *top) {
rz_return_val_if_fail(top, NULL);
RzILOpPure *shifted_top = SHIFTL0(UNSIGNED(16, top), UN(8, 11));
/* 0x3800 only has the 12, 13 & 14 bits set, so we take its negation for the
* mask. */
RzILOpPure *mask = UN(16, ~(0x3800));
RzILOpPure *new_fpsw = LOGOR(shifted_top, LOGAND(mask, x86_il_get_reg_bits(X86_REG_X87STATUS, 0, 0)));
return x86_il_set_reg_bits(X86_REG_X87STATUS, new_fpsw, 0);
}
#define ST_MOVE_RIGHT(l, r) x86_il_set_st_reg(X86_REG_ST##r, x86_il_get_st_reg(X86_REG_ST##l), RZ_FLOAT_IEEE754_BIN_80)
/**
* \brief Push \p val on the FPU stack
*
* \param val
* \param val_format Format of \p val
* \param ctx use_rmode gets set to true if any resizing of \p val is required
* \return RzILOpEffect* Push effect
*/
RZ_IPI RzILOpEffect *x86_il_st_push_ctx(RZ_OWN RZ_NONNULL RzILOpFloat *val, RzFloatFormat val_format, RZ_BORROW X86ILContext *ctx) {
rz_return_val_if_fail(val, NULL);
/* No need for a modulo here since the bitvector width will truncate any top
* value > 7 */
RzILOpEffect *set_top = x86_il_set_fpu_stack_top(SUB(x86_il_get_fpu_stack_top(), UN(3, 1)));
RzILOpEffect *st_shift = SEQ8(
ST_MOVE_RIGHT(6, 7),
ST_MOVE_RIGHT(5, 6),
ST_MOVE_RIGHT(4, 5),
ST_MOVE_RIGHT(3, 4),
ST_MOVE_RIGHT(2, 3),
ST_MOVE_RIGHT(1, 2),
ST_MOVE_RIGHT(0, 1),
x86_il_set_st_reg(X86_REG_ST0, val, val_format));
/* Set C1 if stack overflow. If stack overflow occurred, then the value of
* stack TOP must be 0x7. */
RzILOpEffect *set_overflow = x86_il_set_fpu_flag(X86_FPU_C1, EQ(x86_il_get_fpu_stack_top(), UN(3, 7)));
return SEQ3(set_top, st_shift, set_overflow);
}
#define ST_MOVE_LEFT(l, r) x86_il_set_st_reg(X86_REG_ST##l, x86_il_get_st_reg(X86_REG_ST##r), RZ_FLOAT_IEEE754_BIN_80)
/**
* \brief Pop a value from the FPU stack
*
* \return RzILOpEffect* Pop effect
*/
RZ_IPI RzILOpEffect *x86_il_st_pop() {
/* We actually don't need a context here because we will never need to resize
* any value. */
X86ILContext *ctx = NULL;
RzILOpEffect *set_top = x86_il_set_fpu_stack_top(ADD(x86_il_get_fpu_stack_top(), UN(3, 1)));
RzILOpEffect *st_shift = SEQ7(
ST_MOVE_LEFT(0, 1),
ST_MOVE_LEFT(1, 2),
ST_MOVE_LEFT(2, 3),
ST_MOVE_LEFT(3, 4),
ST_MOVE_LEFT(4, 5),
ST_MOVE_LEFT(5, 6),
ST_MOVE_LEFT(6, 7));
/* Set C1 if stack underflow. If stack underflow occurred, then the value of
* stack TOP must be 0x0. */
RzILOpEffect *set_underflow = x86_il_set_fpu_flag(X86_FPU_C1, EQ(x86_il_get_fpu_stack_top(), UN(3, 0)));
return SEQ3(set_top, st_shift, set_underflow);
}
RZ_IPI ILPureEffectPair x86_il_st_pop_with_val() {
ILPureEffectPair ret;
ret.val = x86_il_get_st_reg(X86_REG_ST0);
ret.eff = x86_il_st_pop();
return ret;
}
RZ_IPI RzILOpBool *x86_il_get_fpu_flag(X86FPUFlags flag) {
RzILOpPure *shifted_fpsw = SHIFTR0(x86_il_get_reg_bits(X86_REG_X87STATUS, 0, 0), UN(8, flag));
return NON_ZERO(UNSIGNED(1, shifted_fpsw));
}
RZ_IPI RzILOpEffect *x86_il_set_fpu_flag(X86FPUFlags flag, RZ_OWN RZ_NONNULL RzILOpBool *value) {
rz_return_val_if_fail(value, NULL);
RzILOpPure *zero_mask = UN(16, ~(1 << flag));
RzILOpPure *value_mask = SHIFTL0(BOOL_TO_BV(value, 16), UN(8, flag));
RzILOpPure *new_fpsw = LOGOR(value_mask, LOGAND(zero_mask, x86_il_get_reg_bits(X86_REG_X87STATUS, 0, 0)));
return x86_il_set_reg_bits(X86_REG_X87STATUS, new_fpsw, 0);
}
#define FLOATING_OP_MEM_WIDTH_CASE(n) \
do { \
case n: \
return BV2F(RZ_FLOAT_IEEE754_BIN_##n, LOADW(n, x86_il_get_memaddr_bits(op.mem, bits, pc))); \
} while (0)
/**
* \brief Get the value of the floating point operand \p op
* This function takes care of everything, like choosing
* the correct typem returning the correct value and
* converting to the correct FP format
* Use the wrapper `x86_il_get_floating_op`
*
* \param op Operand to get
* \param bits bitness
* \param pc
*/
RZ_IPI RzILOpPure *x86_il_get_floating_operand_bits(X86Op op, int bits, ut64 pc) {
switch (op.type) {
case X86_OP_REG:
if (x86_il_is_st_reg(op.reg.value)) {
return x86_il_get_st_reg(op.reg.value);
} else {
RZ_LOG_ERROR("x86: RzIL: Invalid register passed as a floating point operand: %d\n", op.reg.value);
}
break;
case X86_OP_MEM:
switch (op.size * BITS_PER_BYTE) {
/* ~Duff's~ DMaroo's device */
FLOATING_OP_MEM_WIDTH_CASE(32);
FLOATING_OP_MEM_WIDTH_CASE(64);
FLOATING_OP_MEM_WIDTH_CASE(80);
default:
RZ_LOG_ERROR("x86: RzIL: Invalid memory operand width for a floating point operand: %d\n", op.size);
}
break;
case X86_OP_IMM:
default:
RZ_LOG_ERROR("x86: RzIL: Invalid param type encountered: %d\n", op.type);
}
return NULL;
}
#define FLOAT_WIDTH_TO_FORMAT_SWITCH_CASE(n) \
do { \
case n: \
return RZ_FLOAT_IEEE754_BIN_##n; \
} while (0)
RZ_IPI RzFloatFormat x86_width_to_format(ut8 width) {
switch (width) {
FLOAT_WIDTH_TO_FORMAT_SWITCH_CASE(32);
FLOAT_WIDTH_TO_FORMAT_SWITCH_CASE(64);
FLOAT_WIDTH_TO_FORMAT_SWITCH_CASE(80);
FLOAT_WIDTH_TO_FORMAT_SWITCH_CASE(128);
default:
rz_warn_if_reached();
return RZ_FLOAT_UNK;
}
}
#define FLOAT_FORMAT_TO_WIDTH_SWITCH_CASE(n) \
do { \
case RZ_FLOAT_IEEE754_BIN_##n: \
return n; \
} while (0)
RZ_IPI ut8 x86_format_to_width(RzFloatFormat format) {
return rz_float_get_format_info(format, RZ_FLOAT_INFO_TOTAL_LEN);
}
/**
* \brief Set the value of the floating point operand \p op
* This function takes care of everything, like choosing
* the correct type, setting the correct value and
* converting to the correct FP format
* Use the wrapper `x86_il_set_floating_op`
*
* \param op Operand to be set
* \param val Value to be used
* \param val_format Format of \p val
* \param bits Bitness
* \param pc
* \param ctx use_rmode gets set to true if any resizing of \p val is required
*/
RZ_IPI RzILOpEffect *x86_il_set_floating_operand_bits_ctx(X86Op op, RZ_OWN RZ_NONNULL RzILOpFloat *val, RzFloatFormat val_format, int bits, ut64 pc, RZ_BORROW X86ILContext *ctx) {
rz_return_val_if_fail(val, NULL);
RzILOpEffect *ret = NULL;
switch (op.type) {
case X86_OP_REG:
return x86_il_set_st_reg(op.reg.value, val, val_format);
case X86_OP_MEM: {
ut64 required_format = x86_width_to_format(op.size * BITS_PER_BYTE);
RzILOpPure *resized_val;
RzILOpEffect *ret = NULL;
if (required_format == val_format) {
ILPureEffectPair resized = x86_il_resize_floating(val, required_format);
resized_val = resized.val;
ret = resized.eff;
} else {
resized_val = val;
}
RzILOpEffect *set_bits = x86_il_set_mem_bits(op.mem, F2BV(resized_val), bits, pc);
if (!ret) {
ret = set_bits;
} else {
ret = SEQ2(ret, set_bits);
}
return ret;
}
case X86_OP_IMM:
default:
RZ_LOG_ERROR("x86: RzIL: Invalid param type encountered: %d\n", X86_OP_IMM);
return ret;
}
}
RZ_IPI RzILOpEffect *x86_il_clear_fpsw_flags() {
RzILOpPure *new_fpsw = LOGAND(x86_il_get_reg_bits(X86_REG_X87STATUS, 0, 0), UN(16, 0x3f80));
return x86_il_set_reg_bits(X86_REG_X87STATUS, new_fpsw, 0);
}
#include <rz_il/rz_il_opbuilder_end.h>