rizin/librz/arch/isa/ppc/ppc_il.c

1119 lines
No EOL
30 KiB
C

// SPDX-FileCopyrightText: 2022 Rot127 <rot127@posteo.com>
// SPDX-License-Identifier: LGPL-3.0-only
#include "ppc_il.h"
#include "capstone.h"
#include "ppc_analysis.h"
#include <capstone/ppc.h>
#include <rz_il/rz_il_opcodes.h>
#include <rz_util/rz_log.h>
#include <rz_util/rz_assert.h>
#include <rz_endian.h>
#include <rz_analysis.h>
#include <rz_il.h>
#include <rz_types.h>
RZ_IPI RzAnalysisILConfig *rz_ppc_cs_64_il_config(bool big_endian) {
RzAnalysisILConfig *r = rz_analysis_il_config_new(64, big_endian, 64);
return r;
}
RZ_IPI RzAnalysisILConfig *rz_ppc_cs_32_il_config(bool big_endian) {
RzAnalysisILConfig *r = rz_analysis_il_config_new(32, big_endian, 32);
return r;
}
/**
* \brief Returns true if the given load/store instruction is in X form (uses register RB as second operand).
*
* \param insn_id The instruction id.
* \return bool True if the load/store instruction is in X form. False otherwise.
*/
RZ_IPI bool ppc_is_x_form(ut32 insn_id) {
switch (insn_id) {
default:
return false;
case PPC_INS_LBZUX:
case PPC_INS_LBZX:
case PPC_INS_LDARX:
case PPC_INS_LDBRX:
case PPC_INS_LDUX:
case PPC_INS_LDX:
case PPC_INS_LHAUX:
case PPC_INS_LHAX:
case PPC_INS_LHBRX:
case PPC_INS_LHZUX:
case PPC_INS_LHZX:
case PPC_INS_LWARX:
case PPC_INS_LWAUX:
case PPC_INS_LWAX:
case PPC_INS_LWBRX:
case PPC_INS_LWZUX:
case PPC_INS_LWZX:
case PPC_INS_STBUX:
case PPC_INS_STHUX:
case PPC_INS_STWUX:
case PPC_INS_STDUX:
case PPC_INS_STBX:
case PPC_INS_STHX:
case PPC_INS_STWX:
case PPC_INS_STDX:
case PPC_INS_STDCX:
case PPC_INS_STWCX:
case PPC_INS_LBZCIX:
case PPC_INS_LDCIX:
case PPC_INS_LHZCIX:
case PPC_INS_LWZCIX:
case PPC_INS_STBCIX:
case PPC_INS_STHCIX:
case PPC_INS_STWCIX:
case PPC_INS_STDCIX:
return true;
}
}
/**
* \brief Returns the memory access size in bytes for a given load/store instruction.
*
* \param insn_id The instruction id.
* \return st32 The memory access size in bytes.
* Or 0 if the load instruction does not access the memory.
* Or -1 if the instruction is no load/store instruction.
*/
RZ_IPI st32 ppc_get_mem_acc_size(ut32 insn_id) {
switch (insn_id) {
default:
RZ_LOG_INFO("Memory access size for instruction %d requested. But it is not in the switch case.\n", insn_id);
return -1;
case PPC_INS_LI:
case PPC_INS_LIS:
// Doesn't read from memory.
return 0;
case PPC_INS_LBZ:
case PPC_INS_LBZU:
case PPC_INS_LBZUX:
case PPC_INS_LBZX:
case PPC_INS_STB:
case PPC_INS_STBU:
case PPC_INS_STBUX:
case PPC_INS_STBX:
case PPC_INS_STBCIX:
case PPC_INS_LBZCIX:
return PPC_BYTE;
case PPC_INS_LHA:
case PPC_INS_LHAU:
case PPC_INS_LHAUX:
case PPC_INS_LHAX:
case PPC_INS_LHBRX:
case PPC_INS_LHZ:
case PPC_INS_LHZU:
case PPC_INS_LHZUX:
case PPC_INS_LHZX:
case PPC_INS_STH:
case PPC_INS_STHBRX:
case PPC_INS_STHU:
case PPC_INS_STHUX:
case PPC_INS_STHX:
case PPC_INS_LHZCIX:
case PPC_INS_STHCIX:
return PPC_HWORD;
case PPC_INS_LWA:
case PPC_INS_LWARX:
case PPC_INS_LWAUX:
case PPC_INS_LWAX:
case PPC_INS_LWBRX:
case PPC_INS_LWZ:
case PPC_INS_LWZU:
case PPC_INS_LWZUX:
case PPC_INS_LWZX:
case PPC_INS_LMW:
case PPC_INS_STW:
case PPC_INS_STWBRX:
case PPC_INS_STWCX:
case PPC_INS_STWU:
case PPC_INS_STWUX:
case PPC_INS_STWX:
case PPC_INS_STMW:
case PPC_INS_LWZCIX:
case PPC_INS_STWCIX:
return PPC_WORD;
case PPC_INS_LD:
case PPC_INS_LDARX:
case PPC_INS_LDBRX:
case PPC_INS_LDU:
case PPC_INS_LDUX:
case PPC_INS_LDX:
case PPC_INS_STD:
case PPC_INS_STDBRX:
case PPC_INS_STDCX:
case PPC_INS_STDU:
case PPC_INS_STDUX:
case PPC_INS_STDX:
case PPC_INS_LDCIX:
case PPC_INS_STDCIX:
return PPC_DWORD;
}
}
/**
* \brief Returns true if the given load/store instruction updates the RA register with EA after memory access.
*
* \param insn_id The instruction id.
* \return bool True if RA is set to EA after the instruction executed. False otherwise.
*/
RZ_IPI bool ppc_updates_ra_with_ea(ut32 insn_id) {
switch (insn_id) {
default:
return false;
case PPC_INS_LBZU:
case PPC_INS_LBZUX:
case PPC_INS_LDU:
case PPC_INS_LDUX:
case PPC_INS_LHAU:
case PPC_INS_LHAUX:
case PPC_INS_LHZU:
case PPC_INS_LHZUX:
case PPC_INS_LWAUX:
case PPC_INS_LWZU:
case PPC_INS_LWZUX:
case PPC_INS_LFDU:
case PPC_INS_LFDUX:
case PPC_INS_LFSU:
case PPC_INS_LFSUX:
case PPC_INS_STBU:
case PPC_INS_STDU:
case PPC_INS_STHU:
case PPC_INS_STWU:
case PPC_INS_STBUX:
case PPC_INS_STHUX:
case PPC_INS_STWUX:
case PPC_INS_STDUX:
case PPC_INS_STFDU:
case PPC_INS_STFDUX:
case PPC_INS_STFSU:
case PPC_INS_STFSUX:
return true;
}
}
/**
* \brief Returns true if the given load instruction is algebraic.
*
* \param insn_id The instruction id.
* \return bool True if the load instruction is algebraic. False otherwise.
*/
RZ_IPI bool ppc_is_algebraic(ut32 insn_id) {
switch (insn_id) {
default:
return false;
case PPC_INS_LA:
case PPC_INS_LDARX:
case PPC_INS_LHA:
case PPC_INS_LHAU:
case PPC_INS_LHAUX:
case PPC_INS_LHAX:
case PPC_INS_LWA:
case PPC_INS_LWAX:
case PPC_INS_LWARX:
case PPC_INS_LWAUX:
return true;
}
}
/**
* \brief Returns true if the given branch instruction sets the LR register.
*
* \param insn_id The instruction id.
* \return bool True if the branch instruction writes the LR register. False otherwise.
*/
RZ_IPI bool ppc_insn_sets_lr(const cs_insn *insn) {
rz_return_val_if_fail(insn, false);
for (int i = 0; i < insn->detail->regs_write_count; ++i) {
ppc_reg reg = insn->detail->regs_write[i];
if (reg == PPC_REG_LR) {
return true;
}
}
return false;
}
/**
* \brief Returns true if the given branch instruction sets the LR register.
*
* \param insn_id The instruction id.
* \return bool True if the branch instruction writes the LR register. False otherwise.
*/
RZ_IPI bool ppc_sets_lr(ut32 insn_id) {
switch (insn_id) {
default:
return false;
#if CS_API_MAJOR == 5 && CS_NEXT_VERSION < 6
case PPC_INS_BEQCTRL:
case PPC_INS_BFCTRL:
case PPC_INS_BGECTRL:
case PPC_INS_BGTCTRL:
case PPC_INS_BLECTRL:
case PPC_INS_BLTCTRL:
case PPC_INS_BNECTRL:
case PPC_INS_BNGCTRL:
case PPC_INS_BNLCTRL:
case PPC_INS_BNSCTRL:
case PPC_INS_BNUCTRL:
case PPC_INS_BSOCTRL:
case PPC_INS_BTCTRL:
case PPC_INS_BUNCTRL:
case PPC_INS_BGEL:
case PPC_INS_BGELRL:
case PPC_INS_BGELA:
case PPC_INS_BDNZL:
case PPC_INS_BDNZLA:
case PPC_INS_BDNZLRL:
case PPC_INS_BDZL:
case PPC_INS_BDZLA:
case PPC_INS_BDZLRL:
case PPC_INS_BDNZTL:
case PPC_INS_BDNZTLA:
case PPC_INS_BDNZFL:
case PPC_INS_BDNZFLA:
case PPC_INS_BDZTL:
case PPC_INS_BDZTLA:
case PPC_INS_BDZFL:
case PPC_INS_BDZFLA:
#endif
case PPC_INS_BCCTRL:
case PPC_INS_BCL:
case PPC_INS_BCLRL:
case PPC_INS_BCTRL:
case PPC_INS_BL:
case PPC_INS_BLA:
case PPC_INS_BLRL:
case PPC_INS_BCLA:
return true;
}
}
#if CS_NEXT_VERSION >= 6
/**
* \brief Returns true if the given branch instruction is conditional.
*
* \param insn_id The instruction id.
* \return bool True if the branch instruction only branches if a condition is met. False otherwise.
*/
RZ_IPI bool ppc_insn_is_conditional(const cs_insn *insn) {
rz_return_val_if_fail(insn, false);
return PPC_DETAIL(insn).bc.pred_cr != PPC_PRED_INVALID || PPC_DETAIL(insn).bc.pred_ctr != PPC_PRED_INVALID;
}
#endif
/**
* \brief Returns true if the given branch instruction is conditional.
*
* \param insn_id The instruction id.
* \return bool True if the branch instruction only branches if a condition is met. False otherwise.
*/
RZ_IPI bool ppc_is_conditional(ut32 insn_id) {
switch (insn_id) {
default:
return false;
case PPC_INS_BC:
case PPC_INS_BCCTR:
case PPC_INS_BCCTRL:
case PPC_INS_BCL:
case PPC_INS_BCLR:
case PPC_INS_BCLRL:
case PPC_INS_BCA:
case PPC_INS_BCLA:
#if CS_API_MAJOR == 5 && CS_NEXT_VERSION < 6
case PPC_INS_BEQ:
case PPC_INS_BEQA:
case PPC_INS_BF:
case PPC_INS_BFA:
case PPC_INS_BGE:
case PPC_INS_BGEA:
case PPC_INS_BGT:
case PPC_INS_BGTA:
case PPC_INS_BLE:
case PPC_INS_BLEA:
case PPC_INS_BLT:
case PPC_INS_BLTA:
case PPC_INS_BNE:
case PPC_INS_BNEA:
case PPC_INS_BNG:
case PPC_INS_BNGA:
case PPC_INS_BNL:
case PPC_INS_BNLA:
case PPC_INS_BNS:
case PPC_INS_BNSA:
case PPC_INS_BNU:
case PPC_INS_BNUA:
case PPC_INS_BSO:
case PPC_INS_BSOA:
case PPC_INS_BT:
case PPC_INS_BTA:
case PPC_INS_BUN:
case PPC_INS_BUNA:
case PPC_INS_BGEL:
case PPC_INS_BGELA:
case PPC_INS_BGELR:
case PPC_INS_BGELRL:
case PPC_INS_BGECTR:
case PPC_INS_BGECTRL:
case PPC_INS_BDNZT:
case PPC_INS_BDNZTL:
case PPC_INS_BDNZTA:
case PPC_INS_BDNZTLA:
case PPC_INS_BDNZF:
case PPC_INS_BDNZFL:
case PPC_INS_BDNZFA:
case PPC_INS_BDNZFLA:
case PPC_INS_BDZT:
case PPC_INS_BDZTA:
case PPC_INS_BDZTL:
case PPC_INS_BDZTLA:
case PPC_INS_BDZF:
case PPC_INS_BDZFA:
case PPC_INS_BDZFL:
case PPC_INS_BDZFLA:
case PPC_INS_BDNZ:
case PPC_INS_BDNZA:
case PPC_INS_BDNZLR:
case PPC_INS_BDNZLRL:
case PPC_INS_BDZ:
case PPC_INS_BDZA:
case PPC_INS_BDZLA:
case PPC_INS_BDZLR:
case PPC_INS_BDZLRL:
#endif
return true;
}
}
/**
* \brief Returns true if the given instruction sets a SPR register.
*
* \param insn_id The instruction id.
* \return bool True if the instructions moves a value to a SPR. False otherwise.
*/
RZ_IPI bool ppc_moves_to_spr(ut32 insn_id) {
switch (insn_id) {
default:
return false;
case PPC_INS_MTCTR:
case PPC_INS_MTCRF:
case PPC_INS_MTDCR:
case PPC_INS_MTFSB0:
case PPC_INS_MTFSB1:
case PPC_INS_MTFSF:
case PPC_INS_MTLR:
case PPC_INS_MTMSR:
case PPC_INS_MTMSRD:
case PPC_INS_MTOCRF:
case PPC_INS_MTSPR:
case PPC_INS_MTSR:
case PPC_INS_MTSRIN:
case PPC_INS_MTVSCR:
#if CS_NEXT_VERSION < 6
case PPC_INS_MTCR:
case PPC_INS_MTBR0:
case PPC_INS_MTBR1:
case PPC_INS_MTBR2:
case PPC_INS_MTBR3:
case PPC_INS_MTBR4:
case PPC_INS_MTBR5:
case PPC_INS_MTBR6:
case PPC_INS_MTBR7:
case PPC_INS_MTXER:
case PPC_INS_MTDSCR:
case PPC_INS_MTDSISR:
case PPC_INS_MTDAR:
case PPC_INS_MTSRR2:
case PPC_INS_MTSRR3:
case PPC_INS_MTCFAR:
case PPC_INS_MTAMR:
case PPC_INS_MTPID:
case PPC_INS_MTTBL:
case PPC_INS_MTTBU:
case PPC_INS_MTTBLO:
case PPC_INS_MTTBHI:
case PPC_INS_MTDBATU:
case PPC_INS_MTDBATL:
case PPC_INS_MTIBATU:
case PPC_INS_MTIBATL:
case PPC_INS_MTDCCR:
case PPC_INS_MTICCR:
case PPC_INS_MTDEAR:
case PPC_INS_MTESR:
case PPC_INS_MTSPEFSCR:
case PPC_INS_MTTCR:
#endif
case PPC_INS_MFSRIN:
return true;
}
}
/**
* \brief Returns true if the given branch instruction decrements the CTR register.
*
* \param insn Instruction id.
* \param mode Capstone mode.
* \return bool True if the instruction decrements the counter. False otherwise.
*/
RZ_IPI bool ppc_decrements_ctr(RZ_BORROW cs_insn *insn, const cs_mode mode) {
rz_return_val_if_fail(insn, false);
#if CS_NEXT_VERSION >= 6
return cs_ppc_bc_decr_ctr(PPC_DETAIL(insn).bc.bo);
#else
ut32 id = insn->id;
switch (id) {
default:
return false;
case PPC_INS_BC:
case PPC_INS_BCL:
case PPC_INS_BCA:
case PPC_INS_BCLA:
case PPC_INS_BCLR:
case PPC_INS_BCLRL:
#if CS_API_MAJOR == 5
case PPC_INS_BGEL:
case PPC_INS_BGELA:
#endif
case PPC_INS_BDNZ:
case PPC_INS_BDNZA:
case PPC_INS_BDNZL:
case PPC_INS_BDNZLA:
case PPC_INS_BDNZLR:
case PPC_INS_BDNZLRL:
case PPC_INS_BDZ:
case PPC_INS_BDZA:
case PPC_INS_BDZL:
case PPC_INS_BDZLA:
case PPC_INS_BDZLR:
case PPC_INS_BDZLRL:
return !(0x4 & PPC_READ_BO_FIELD); // not BO_2
case PPC_INS_BDNZT:
case PPC_INS_BDNZTL:
case PPC_INS_BDNZTA:
case PPC_INS_BDNZTLA:
case PPC_INS_BDNZF:
case PPC_INS_BDNZFL:
case PPC_INS_BDNZFA:
case PPC_INS_BDNZFLA:
case PPC_INS_BDZT:
case PPC_INS_BDZTA:
case PPC_INS_BDZTL:
case PPC_INS_BDZTLA:
case PPC_INS_BDZF:
case PPC_INS_BDZFA:
case PPC_INS_BDZFL:
case PPC_INS_BDZFLA:
return true;
}
#endif
}
//
// IL helper BEGIN
//
#include <rz_il/rz_il_opbuilder_begin.h>
/**
* \brief Returns the value of the a bit at position \p pos in CR.
*
* NOTE: The Condition Register and its fields (cr0-cr7) start at bit 32.
* The CR reg is not defined for bits at positions smaller than 32 or larger than 63.
*
* \param pos The bit position to look up.
* \return RzILOpBool* The value of the bit at position \p pos in the CR register.
* Or IL_FALSE for invalid access.
*/
static RZ_OWN RzILOpBool *get_cr_bit(const ut8 pos) {
if (pos > 63 || pos < 32) {
RZ_LOG_WARN("Undefined access into CR register.\n");
return IL_FALSE;
}
ut8 field_bit;
RzILOpPure *cr_field;
if (pos < 36) {
field_bit = 0b1000 >> (pos - 32);
cr_field = VARG("cr0");
} else if (pos < 40) {
field_bit = 0b1000 >> (pos - 36);
cr_field = VARG("cr1");
} else if (pos < 44) {
field_bit = 0b1000 >> (pos - 40);
cr_field = VARG("cr2");
} else if (pos < 48) {
field_bit = 0b1000 >> (pos - 44);
cr_field = VARG("cr3");
} else if (pos < 52) {
field_bit = 0b1000 >> (pos - 48);
cr_field = VARG("cr4");
} else if (pos < 56) {
field_bit = 0b1000 >> (pos - 52);
cr_field = VARG("cr5");
} else if (pos < 60) {
field_bit = 0b1000 >> (pos - 56);
cr_field = VARG("cr6");
} else {
field_bit = 0b1000 >> (pos - 60);
cr_field = VARG("cr7");
}
return NON_ZERO(LOGAND(cr_field, UN(4, field_bit)));
}
/**
* \brief Synchronizes the CR register with the CR0-CR7 registers.
* Since CR contains CR0-CR7 but are separated in the register profile
* this function should be called before CR as a whole is read and after it was written.
*
* \param to_cr True: CR0-CR7 are copied to CR. False: CR is copied to CR0-CR7 according to \p cr_mask.
* \param cr_mask Masks the bits which are copied from CR to CR0-CR7. Ignored if \p crx_to_cr == true.
* \return RzILOpEffect* Sequence of effects to sync the CR/CRx registers.
*/
RZ_IPI RZ_OWN RzILOpEffect *ppc_sync_crx_cr(const bool crx_to_cr, const ut32 cr_mask) {
RzILOpEffect *effect;
if (crx_to_cr) {
effect = SETL("cr", LOGOR(UNSIGNED(32, VARG("cr7")), LOGOR(SHIFTL0(UNSIGNED(32, VARG("cr6")), U8(0x4)), LOGOR(SHIFTL0(UNSIGNED(32, VARG("cr5")), U8(0x8)), LOGOR(SHIFTL0(UNSIGNED(32, VARG("cr4")), U8(0xc)), LOGOR(SHIFTL0(UNSIGNED(32, VARG("cr3")), U8(0x10)), LOGOR(SHIFTL0(UNSIGNED(32, VARG("cr2")), U8(0x14)), LOGOR(SHIFTL0(UNSIGNED(32, VARG("cr1")), U8(0x18)), SHIFTL0(UNSIGNED(32, VARG("cr0")), U8(0x1c))))))))));
return effect;
}
effect = SEQN(10,
SETL("cr_mask", U32(cr_mask)),
SETL("crm", LOGAND(VARL("cr"), U32(cr_mask))),
BRANCH(IS_ZERO(LOGAND(VARL("cr_mask"), U32(0xf))), EMPTY(), SETG("cr7", UNSIGNED(4, VARL("crm")))),
BRANCH(IS_ZERO(LOGAND(VARL("cr_mask"), U32(0xf0))), EMPTY(), SETG("cr6", UNSIGNED(4, SHIFTR0(VARL("crm"), U8(0x4))))),
BRANCH(IS_ZERO(LOGAND(VARL("cr_mask"), U32(0xf00))), EMPTY(), SETG("cr5", UNSIGNED(4, SHIFTR0(VARL("crm"), U8(0x8))))),
BRANCH(IS_ZERO(LOGAND(VARL("cr_mask"), U32(0xf000))), EMPTY(), SETG("cr4", UNSIGNED(4, SHIFTR0(VARL("crm"), U8(0xc))))),
BRANCH(IS_ZERO(LOGAND(VARL("cr_mask"), U32(0xf0000))), EMPTY(), SETG("cr3", UNSIGNED(4, SHIFTR0(VARL("crm"), U8(0x10))))),
BRANCH(IS_ZERO(LOGAND(VARL("cr_mask"), U32(0xf00000))), EMPTY(), SETG("cr2", UNSIGNED(4, SHIFTR0(VARL("crm"), U8(0x14))))),
BRANCH(IS_ZERO(LOGAND(VARL("cr_mask"), U32(0xf000000))), EMPTY(), SETG("cr1", UNSIGNED(4, SHIFTR0(VARL("crm"), U8(0x18))))),
BRANCH(IS_ZERO(LOGAND(VARL("cr_mask"), U32(0xf0000000))), EMPTY(), SETG("cr0", UNSIGNED(4, SHIFTR0(VARL("crm"), U8(0x1c))))));
return effect;
}
/**
* \brief Returns the mask for a given fxm operand.
* For details look up the "mtcrf" instruction in the Power ISA
*
* \param fmx The fmx value.
* \return ut32 The mask for writing to the CR register.
*/
RZ_IPI ut32 ppc_fmx_to_mask(const ut8 fmx) {
ut32 x = 0xf;
return (
(fmx & 0x80 ? x << 28 : 0) |
(fmx & 0x40 ? x << 24 : 0) |
(fmx & 0x20 ? x << 20 : 0) |
(fmx & 0x10 ? x << 16 : 0) |
(fmx & 0x08 ? x << 12 : 0) |
(fmx & 0x04 ? x << 8 : 0) |
(fmx & 0x02 ? x << 4 : 0) |
(fmx & 0x01 ? x : 0));
}
#if CS_NEXT_VERSION < 6
static const char *get_crx_reg(const csh handle, cs_insn *insn, size_t n) {
#if CS_API_MAJOR == 5 && CS_API_MINOR == 0
// bug on crx not being populated in capstone v5.0
ppc_reg reg = INSOP(n).reg;
if (reg >= PPC_REG_CR0EQ && reg <= PPC_REG_CR7EQ) {
reg = (reg - PPC_REG_CR0EQ) + PPC_REG_CR0;
} else if (reg >= PPC_REG_CR0GT && reg <= PPC_REG_CR7GT) {
reg = (reg - PPC_REG_CR0GT) + PPC_REG_CR0;
} else if (reg >= PPC_REG_CR0LT && reg <= PPC_REG_CR7LT) {
reg = (reg - PPC_REG_CR0LT) + PPC_REG_CR0;
} else if (reg >= PPC_REG_CR0UN && reg <= PPC_REG_CR7UN) {
reg = (reg - PPC_REG_CR0UN) + PPC_REG_CR0;
} else {
rz_warn_if_reached();
}
return cs_reg_name(handle, reg);
#else
return cs_reg_name(handle, INSOP(n).crx.reg);
#endif
}
static ut32 get_crx_cond(const csh handle, cs_insn *insn, size_t n) {
#if CS_API_MAJOR == 5 && CS_API_MINOR == 0
// bug on crx not being populated in capstone v5.0
ppc_reg reg = INSOP(n).reg;
if (reg >= PPC_REG_CR0EQ && reg <= PPC_REG_CR7EQ) {
return PPC_BC_EQ;
} else if (reg >= PPC_REG_CR0GT && reg <= PPC_REG_CR7GT) {
return PPC_BC_GT;
} else if (reg >= PPC_REG_CR0LT && reg <= PPC_REG_CR7LT) {
return PPC_BC_LT;
} else if (reg >= PPC_REG_CR0UN && reg <= PPC_REG_CR7UN) {
return PPC_BC_UN;
}
rz_warn_if_reached();
return PPC_BC_INVALID;
#else
return INSOP(n).crx.cond;
#endif
}
#endif
/**
* \brief Get the branch condition for a given instruction.
* Checkout the "Simple Branch Mnemonics" in Appendix C in PowerISA v3.1B and
* the chapter about branch instructions for an overview of possible conditions.
*
* NOTE: This function *does not* decrement CTR, if required by the instruction.
* This should have been done before.
*
* \param insn The capstone instructions.
* \param mode The capstone mode.
* \return RzILOpPure* The condition the branch occurs as a Pure.
*/
RZ_IPI RZ_OWN RzILOpPure *ppc_get_branch_cond(const csh handle, RZ_BORROW cs_insn *insn, const cs_mode mode) {
rz_return_val_if_fail(insn, NULL);
ut32 id = insn->id;
#if CS_NEXT_VERSION >= 6
ut8 bi = PPC_DETAIL(insn).bc.bi;
ut8 bo = PPC_DETAIL(insn).bc.bo;
RzILOpBool *decr_ctr = cs_ppc_bc_decr_ctr(bo) ? IL_TRUE : IL_FALSE;
RzILOpBool *test_cr_bit = cs_ppc_bc_cr_is_tested(bo) ? IL_TRUE : IL_FALSE;
RzILOpBool *check_ctr_is_zero = cs_ppc_bc_tests_ctr_is_zero(bo) ? IL_TRUE : IL_FALSE;
RzILOpBool *check_cr_bit_is_one = cs_ppc_bc_cr_bit_is_one(bo) ? IL_TRUE : IL_FALSE;
#else
ut8 bo = PPC_READ_BO_FIELD;
ut8 bi = PPC_READ_BI_FIELD;
RzILOpPure *bo_0;
RzILOpPure *bo_1;
RzILOpPure *bo_2;
RzILOpPure *bo_3;
RzILOpPure *cr;
RzILOpPure *cr_bit;
#endif
RzILOpPure *ctr_cond_fullfilled;
RzILOpPure *cr_cond_fullfilled;
switch (id) {
default:
RZ_LOG_WARN("Instruction %d has no condition implemented.\n", id);
return IL_FALSE;
// For learning how the conditions of BCxxx branch instructions are
// formed see the Power ISA
#if CS_API_MAJOR == 5 && CS_NEXT_VERSION < 6
case PPC_INS_BEQ:
case PPC_INS_BEQA:
case PPC_INS_BF:
case PPC_INS_BFA:
case PPC_INS_BGE:
case PPC_INS_BGEA:
case PPC_INS_BGT:
case PPC_INS_BGTA:
case PPC_INS_BLE:
case PPC_INS_BLEA:
case PPC_INS_BLT:
case PPC_INS_BLTA:
case PPC_INS_BNE:
case PPC_INS_BNEA:
case PPC_INS_BNG:
case PPC_INS_BNGA:
case PPC_INS_BNL:
case PPC_INS_BNLA:
case PPC_INS_BNS:
case PPC_INS_BNSA:
case PPC_INS_BNU:
case PPC_INS_BNUA:
case PPC_INS_BSO:
case PPC_INS_BSOA:
case PPC_INS_BT:
case PPC_INS_BTA:
case PPC_INS_BUN:
case PPC_INS_BUNA:
case PPC_INS_BGEL:
case PPC_INS_BGELA:
case PPC_INS_BGELR:
case PPC_INS_BGELRL:
#endif
case PPC_INS_BC:
case PPC_INS_BCL:
case PPC_INS_BCA:
case PPC_INS_BCLA:
case PPC_INS_BCLR:
case PPC_INS_BCLRL:
#if CS_NEXT_VERSION >= 6
ctr_cond_fullfilled = ITE(decr_ctr, XOR(NON_ZERO(VARG("ctr")), check_ctr_is_zero), IL_TRUE);
cr_cond_fullfilled = ITE(test_cr_bit, XOR(get_cr_bit(bi + 32), INV(check_cr_bit_is_one)), IL_TRUE);
return AND(ctr_cond_fullfilled, cr_cond_fullfilled);
#else
// BO_2 == 0: Decrement CTR
// BO_2 == 1: Don't use CTR
bo_2 = NON_ZERO(LOGAND(UN(5, 0b00100), VARLP("bo")));
// BO_3 == 0: Check CTR != 0
// BO_3 == 1: Check CTR == 0
bo_3 = NON_ZERO(LOGAND(UN(5, 0b00010), VARLP("bo")));
ctr_cond_fullfilled = OR(bo_2, XOR(NON_ZERO(VARG("ctr")), bo_3)); // BO_2 | (CTR_M:63 ≠ 0) ⊕ BO_3
// BO_0 == 0: Check CR_bi
// BO_0 == 1: Don't check CR_bi
bo_0 = NON_ZERO(LOGAND(UN(5, 0b10000), VARLP("bo")));
// BO_1 == 0: Check CR_bi == 0
// BO_1 == 1: Check CR_bi == 1
bo_1 = NON_ZERO(LOGAND(UN(5, 0b01000), VARLP("bo")));
cr_cond_fullfilled = OR(bo_0, XOR(get_cr_bit(bi + 32), INV(bo_1))); // BO_0 | (CR_BI+32 ≡ BO_1)
return LET("bo", UN(5, bo), AND(cr_cond_fullfilled, ctr_cond_fullfilled));
#endif
case PPC_INS_BCCTR:
case PPC_INS_BCCTRL:
#if CS_API_MAJOR == 5 && CS_NEXT_VERSION < 6
case PPC_INS_BEQCTR:
case PPC_INS_BEQCTRL:
case PPC_INS_BFCTR:
case PPC_INS_BFCTRL:
case PPC_INS_BGTCTR:
case PPC_INS_BGTCTRL:
case PPC_INS_BLECTR:
case PPC_INS_BLECTRL:
case PPC_INS_BLTCTR:
case PPC_INS_BLTCTRL:
case PPC_INS_BNECTR:
case PPC_INS_BNECTRL:
case PPC_INS_BNGCTR:
case PPC_INS_BNGCTRL:
case PPC_INS_BNLCTR:
case PPC_INS_BNLCTRL:
case PPC_INS_BNSCTR:
case PPC_INS_BNSCTRL:
case PPC_INS_BNUCTR:
case PPC_INS_BNUCTRL:
case PPC_INS_BSOCTR:
case PPC_INS_BSOCTRL:
case PPC_INS_BTCTR:
case PPC_INS_BTCTRL:
case PPC_INS_BUNCTR:
case PPC_INS_BUNCTRL:
case PPC_INS_BGECTR:
case PPC_INS_BGECTRL:
#endif
#if CS_NEXT_VERSION >= 6
cr_cond_fullfilled = AND(test_cr_bit, XOR(get_cr_bit(bi + 32), INV(check_cr_bit_is_one)));
return cr_cond_fullfilled;
#else
bo_0 = NON_ZERO(LOGAND(UN(5, 0b10000), VARLP("bo")));
bo_1 = NON_ZERO(LOGAND(UN(5, 0b01000), VARLP("bo")));
cr_cond_fullfilled = OR(bo_0, XOR(get_cr_bit(bi + 32), INV(bo_1))); // BO_0 | (CR_BI+32 ≡ BO_1)
return LET("bo", UN(5, bo), cr_cond_fullfilled);
// CTR != 0
case PPC_INS_BDNZ:
case PPC_INS_BDNZA:
case PPC_INS_BDNZL:
case PPC_INS_BDNZLA:
case PPC_INS_BDNZLR:
case PPC_INS_BDNZLRL:
return NON_ZERO(VARG("ctr"));
// CTR == 0
case PPC_INS_BDZ:
case PPC_INS_BDZA:
case PPC_INS_BDZL:
case PPC_INS_BDZLA:
case PPC_INS_BDZLR:
case PPC_INS_BDZLRL:
return IS_ZERO(VARG("ctr"));
// ctr != 0 && cr_bi == 1
case PPC_INS_BDNZT:
case PPC_INS_BDNZTL:
case PPC_INS_BDNZTA:
case PPC_INS_BDNZTLA:
// ctr == 0 && cr_bi == 1
case PPC_INS_BDZT:
case PPC_INS_BDZTL:
case PPC_INS_BDZTA:
case PPC_INS_BDZTLA:
if (insn->detail->ppc.op_count == 1) {
// If Capstone doesn't provide a CR register it means that the LT bit in cr0 is checked.
cr = VARG("cr0");
// LT bit
cr_bit = UN(4, 8);
} else {
cr = VARG(get_crx_reg(handle, insn, 0));
ut32 cond = get_crx_cond(handle, insn, 0);
cr_bit = UN(4, ((cond & PPC_BC_SO) ? 1 : ((cond & PPC_BC_EQ) ? 2 : ((cond & PPC_BC_GT) ? 4 : 8))));
}
if (id == PPC_INS_BDZT || id == PPC_INS_BDZTL || id == PPC_INS_BDZTA || id == PPC_INS_BDZTLA) {
return AND(IS_ZERO(VARG("ctr")), NON_ZERO(LOGAND(cr, cr_bit)));
}
return AND(NON_ZERO(VARG("ctr")), NON_ZERO(LOGAND(cr, cr_bit)));
// ctr != 0 && cr_bi == 0
case PPC_INS_BDNZF:
case PPC_INS_BDNZFL:
case PPC_INS_BDNZFA:
case PPC_INS_BDNZFLA:
// ctr == 0 && cr_bi == 0
case PPC_INS_BDZF:
case PPC_INS_BDZFL:
case PPC_INS_BDZFA:
case PPC_INS_BDZFLA:
if (insn->detail->ppc.op_count == 1) {
cr = VARG("cr0");
cr_bit = UN(4, 8);
} else {
cr = VARG(get_crx_reg(handle, insn, 0));
ut32 cond = get_crx_cond(handle, insn, 0);
cr_bit = UN(4, ((cond & PPC_BC_SO) ? 1 : ((cond & PPC_BC_EQ) ? 2 : ((cond & PPC_BC_GT) ? 4 : 8))));
}
cr = insn->detail->ppc.op_count == 1 ? VARG("cr0") : VARG(get_crx_reg(handle, insn, 0));
if (id == PPC_INS_BDZF || id == PPC_INS_BDZFL || id == PPC_INS_BDZFA || id == PPC_INS_BDZFLA) {
return AND(IS_ZERO(VARG("ctr")), IS_ZERO(LOGAND(cr, cr_bit)));
}
return AND(NON_ZERO(VARG("ctr")), IS_ZERO(LOGAND(cr, cr_bit)));
#endif
}
}
/**
* \brief Get the branch instruction's target address.
* In case of conditional branches it returns the address if the condition would be fulfilled.
*
* There are five types of target addresses:
* * Absolute address
* * Relative address (relative to current instruction address)
* * Address stored in LR
* * Address stored in CTR
* * Address stored in TAR
*
* NOTE: Capstone calculates the NIA and fills the operand member with it.
* We don't need to do the shift, add and extend here.
*
* \param insn The capstone instructions.
* \param mode The capstone mode.
* \return RzILOpPure* The target address of the jump.
*/
RZ_IPI RZ_OWN RzILOpPure *ppc_get_branch_ta(RZ_BORROW cs_insn *insn, const cs_mode mode) {
rz_return_val_if_fail(insn, NULL);
ut32 id = insn->id;
switch (id) {
default:
RZ_LOG_WARN("Target address of branch instruction %d can not be resolved.\n", id);
return UA(0);
// Target address is pre-calculated by Capstone and stored in INSOP().imm
// The comments show only the TA calculation according to the ISA.
// Banch to absolute address
case PPC_INS_BA:
case PPC_INS_BLA:
case PPC_INS_BCA:
case PPC_INS_BCLA:
#if CS_NEXT_VERSION < 6
case PPC_INS_BDNZTA:
case PPC_INS_BDNZTLA:
case PPC_INS_BDNZFA:
case PPC_INS_BDNZFLA:
case PPC_INS_BDZTA:
case PPC_INS_BDZTLA:
case PPC_INS_BDZFA:
case PPC_INS_BDZFLA:
#endif
// EXTS(LI || 0b00)
// Branch to relative address
#if CS_API_MAJOR == 5 && CS_NEXT_VERSION < 6
case PPC_INS_BEQ:
case PPC_INS_BEQA:
case PPC_INS_BF:
case PPC_INS_BFA:
case PPC_INS_BGE:
case PPC_INS_BGEA:
case PPC_INS_BGT:
case PPC_INS_BGTA:
case PPC_INS_BLE:
case PPC_INS_BLEA:
case PPC_INS_BLT:
case PPC_INS_BLTA:
case PPC_INS_BNE:
case PPC_INS_BNEA:
case PPC_INS_BNG:
case PPC_INS_BNGA:
case PPC_INS_BNL:
case PPC_INS_BNLA:
case PPC_INS_BNS:
case PPC_INS_BNSA:
case PPC_INS_BNU:
case PPC_INS_BNUA:
case PPC_INS_BSO:
case PPC_INS_BSOA:
case PPC_INS_BT:
case PPC_INS_BTA:
case PPC_INS_BUN:
case PPC_INS_BUNA:
#endif
case PPC_INS_B:
case PPC_INS_BL:
#if CS_NEXT_VERSION < 6
case PPC_INS_BDZF:
case PPC_INS_BDZFL:
case PPC_INS_BDZT:
case PPC_INS_BDZTL:
case PPC_INS_BDNZF:
case PPC_INS_BDNZFL:
case PPC_INS_BDNZT:
case PPC_INS_BDNZTL:
#endif
// CIA + EXTS(LI || 0b00)
if (insn->detail->ppc.op_count == 2) {
return UA(INSOP(1).imm);
} else {
return UA(INSOP(0).imm);
}
#if CS_NEXT_VERSION < 6
case PPC_INS_BDZA:
case PPC_INS_BDZLA:
case PPC_INS_BDNZA:
case PPC_INS_BDNZLA:
#endif
// EXTS(BD || 0b00)
#if CS_API_MAJOR == 5 && CS_NEXT_VERSION < 6
case PPC_INS_BGEL:
case PPC_INS_BGELA:
#endif
case PPC_INS_BC:
case PPC_INS_BCL:
#if CS_NEXT_VERSION >= 6
return UA(INSOP(2).imm);
#else
case PPC_INS_BDZ:
case PPC_INS_BDZL:
case PPC_INS_BDNZ:
case PPC_INS_BDNZL:
// If bits in cr0 are checked, the opcount is 1.
// CIA + EXTS(BD || 0b00)
if (insn->detail->ppc.op_count == 2) {
return UA(INSOP(1).imm);
} else {
return UA(INSOP(0).imm);
}
#endif
// Branch to LR
#if CS_API_MAJOR == 5 && CS_NEXT_VERSION < 6
case PPC_INS_BEQLR:
case PPC_INS_BEQLRL:
case PPC_INS_BLELR:
case PPC_INS_BLELRL:
case PPC_INS_BLTLR:
case PPC_INS_BLTLRL:
case PPC_INS_BGELR:
case PPC_INS_BGELRL:
#endif
case PPC_INS_BLR:
case PPC_INS_BLRL:
case PPC_INS_BCLR:
case PPC_INS_BCLRL:
#if CS_NEXT_VERSION < 6
case PPC_INS_BDZLR:
case PPC_INS_BDZLRL:
case PPC_INS_BDNZLR:
case PPC_INS_BDNZLRL:
#endif
// LR_0:61 || 0b00
return LOGAND(UA(-4), VARG("lr"));
// Branch to CTR
#if CS_API_MAJOR == 5 && CS_NEXT_VERSION < 6
case PPC_INS_BEQCTR:
case PPC_INS_BEQCTRL:
case PPC_INS_BFCTR:
case PPC_INS_BFCTRL:
case PPC_INS_BGECTR:
case PPC_INS_BGECTRL:
case PPC_INS_BGTCTR:
case PPC_INS_BGTCTRL:
case PPC_INS_BLECTR:
case PPC_INS_BLECTRL:
case PPC_INS_BLTCTR:
case PPC_INS_BLTCTRL:
case PPC_INS_BNECTR:
case PPC_INS_BNECTRL:
case PPC_INS_BNGCTR:
case PPC_INS_BNGCTRL:
case PPC_INS_BNLCTR:
case PPC_INS_BNLCTRL:
case PPC_INS_BNSCTR:
case PPC_INS_BNSCTRL:
case PPC_INS_BNUCTR:
case PPC_INS_BNUCTRL:
case PPC_INS_BSOCTR:
case PPC_INS_BSOCTRL:
case PPC_INS_BTCTR:
case PPC_INS_BTCTRL:
case PPC_INS_BUNCTR:
case PPC_INS_BUNCTRL:
#endif
case PPC_INS_BCTR:
case PPC_INS_BCTRL:
case PPC_INS_BCCTR:
case PPC_INS_BCCTRL:
// CTR_0:61 || 0b00
return LOGAND(UA(-4), VARG("ctr"));
}
}
/**
* \brief Returns true if the multiplication instruction operates on double words.
*
* \param id The instruction id.
* \param mode The Capstone mode.
* \return bool True if the instruction operates on double words. False otherwise;
*/
RZ_IPI bool ppc_is_mul_div_d(const ut32 id, const cs_mode mode) {
return id == PPC_INS_MULHD || id == PPC_INS_MULLD || id == PPC_INS_MULHDU ||
id == PPC_INS_DIVD || id == PPC_INS_DIVDU || ((id == PPC_INS_MULLI) && IN_64BIT_MODE);
}
/**
* \brief Returns true if the division instruction operates on double words.
*
* \param id The instruction id.
* \param mode The Capstone mode.
* \return bool True if the instruction operates on double words. False otherwise;
*/
RZ_IPI bool ppc_is_mul_div_u(const ut32 id) {
return id == PPC_INS_MULHDU || id == PPC_INS_MULHWU || id == PPC_INS_DIVWU || id == PPC_INS_DIVDU;
}
/**
* \brief Assembles the current XER value by combining the values
* from the flag registers "so", "ov", "ca".
*
* \param mode The capstone mode.
* \return RzILOpPure* The Pure containing the current XER value.
*/
RZ_IPI RZ_OWN RzILOpPure *ppc_get_xer(cs_mode mode) {
RzILOpPure *so = SHIFTL0(EXTZ(BOOL_TO_BV(VARG("so"), 1)), U8(31));
RzILOpPure *ov = SHIFTL0(EXTZ(BOOL_TO_BV(VARG("ov"), 1)), U8(30));
RzILOpPure *ca = SHIFTL0(EXTZ(BOOL_TO_BV(VARG("ca"), 1)), U8(29));
// For ISA v3 CPUs register ca32 and ov32 should be handled here as well.
// Currently they are ignored. If you want to add them take a look at:
// https://github.com/Rot127/rizin/tree/Examples-ppc-rzil-isav3-regs
return LOGOR(LOGOR(so, ov), ca);
}
/**
* \brief Sets the XER register to \p val and updates the flag register "so", "ov", "ca" accordingly.
*
* \param val The new value of XER.
* \param mode The capstone mode.
* \return RzILOpEffect* The sequence of effects setting all registers to their respective values.
*/
RZ_IPI RZ_OWN RzILOpEffect *ppc_set_xer(RzILOpPure *val, cs_mode mode) {
rz_return_val_if_fail(val, NULL);
RzILOpPure *v = LOGAND(BIT_MASK(64, U8(32), U8(34)), UNSIGNED(64, val));
return SEQ5(SETL("v", v), SETG("xer", VARL("v")),
SETG("so", BIT_IS_SET(VARL("v"), 64, U8(32))),
SETG("ov", BIT_IS_SET(VARL("v"), 64, U8(33))),
SETG("ca", BIT_IS_SET(VARL("v"), 64, U8(34))));
// For ISA v3 CPUs register ca32 and ov32 should be handled here as well.
// Currently they are ignored. If you want to add them take a look at:
// https://github.com/Rot127/rizin/tree/Examples-ppc-rzil-isav3-regs
}
#include <rz_il/rz_il_opbuilder_end.h>
//
// IL helper END
//