1980 lines
49 KiB
C
1980 lines
49 KiB
C
// SPDX-FileCopyrightText: 2022 Rot127 <rot127@posteo.com>
|
||
// SPDX-License-Identifier: LGPL-3.0-only
|
||
|
||
#include "ppc_il.h"
|
||
#include "ppc_analysis.h"
|
||
#include "rz_types_base.h"
|
||
#include <rz_il/rz_il_opcodes.h>
|
||
#include <rz_util/rz_assert.h>
|
||
#include <capstone/capstone.h>
|
||
#include <rz_il/rz_il_opbuilder_begin.h>
|
||
|
||
static RzILOpEffect *load_op(RZ_BORROW csh handle, RZ_BORROW cs_insn *insn, const cs_mode mode) {
|
||
rz_return_val_if_fail(handle && insn, EMPTY());
|
||
ut32 id = insn->id;
|
||
// READ
|
||
const char *rT = cs_reg_name(handle, INSOP(0).reg);
|
||
const char *rA = cs_reg_name(handle, INSOP(1).mem.base);
|
||
#if CS_NEXT_VERSION < 6
|
||
const char *rB = cs_reg_name(handle, INSOP(2).reg);
|
||
#else
|
||
const char *rB = cs_reg_name(handle, INSOP(1).mem.offset);
|
||
#endif
|
||
st64 d = INSOP(1).mem.disp; // RA = base ; D = Disposition
|
||
st64 sI = INSOP(1).imm; // liX instructions (alias for addX).
|
||
bool update_ra = ppc_updates_ra_with_ea(id); // Save ea in RA?
|
||
ut32 mem_acc_size = ppc_get_mem_acc_size(id);
|
||
RzILOpPure *base;
|
||
RzILOpPure *disp;
|
||
RzILOpPure *ea;
|
||
RzILOpPure *into_rt;
|
||
|
||
if (mem_acc_size < 0) {
|
||
NOT_IMPLEMENTED;
|
||
}
|
||
|
||
// How to read instruction ids:
|
||
// Letter Meaning
|
||
// L Load
|
||
// B/H/W/D/F Byte, Half Word, Word, Double Word, Float
|
||
// Z/A/B Zero extend, Algebraic, Byte reversal
|
||
// U/R Update (store EA in RA), Reserve indexed
|
||
// X X Form instruction (uses RB instead of immediate)
|
||
// CIX Caching Inhibited Indexed
|
||
// V Vector indexed
|
||
|
||
// EXEC
|
||
switch (id) {
|
||
default:
|
||
NOT_IMPLEMENTED;
|
||
case PPC_INS_LI: // RT = sI
|
||
into_rt = EXTEND(PPC_ARCH_BITS, SN(16, sI));
|
||
update_ra = false;
|
||
break;
|
||
case PPC_INS_LIS: // RT = SI << 16
|
||
into_rt = EXTEND(PPC_ARCH_BITS, APPEND(SN(16, sI), U16(0)));
|
||
update_ra = false;
|
||
break;
|
||
case PPC_INS_LA: // RT = EA
|
||
NOT_IMPLEMENTED;
|
||
case PPC_INS_LBZ:
|
||
case PPC_INS_LBZX:
|
||
case PPC_INS_LBZU:
|
||
case PPC_INS_LBZUX:
|
||
case PPC_INS_LHZ:
|
||
case PPC_INS_LHZX:
|
||
case PPC_INS_LHZU:
|
||
case PPC_INS_LHZUX:
|
||
case PPC_INS_LWZ:
|
||
case PPC_INS_LWZU:
|
||
case PPC_INS_LWZUX:
|
||
case PPC_INS_LWZX:
|
||
case PPC_INS_LD:
|
||
case PPC_INS_LDX:
|
||
case PPC_INS_LDU:
|
||
case PPC_INS_LDUX:
|
||
case PPC_INS_LHA:
|
||
case PPC_INS_LHAX:
|
||
case PPC_INS_LHAU:
|
||
case PPC_INS_LHAUX:
|
||
case PPC_INS_LWA:
|
||
case PPC_INS_LWAX:
|
||
case PPC_INS_LWAUX:
|
||
case PPC_INS_LBZCIX:
|
||
case PPC_INS_LHZCIX:
|
||
case PPC_INS_LWZCIX:
|
||
case PPC_INS_LDCIX:
|
||
#if CS_NEXT_VERSION >= 6
|
||
base = VARG(rA);
|
||
#else
|
||
base = rA ? VARG(rA) : NULL;
|
||
#endif
|
||
if (ppc_is_x_form(id)) {
|
||
disp = VARG(rB);
|
||
} else {
|
||
// "disp << 2" done by capstone.
|
||
RzILOpPure *imm = SN(16, d);
|
||
disp = EXTEND(PPC_ARCH_BITS, imm);
|
||
}
|
||
ea = base ? ADD(base, disp) : disp;
|
||
RzILOpPure *loadw = LOADW(mem_acc_size, VARLP("ea"));
|
||
if (ppc_is_algebraic(id)) {
|
||
into_rt = EXTEND(PPC_ARCH_BITS, VARLP("loadw"));
|
||
} else {
|
||
into_rt = EXTZ(VARLP("loadw"));
|
||
}
|
||
into_rt = LET("ea", ea, LET("loadw", loadw, into_rt));
|
||
break;
|
||
case PPC_INS_LWARX:
|
||
case PPC_INS_LDARX:
|
||
NOT_IMPLEMENTED;
|
||
// Byte reverse and reserved indexed
|
||
case PPC_INS_LHBRX:
|
||
case PPC_INS_LWBRX:
|
||
case PPC_INS_LDBRX:
|
||
#if CS_NEXT_VERSION >= 6
|
||
base = VARG(rA);
|
||
#else
|
||
base = rA ? VARG(rA) : NULL;
|
||
#endif
|
||
disp = VARG(rB);
|
||
ea = base ? ADD(base, disp) : disp;
|
||
|
||
#define LB(i) LOADW(8, ADD(VARLP("ea"), UA(i)))
|
||
if (IN_BE_MODE) {
|
||
if (id == PPC_INS_LHBRX) {
|
||
into_rt = LET("ea", ea, APPEND(LB(0), LB(1)));
|
||
} else if (id == PPC_INS_LWBRX) {
|
||
into_rt = LET("ea", ea, APPEND(LB(0), APPEND(LB(1), APPEND(LB(2), LB(3)))));
|
||
} else {
|
||
into_rt = LET("ea", ea, APPEND(LB(0), APPEND(LB(1), APPEND(LB(2), APPEND(LB(3), APPEND(LB(4), APPEND(LB(5), APPEND(LB(6), LB(7)))))))));
|
||
}
|
||
} else {
|
||
if (id == PPC_INS_LHBRX) {
|
||
into_rt = LET("ea", ea, APPEND(LB(1), LB(0)));
|
||
} else if (id == PPC_INS_LWBRX) {
|
||
into_rt = LET("ea", ea, APPEND(LB(3), APPEND(LB(2), APPEND(LB(1), LB(0)))));
|
||
} else {
|
||
into_rt = LET("ea", ea, APPEND(LB(7), APPEND(LB(6), APPEND(LB(5), APPEND(LB(4), APPEND(LB(3), APPEND(LB(2), APPEND(LB(1), LB(0)))))))));
|
||
}
|
||
}
|
||
into_rt = EXTZ(into_rt);
|
||
break;
|
||
// Floats
|
||
case PPC_INS_LFD:
|
||
case PPC_INS_LFDX:
|
||
case PPC_INS_LFDU:
|
||
case PPC_INS_LFDUX:
|
||
case PPC_INS_LFIWAX:
|
||
case PPC_INS_LFIWZX:
|
||
case PPC_INS_LFS:
|
||
case PPC_INS_LFSX:
|
||
case PPC_INS_LFSU:
|
||
case PPC_INS_LFSUX:
|
||
NOT_IMPLEMENTED;
|
||
// Vector
|
||
case PPC_INS_LVEBX:
|
||
case PPC_INS_LVEHX:
|
||
case PPC_INS_LVEWX:
|
||
case PPC_INS_LVSL:
|
||
case PPC_INS_LVSR:
|
||
case PPC_INS_LVX:
|
||
case PPC_INS_LVXL:
|
||
NOT_IMPLEMENTED;
|
||
// String word
|
||
case PPC_INS_LSWI:
|
||
NOT_IMPLEMENTED;
|
||
// VSX Scalar
|
||
case PPC_INS_LXSDX:
|
||
// VSX Vector
|
||
case PPC_INS_LXVD2X:
|
||
case PPC_INS_LXVDSX:
|
||
case PPC_INS_LXVW4X:
|
||
NOT_IMPLEMENTED;
|
||
}
|
||
|
||
rz_return_val_if_fail(into_rt, NULL);
|
||
RzILOpEffect *res = SETG(rT, into_rt);
|
||
RzILOpEffect *update = update_ra ? SETG(rA, DUP(ea)) : EMPTY();
|
||
return SEQ2(res, update);
|
||
}
|
||
|
||
/**
|
||
* \brief Determine log_2( \p v ). \p v must be a multiple of 2.
|
||
* See: https://graphics.stanford.edu/~seander/bithacks.html#IntegerLog
|
||
*
|
||
* \param v Value to determine lg(v) for.
|
||
* \return ut32 lg(v)
|
||
*/
|
||
static inline ut32 ppc_log_2(const ut32 v) {
|
||
static const ut32 b[] = { 0xAAAAAAAA, 0xCCCCCCCC, 0xF0F0F0F0,
|
||
0xFF00FF00, 0xFFFF0000 };
|
||
register ut32 r = (v & b[0]) != 0;
|
||
r |= ((v & b[4]) != 0) << 4;
|
||
r |= ((v & b[3]) != 0) << 3;
|
||
r |= ((v & b[2]) != 0) << 2;
|
||
r |= ((v & b[1]) != 0) << 1;
|
||
return r;
|
||
}
|
||
|
||
static RzILOpEffect *store_op(RZ_BORROW csh handle, RZ_BORROW cs_insn *insn, const cs_mode mode) {
|
||
rz_return_val_if_fail(handle && insn, EMPTY());
|
||
ut32 id = insn->id;
|
||
|
||
// How to read instruction ids:
|
||
// Letter Meaning
|
||
// ST Store
|
||
// B/H/W/D/F Byte, Half Word, Word, Double Word, Float
|
||
// BR Byte reversal
|
||
// U/X Update (store EA in RA), X Form instruction (uses RB instead of immediate)
|
||
// MW/CIX Multiple word, Caching Inhibited Indexed
|
||
// V Vector
|
||
|
||
// READ
|
||
const char *rS = cs_reg_name(handle, INSOP(0).reg);
|
||
const char *rA = cs_reg_name(handle, INSOP(1).mem.base);
|
||
#if CS_NEXT_VERSION < 6
|
||
const char *rB = cs_reg_name(handle, INSOP(2).reg);
|
||
#else
|
||
const char *rB = cs_reg_name(handle, INSOP(1).mem.offset);
|
||
#endif
|
||
st64 d = INSOP(1).mem.disp; // RA = base ; D = Disposition
|
||
bool update_ra = ppc_updates_ra_with_ea(id); // Save ea in RA?
|
||
ut32 mem_acc_size = ppc_get_mem_acc_size(id);
|
||
RzILOpPure *base;
|
||
RzILOpPure *disp;
|
||
RzILOpPure *ea;
|
||
RzILOpEffect *store;
|
||
|
||
if (mem_acc_size < 0) {
|
||
NOT_IMPLEMENTED;
|
||
}
|
||
|
||
// EXEC
|
||
switch (id) {
|
||
default:
|
||
NOT_IMPLEMENTED;
|
||
case PPC_INS_DCBZ: {
|
||
ut32 r = ppc_log_2(DCACHE_LINE_SIZE);
|
||
#if CS_NEXT_VERSION >= 6
|
||
rA = cs_reg_name(handle, INSOP(0).mem.base);
|
||
rB = cs_reg_name(handle, INSOP(0).mem.offset);
|
||
base = VARG(rA);
|
||
#else
|
||
rA = cs_reg_name(handle, INSOP(0).reg);
|
||
rB = cs_reg_name(handle, INSOP(1).reg);
|
||
base = rA ? VARG(rA) : NULL;
|
||
#endif
|
||
|
||
ea = base ? ADD(base, VARG(rB)) : VARG(rB);
|
||
// Align EA
|
||
ea = LOGAND(ea, SHIFTL0(UA(-1), U8(r)));
|
||
//! DCACHE_LINE_SIZE is currently hard coded. Should be replaced by config option.
|
||
store = STOREW(ea, UN(DCACHE_LINE_SIZE * 8, 0));
|
||
break;
|
||
}
|
||
case PPC_INS_STB:
|
||
case PPC_INS_STH:
|
||
case PPC_INS_STW:
|
||
case PPC_INS_STD:
|
||
case PPC_INS_STBX:
|
||
case PPC_INS_STHX:
|
||
case PPC_INS_STWX:
|
||
case PPC_INS_STDX:
|
||
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_STBCIX:
|
||
case PPC_INS_STHCIX:
|
||
case PPC_INS_STWCIX:
|
||
case PPC_INS_STDCIX:
|
||
#if CS_NEXT_VERSION >= 6
|
||
base = VARG(rA);
|
||
#else
|
||
base = rA ? VARG(rA) : NULL;
|
||
#endif
|
||
if (ppc_is_x_form(id)) {
|
||
disp = VARG(rB);
|
||
} else {
|
||
disp = EXTEND(PPC_ARCH_BITS, S16(d));
|
||
}
|
||
ea = base ? ADD(base, disp) : disp;
|
||
store = STOREW(ea, CAST(mem_acc_size, IL_FALSE, VARG(rS)));
|
||
break;
|
||
case PPC_INS_STDCX:
|
||
case PPC_INS_STWCX:
|
||
// Float
|
||
case PPC_INS_STFD:
|
||
case PPC_INS_STFDU:
|
||
case PPC_INS_STFDUX:
|
||
case PPC_INS_STFDX:
|
||
case PPC_INS_STFIWX:
|
||
case PPC_INS_STFS:
|
||
case PPC_INS_STFSU:
|
||
case PPC_INS_STFSUX:
|
||
case PPC_INS_STFSX:
|
||
NOT_IMPLEMENTED;
|
||
// Byte reverse and reserved indexed
|
||
case PPC_INS_STHBRX:
|
||
case PPC_INS_STWBRX:
|
||
case PPC_INS_STDBRX:
|
||
NOT_IMPLEMENTED;
|
||
// Multiple word
|
||
case PPC_INS_STMW:
|
||
NOT_IMPLEMENTED;
|
||
// String word
|
||
case PPC_INS_STSWI:
|
||
NOT_IMPLEMENTED;
|
||
// Vector
|
||
case PPC_INS_STVEBX:
|
||
case PPC_INS_STVEHX:
|
||
case PPC_INS_STVEWX:
|
||
case PPC_INS_STVX:
|
||
case PPC_INS_STVXL:
|
||
NOT_IMPLEMENTED;
|
||
// VSX Vector
|
||
case PPC_INS_STXSDX:
|
||
case PPC_INS_STXVD2X:
|
||
case PPC_INS_STXVW4X:
|
||
NOT_IMPLEMENTED;
|
||
}
|
||
|
||
// WRITE
|
||
rz_return_val_if_fail(store, NULL);
|
||
RzILOpEffect *update = update_ra ? SETG(rA, DUP(ea)) : EMPTY();
|
||
return SEQ2(store, update);
|
||
}
|
||
|
||
/**
|
||
* NOTE: Instructions which set the 'OV' bit are not yet supported by capstone.
|
||
*/
|
||
static RzILOpEffect *add_sub_op(RZ_BORROW csh handle, RZ_BORROW cs_insn *insn, bool add, const cs_mode mode) {
|
||
rz_return_val_if_fail(handle && insn, EMPTY());
|
||
ut32 id = insn->id;
|
||
|
||
// READ
|
||
const char *rT = cs_reg_name(handle, INSOP(0).reg);
|
||
const char *rA = cs_reg_name(handle, INSOP(1).reg);
|
||
const char *rB = cs_reg_name(handle, INSOP(2).reg);
|
||
st64 sI = INSOP(2).imm;
|
||
|
||
bool set_ca = (id != PPC_INS_ADD && id != PPC_INS_ADDI && id != PPC_INS_ADDIS && id != PPC_INS_SUBF && id != PPC_INS_NEG);
|
||
bool cr0 = insn->detail->ppc.update_cr0;
|
||
|
||
RzILOpPure *a = NULL;
|
||
RzILOpPure *b = NULL;
|
||
RzILOpPure *c = NULL;
|
||
RzILOpPure *res = NULL;
|
||
|
||
// How to read instruction ids:
|
||
// Letter Meaning
|
||
// ADD/SUBF Add, Subtract from
|
||
// I/M/Z Immediate, Minus one, Zero extend,
|
||
// C/E/S Carry (sets it), Extends (adds carry it), Shift immediate
|
||
|
||
#if CS_NEXT_VERSION >= 6
|
||
// Handle Add alias
|
||
switch (insn->alias_id) {
|
||
default:
|
||
break;
|
||
case PPC_INS_ALIAS_LI: // RT = sI
|
||
return SETG(rT, EXTEND(PPC_ARCH_BITS, SN(16, sI)));
|
||
case PPC_INS_ALIAS_LIS: // RT = SI << 16
|
||
return SETG(rT, EXTEND(PPC_ARCH_BITS, APPEND(SN(16, sI), U16(0))));
|
||
}
|
||
#endif
|
||
|
||
// EXEC
|
||
switch (id) {
|
||
default:
|
||
NOT_IMPLEMENTED;
|
||
case PPC_INS_ADD:
|
||
case PPC_INS_ADDC:
|
||
case PPC_INS_SUBF:
|
||
case PPC_INS_SUBFC:
|
||
a = add ? VARG(rA) : ADD(LOGNOT(VARG(rA)), UA(1));
|
||
b = VARG(rB);
|
||
res = ADD(VARL("a"), VARL("b"));
|
||
break;
|
||
case PPC_INS_ADDE:
|
||
case PPC_INS_SUBFE:
|
||
a = add ? VARG(rA) : LOGNOT(VARG(rA));
|
||
b = VARG(rB);
|
||
c = BOOL_TO_BV(VARG("ca"), PPC_ARCH_BITS);
|
||
res = ADD(VARL("a"), ADD(VARL("b"), VARL("c")));
|
||
break;
|
||
case PPC_INS_ADDI:
|
||
case PPC_INS_ADDIC:
|
||
case PPC_INS_ADDIS:
|
||
case PPC_INS_SUBFIC:
|
||
a = add ? VARG(rA) : LOGNOT(VARG(rA));
|
||
if (id == PPC_INS_ADDIS) {
|
||
b = EXTEND(PPC_ARCH_BITS, APPEND(SN(16, sI), U16(0)));
|
||
} else if (id == PPC_INS_SUBFIC) {
|
||
b = EXTEND(PPC_ARCH_BITS, SN(16, sI));
|
||
c = UA(1);
|
||
} else {
|
||
b = EXTEND(PPC_ARCH_BITS, SN(16, sI));
|
||
}
|
||
|
||
if (c) {
|
||
res = ADD(VARL("a"), ADD(VARL("b"), VARL("c")));
|
||
break;
|
||
}
|
||
res = ADD(VARL("a"), VARL("b"));
|
||
break;
|
||
case PPC_INS_ADDME:
|
||
case PPC_INS_ADDZE:
|
||
case PPC_INS_SUBFME:
|
||
case PPC_INS_SUBFZE:
|
||
a = add ? VARG(rA) : LOGNOT(VARG(rA));
|
||
b = BOOL_TO_BV(VARG("ca"), PPC_ARCH_BITS);
|
||
if (id == PPC_INS_ADDME || id == PPC_INS_SUBFME) {
|
||
c = UA(-1);
|
||
res = ADD(VARL("a"), ADD(VARL("b"), VARL("c")));
|
||
} else {
|
||
res = ADD(VARL("a"), VARL("b"));
|
||
}
|
||
break;
|
||
case PPC_INS_NEG:
|
||
a = LOGNOT(VARG(rA));
|
||
b = UA(1);
|
||
res = ADD(VARL("a"), VARL("b"));
|
||
}
|
||
if (!(a && b)) {
|
||
rz_warn_if_reached();
|
||
return NULL;
|
||
}
|
||
|
||
RzILOpEffect *set_carry = set_ca ? ppc_set_carry_add_sub(VARL("a"), VARL("b"), c ? VARL("c") : NULL, mode) : EMPTY();
|
||
|
||
// Instructions which set the OV bit are not supported in capstone.
|
||
// See: https://github.com/capstone-engine/capstone/issues/944
|
||
RzILOpPure *zero = UA(0);
|
||
RzILOpEffect *overflow = EMPTY();
|
||
RzILOpEffect *update_cr0 = cr0 ? ppc_cmp_set_cr(res, zero, true, "cr0", mode) : EMPTY();
|
||
RzILOpEffect *set = SETG(rT, res);
|
||
RzILOpEffect *set_ops = SEQ3(SETL("a", a), SETL("b", b), c ? SETL("c", c) : EMPTY());
|
||
rz_il_op_pure_free(zero);
|
||
return SEQ5(set_ops, set, set_carry, overflow, update_cr0);
|
||
}
|
||
|
||
static bool is_logical_compare(ut32 id) {
|
||
switch (id) {
|
||
case PPC_INS_CMPW:
|
||
case PPC_INS_CMPD:
|
||
case PPC_INS_CMPWI:
|
||
case PPC_INS_CMPDI:
|
||
return true;
|
||
default:
|
||
return false;
|
||
}
|
||
}
|
||
|
||
static RzILOpEffect *compare_op(RZ_BORROW csh handle, RZ_BORROW cs_insn *insn, const cs_mode mode) {
|
||
rz_return_val_if_fail(handle && insn, EMPTY());
|
||
ut32 id = insn->id;
|
||
const char *crX = NULL;
|
||
const char *rA = NULL;
|
||
const char *rB = NULL;
|
||
st64 imm = 0;
|
||
|
||
RzILOpPure *left = NULL;
|
||
RzILOpPure *right = NULL;
|
||
|
||
bool signed_cmp = false;
|
||
|
||
#if CS_API_MAJOR == 5 && CS_NEXT_VERSION < 6
|
||
// weird bug on cmp/cmpl in capstone v5
|
||
if (id == PPC_INS_CMP) {
|
||
if (!strcmp(insn->mnemonic, "cmpw")) {
|
||
id = PPC_INS_CMPW;
|
||
} else if (!strcmp(insn->mnemonic, "cmpd")) {
|
||
id = PPC_INS_CMPD;
|
||
} else {
|
||
rz_warn_if_reached();
|
||
}
|
||
} else if (id == PPC_INS_CMPL) {
|
||
if (!strcmp(insn->mnemonic, "cmplw")) {
|
||
id = PPC_INS_CMPLW;
|
||
} else if (!strcmp(insn->mnemonic, "cmpld")) {
|
||
id = PPC_INS_CMPLD;
|
||
} else {
|
||
rz_warn_if_reached();
|
||
}
|
||
} else if (id == PPC_INS_CMPI) {
|
||
if (!strcmp(insn->mnemonic, "cmpwi")) {
|
||
id = PPC_INS_CMPWI;
|
||
} else if (!strcmp(insn->mnemonic, "cmpdi")) {
|
||
id = PPC_INS_CMPDI;
|
||
} else {
|
||
rz_warn_if_reached();
|
||
}
|
||
} else if (id == PPC_INS_CMPLI) {
|
||
if (!strcmp(insn->mnemonic, "cmplwi")) {
|
||
id = PPC_INS_CMPLWI;
|
||
} else if (!strcmp(insn->mnemonic, "cmpldi")) {
|
||
id = PPC_INS_CMPLDI;
|
||
} else {
|
||
rz_warn_if_reached();
|
||
}
|
||
}
|
||
#endif
|
||
|
||
// READ
|
||
#if CS_NEXT_VERSION >= 6
|
||
// Uses REAL instruction operand set.
|
||
crX = cs_reg_name(handle, INSOP(0).reg);
|
||
rA = cs_reg_name(handle, INSOP(1).reg);
|
||
rB = cs_reg_name(handle, INSOP(2).reg);
|
||
imm = INSOP(2).imm;
|
||
#else
|
||
// cr0 reg is not explicitly stored in the operands list.
|
||
if (OP_CNT == 2) {
|
||
crX = "cr0";
|
||
rA = cs_reg_name(handle, INSOP(0).reg);
|
||
rB = cs_reg_name(handle, INSOP(1).reg);
|
||
imm = INSOP(1).imm;
|
||
} else {
|
||
crX = cs_reg_name(handle, INSOP(0).reg);
|
||
rA = cs_reg_name(handle, INSOP(1).reg);
|
||
rB = cs_reg_name(handle, INSOP(2).reg);
|
||
imm = INSOP(2).imm;
|
||
}
|
||
#endif
|
||
|
||
// How to read instruction ids:
|
||
// Letter Meaning
|
||
// CMP Compare
|
||
// B/H/W/D Byte, Half Word, Word, Double Word
|
||
// I/L Immediate, Logical (unsigned compare)
|
||
|
||
// EXEC
|
||
// Logical <=> unsigned comparisons ; Not logical <=> signed comparison.
|
||
signed_cmp = is_logical_compare(id);
|
||
|
||
// Left operand is always RA
|
||
if (id == PPC_INS_CMPW || id == PPC_INS_CMPWI || id == PPC_INS_CMPLW || id == PPC_INS_CMPLWI) {
|
||
left = EXTS(CAST(32, IL_FALSE, VARG(rA)));
|
||
} else {
|
||
left = VARG(rA);
|
||
}
|
||
|
||
// Right operand differs between instructions.
|
||
switch (id) {
|
||
default:
|
||
NOT_IMPLEMENTED;
|
||
case PPC_INS_CMPW:
|
||
case PPC_INS_CMPLW:
|
||
right = EXTS(CAST(32, IL_FALSE, VARG(rB)));
|
||
break;
|
||
case PPC_INS_CMPD:
|
||
case PPC_INS_CMPLD:
|
||
right = VARG(rB);
|
||
break;
|
||
case PPC_INS_CMPWI:
|
||
case PPC_INS_CMPLWI:
|
||
right = (id == PPC_INS_CMPWI) ? EXTS(S16(imm)) : EXTZ(U16(imm));
|
||
break;
|
||
case PPC_INS_CMPDI:
|
||
case PPC_INS_CMPLDI:
|
||
right = (id == PPC_INS_CMPDI) ? EXTEND(64, S16(imm)) : APPEND(U48(0), U16(imm));
|
||
break;
|
||
}
|
||
RzILOpEffect *ret = ppc_cmp_set_cr(left, right, signed_cmp, crX, mode);
|
||
rz_il_op_pure_free(left);
|
||
rz_il_op_pure_free(right);
|
||
return ret;
|
||
}
|
||
|
||
#if CS_API_MAJOR == 5 && CS_NEXT_VERSION < 6
|
||
// bug on xori in capstone v5
|
||
static bool is_xnop(cs_insn *insn) {
|
||
return insn->id == PPC_INS_XNOP &&
|
||
INSOP(0).reg == 0 &&
|
||
INSOP(1).reg == 0 &&
|
||
INSOP(2).imm == 0;
|
||
}
|
||
#endif
|
||
|
||
static RzILOpEffect *bitwise_op(RZ_BORROW csh handle, RZ_BORROW cs_insn *insn, const cs_mode mode) {
|
||
rz_return_val_if_fail(handle && insn, EMPTY());
|
||
#if CS_API_MAJOR == 5 && CS_NEXT_VERSION < 6
|
||
if (is_xnop(insn)) {
|
||
return NOP();
|
||
}
|
||
#endif
|
||
|
||
ut32 id = insn->id;
|
||
// READ
|
||
const char *rA = cs_reg_name(handle, INSOP(0).reg);
|
||
const char *rS = cs_reg_name(handle, INSOP(1).reg);
|
||
const char *rB = cs_reg_name(handle, INSOP(2).reg);
|
||
st64 uI = INSOP(2).imm;
|
||
bool cr0 = insn->detail->ppc.update_cr0;
|
||
RzILOpPure *op0;
|
||
RzILOpPure *op1;
|
||
RzILOpPure *res;
|
||
|
||
// How to read instruction ids:
|
||
// Letter Meaning
|
||
// AND/OR/... AND, OR etc.
|
||
// B/H/W/D Byte, Half Word, Word, Double Word
|
||
// I/C/S Immediate, Complement, Shifted
|
||
|
||
// EXEC
|
||
switch (id) {
|
||
default:
|
||
NOT_IMPLEMENTED;
|
||
case PPC_INS_AND:
|
||
case PPC_INS_ANDC:
|
||
case PPC_INS_ANDIS:
|
||
case PPC_INS_ANDI:
|
||
op0 = VARG(rS);
|
||
if (id == PPC_INS_AND || id == PPC_INS_ANDC) {
|
||
op1 = (id == PPC_INS_AND) ? VARG(rB) : LOGNOT(VARG(rB));
|
||
} else {
|
||
op1 = (id == PPC_INS_ANDI) ? EXTZ(U16(uI)) : EXTZ(APPEND(U16(uI), U16(0)));
|
||
}
|
||
res = LOGAND(op0, op1);
|
||
break;
|
||
case PPC_INS_OR:
|
||
case PPC_INS_ORC:
|
||
case PPC_INS_ORI:
|
||
case PPC_INS_ORIS:
|
||
op0 = VARG(rS);
|
||
if (id == PPC_INS_OR || id == PPC_INS_ORC) {
|
||
op1 = (id == PPC_INS_OR) ? VARG(rB) : LOGNOT(VARG(rB));
|
||
} else {
|
||
op1 = (id == PPC_INS_ORI) ? EXTZ(U16(uI)) : EXTZ(APPEND(U16(uI), U16(0)));
|
||
}
|
||
res = LOGOR(op0, op1);
|
||
break;
|
||
#if CS_NEXT_VERSION < 6
|
||
// bug on xori in capstone v5
|
||
case PPC_INS_XNOP:
|
||
op0 = VARG(rS);
|
||
op1 = EXTZ(U16(uI));
|
||
res = LOGXOR(op0, op1);
|
||
break;
|
||
#endif
|
||
case PPC_INS_XOR:
|
||
case PPC_INS_XORI:
|
||
case PPC_INS_XORIS:
|
||
op0 = VARG(rS);
|
||
if (id == PPC_INS_XOR) {
|
||
op1 = VARG(rB);
|
||
} else {
|
||
op1 = (id == PPC_INS_XORI) ? EXTZ(U16(uI)) : EXTZ(APPEND(U16(uI), U16(0)));
|
||
}
|
||
res = LOGXOR(op0, op1);
|
||
break;
|
||
case PPC_INS_NAND:
|
||
case PPC_INS_NOR:
|
||
op0 = VARG(rS);
|
||
op1 = VARG(rB);
|
||
res = LOGNOT(
|
||
(id == PPC_INS_NAND) ? LOGAND(op0, op1) : LOGOR(op0, op1));
|
||
break;
|
||
// Compare bytes
|
||
case PPC_INS_CMPB: {
|
||
// do n = 0 to (64BIT_CPU ? 7 : 3)
|
||
// if RS[8×n:8×n+7] = RB[8×n:8×n+7] then
|
||
// RA[8×n:8×n+7] ← 0b1111_1111
|
||
// else
|
||
// RA[8×n:8×n+7] ← 0b0000_0000
|
||
|
||
ut8 bits = PPC_ARCH_BITS;
|
||
RzILOpEffect *loop, *init_n, *init_bitmask, *ones_rA;
|
||
|
||
RzILOpPure *bitmask_n_n7 = BIT_MASK(bits, MUL(U8(8), VARL("n")), ADD(MUL(U8(8), VARL("n")), U8(7)));
|
||
// RS[8×n:8×n+7]
|
||
RzILOpPure *rS_8n_8n7 = LOGAND(VARL("bitmask_n_n7"), VARG(rS));
|
||
// RB[8×n:8×n+7]
|
||
RzILOpPure *rB_8n_8n7 = LOGAND(VARL("bitmask_n_n7"), VARG(rB));
|
||
RzILOpPure *b_cond = EQ(rS_8n_8n7, rB_8n_8n7);
|
||
|
||
init_n = SETL("n", U8(0));
|
||
init_bitmask = SETL("bitmask_n_n7", bitmask_n_n7);
|
||
// RA[8×n:8×n+7] ← 0b1111_1111
|
||
ones_rA = SETL("res", LOGOR(VARL("res"), LOGAND(UA(-1), VARL("bitmask_n_n7"))));
|
||
ut8 m = IN_64BIT_MODE ? 8 : 4;
|
||
|
||
loop = REPEAT(ULT(VARL("n"), U8(m)),
|
||
SEQ3(BRANCH(b_cond,
|
||
ones_rA, EMPTY()),
|
||
SETL("n", ADD(VARL("n"), U8(1))),
|
||
SETL("bitmask_n_n7", DUP(bitmask_n_n7))));
|
||
|
||
return SEQ5(SETL("res", UA(0)), init_n, init_bitmask, loop, SETG(rA, VARL("res")));
|
||
}
|
||
case PPC_INS_EQV:
|
||
op0 = VARG(rS);
|
||
op1 = VARG(rB);
|
||
res = LOGXOR(op0, LOGNOT(op1));
|
||
break;
|
||
// Extend
|
||
case PPC_INS_EXTSB:
|
||
res = EXTS(UNSIGNED(PPC_BYTE, VARG(rS)));
|
||
break;
|
||
case PPC_INS_EXTSH:
|
||
res = EXTS(UNSIGNED(PPC_HWORD, VARG(rS)));
|
||
break;
|
||
case PPC_INS_EXTSW:
|
||
res = EXTS(UNSIGNED(PPC_WORD, VARG(rS)));
|
||
break;
|
||
// Count leading zeros
|
||
case PPC_INS_CNTLZD:
|
||
case PPC_INS_CNTLZW: {
|
||
RzILOpEffect *n, *set_m;
|
||
if ((id == PPC_INS_CNTLZW) && IN_64BIT_MODE) {
|
||
n = SETL("n", UA(32));
|
||
set_m = SETL("m", UA(32));
|
||
} else {
|
||
n = SETL("n", UA(0));
|
||
set_m = SETL("m", UA(0));
|
||
}
|
||
RzILOpPure *cond = AND(ULT(VARL("n"), UA(PPC_ARCH_BITS)), INV(BIT_IS_SET(VARG(rS), PPC_ARCH_BITS, VARL("n"))));
|
||
RzILOpEffect *cloop = REPEAT(cond, SETL("n", ADD(VARL("n"), UA(1))));
|
||
return SEQ4(set_m, n, cloop, SETG(rA, SUB(VARL("n"), VARL("m"))));
|
||
}
|
||
// Population count
|
||
case PPC_INS_POPCNTD:
|
||
case PPC_INS_POPCNTW:
|
||
NOT_IMPLEMENTED;
|
||
}
|
||
|
||
// WRITE
|
||
RzILOpPure *zero = UA(0);
|
||
RzILOpPure *old_res = VARL("res");
|
||
RzILOpEffect *update_cr0 = cr0 ? ppc_cmp_set_cr(old_res, zero, true, "cr0", mode) : EMPTY();
|
||
RzILOpEffect *set = SETG(rA, old_res);
|
||
rz_il_op_pure_free(zero);
|
||
return SEQ3(SETL("res", res), set, update_cr0);
|
||
}
|
||
|
||
static RzILOpEffect *branch_op(RZ_BORROW csh handle, RZ_BORROW cs_insn *insn, const cs_mode mode) {
|
||
rz_return_val_if_fail(handle && insn, EMPTY());
|
||
#if CS_NEXT_VERSION >= 6
|
||
bool is_conditional = ppc_insn_is_conditional(insn);
|
||
#else
|
||
ut32 id = insn->id;
|
||
bool is_conditional = ppc_is_conditional(id);
|
||
#endif
|
||
RzILOpEffect *set_cia; // Current instruction address
|
||
RzILOpEffect *set_nia; // Next instruction address
|
||
RzILOpEffect *set_lr; // Set Link Register
|
||
RzILOpEffect *decr_ctr; // Effect to decrement CTR
|
||
|
||
// How to read instruction ids:
|
||
// Letter Meaning
|
||
// B Branch
|
||
// C/D Conditional, Decrement CTR
|
||
// Z/NZ/T/F Branch if CTR is: zero/not zero, branch if CR=1 (true)/CR=0 (false)
|
||
// L/A/LR/CTR/TAR Set LR, branch to absolute address, branch to LR, branch to CTR, branch to target address register
|
||
|
||
// Determine next instruction address.
|
||
if (!is_conditional) {
|
||
set_nia = SETL("NIA", ppc_get_branch_ta(insn, mode));
|
||
} else {
|
||
set_nia = SETL("NIA", ITE(ppc_get_branch_cond(handle, insn, mode), ppc_get_branch_ta(insn, mode), ADD(VARL("CIA"), UA(4))));
|
||
}
|
||
|
||
set_cia = SETL("CIA", UA(insn->address));
|
||
#if CS_NEXT_VERSION >= 6
|
||
set_lr = ppc_insn_sets_lr(insn) ? SETG("lr", ADD(VARL("CIA"), UA(4))) : EMPTY();
|
||
#else
|
||
set_lr = ppc_sets_lr(id) ? SETG("lr", ADD(VARL("CIA"), UA(4))) : EMPTY();
|
||
#endif
|
||
decr_ctr = ppc_decrements_ctr(insn, mode) ? SETG("ctr", SUB(VARG("ctr"), UA(1))) : EMPTY();
|
||
|
||
return SEQ5(set_cia, decr_ctr, set_lr, set_nia, JMP(VARL("NIA")));
|
||
}
|
||
|
||
/**
|
||
* NOTE: Instructions which set the 'OV' bit are not yet supported by Capstone.
|
||
*/
|
||
static RzILOpEffect *div_mul_op(RZ_BORROW csh handle, RZ_BORROW cs_insn *insn, const cs_mode mode) {
|
||
rz_return_val_if_fail(handle && insn, EMPTY());
|
||
ut32 id = insn->id;
|
||
|
||
// READ
|
||
const char *rT = cs_reg_name(handle, INSOP(0).reg);
|
||
const char *rA = cs_reg_name(handle, INSOP(1).reg);
|
||
const char *rB = cs_reg_name(handle, INSOP(2).reg);
|
||
st64 sI = INSOP(2).imm;
|
||
bool set_cr0 = insn->detail->ppc.update_cr0;
|
||
|
||
RzILOpPure *op0;
|
||
RzILOpPure *op1;
|
||
RzILOpPure *prod;
|
||
|
||
// How to read instruction IDs:
|
||
// Letter Meaning
|
||
// MUL/DIV Multiply, Divide
|
||
// W/D/LW/HW/LI Word, double word, low word, high word, Low immediate
|
||
// O/U/E Overflow (not supported), Unsigned, Extended
|
||
|
||
if (id == PPC_INS_MULLI) {
|
||
op0 = VARG(rA);
|
||
op1 = S16(sI);
|
||
} else {
|
||
op0 = VARG(rA);
|
||
op1 = VARG(rB);
|
||
}
|
||
|
||
if (id == PPC_INS_MULHW || id == PPC_INS_MULLW || id == PPC_INS_DIVW) {
|
||
op0 = UNSIGNED(32, op0);
|
||
op1 = UNSIGNED(32, op1);
|
||
} else if (id == PPC_INS_DIVWU || id == PPC_INS_MULHWU) {
|
||
op0 = SIGNED(32, op0);
|
||
op1 = SIGNED(32, op1);
|
||
}
|
||
|
||
if (ppc_is_mul_div_u(id) && ppc_is_mul_div_d(id, mode)) {
|
||
op0 = UNSIGNED(128, op0);
|
||
op1 = UNSIGNED(128, op1);
|
||
} else if (ppc_is_mul_div_u(id) && !ppc_is_mul_div_d(id, mode)) {
|
||
op0 = UNSIGNED(64, op0);
|
||
op1 = UNSIGNED(64, op1);
|
||
} else if (!ppc_is_mul_div_u(id) && ppc_is_mul_div_d(id, mode)) {
|
||
op0 = SIGNED(128, op0);
|
||
op1 = SIGNED(128, op1);
|
||
} else {
|
||
op0 = SIGNED(64, op0);
|
||
op1 = SIGNED(64, op1);
|
||
}
|
||
|
||
switch (id) {
|
||
default:
|
||
NOT_IMPLEMENTED;
|
||
case PPC_INS_MULHD:
|
||
case PPC_INS_MULHDU:
|
||
case PPC_INS_MULHW:
|
||
case PPC_INS_MULHWU:
|
||
case PPC_INS_MULLI:
|
||
case PPC_INS_MULLD:
|
||
case PPC_INS_MULLW:
|
||
prod = MUL(op0, op1);
|
||
break;
|
||
case PPC_INS_DIVWU:
|
||
case PPC_INS_DIVDU:
|
||
prod = DIV(op0, op1);
|
||
break;
|
||
case PPC_INS_DIVW:
|
||
case PPC_INS_DIVD:
|
||
prod = SDIV(op0, op1);
|
||
break;
|
||
}
|
||
|
||
// Get high word/double word of product.
|
||
if (id == PPC_INS_MULHW || id == PPC_INS_MULHWU) {
|
||
prod = SHIFTR0(prod, U8(32));
|
||
} else if (id == PPC_INS_MULHD || id == PPC_INS_MULHDU) {
|
||
prod = SHIFTR0(prod, U8(64));
|
||
}
|
||
|
||
RzILOpEffect *cr0 = set_cr0 ? ppc_cmp_set_cr(VARG(rT), UA(0), true, "cr0", mode) : EMPTY();
|
||
return SEQ2(SETG(rT, CAST(PPC_ARCH_BITS, IL_FALSE, prod)), cr0);
|
||
}
|
||
|
||
static RzILOpEffect *move_from_to_spr_op(RZ_BORROW csh handle, RZ_BORROW cs_insn *insn, const cs_mode mode) {
|
||
rz_return_val_if_fail(handle && insn, EMPTY());
|
||
ut32 id = insn->id;
|
||
|
||
#if CS_NEXT_VERSION >= 6
|
||
const char *rS;
|
||
const char *rT;
|
||
if (insn->id == PPC_INS_MFSPR || insn->id == PPC_INS_MTSPR) {
|
||
rT = cs_reg_name(handle, INSOP(0).reg);
|
||
rS = cs_reg_name(handle, INSOP(1).reg);
|
||
} else {
|
||
rS = cs_reg_name(handle, INSOP(0).reg);
|
||
rT = cs_reg_name(handle, INSOP(0).reg);
|
||
}
|
||
#else
|
||
const char *rS = cs_reg_name(handle, INSOP(0).reg);
|
||
const char *rT = cs_reg_name(handle, INSOP(0).reg);
|
||
#endif
|
||
const char *spr_name = "";
|
||
// Some registers need to assemble the value before it is read or written (e.g. XER with all its bits).
|
||
// Leave it NULL if the value of the SPR or RS should be used.
|
||
RzILOpEffect *set_val = NULL;
|
||
// Size of the value written to the target register (SPR or RT)
|
||
ut32 size = PPC_ARCH_BITS;
|
||
|
||
switch (id) {
|
||
default:
|
||
NOT_IMPLEMENTED;
|
||
case PPC_INS_MTDCR:
|
||
case PPC_INS_MFDCR:
|
||
NOT_IMPLEMENTED;
|
||
case PPC_INS_MFVSCR:
|
||
case PPC_INS_MTVSCR:
|
||
NOT_IMPLEMENTED;
|
||
break;
|
||
case PPC_INS_MFMSR:
|
||
spr_name = "msr";
|
||
break;
|
||
case PPC_INS_MTMSR:
|
||
case PPC_INS_MTMSRD:
|
||
NOT_IMPLEMENTED;
|
||
case PPC_INS_MTCRF: {
|
||
ut32 mask = 0xffffffff;
|
||
if (id == PPC_INS_MTCRF) {
|
||
ut8 fxm = INSOP(0).imm;
|
||
rS = cs_reg_name(handle, INSOP(1).reg);
|
||
mask = ppc_fmx_to_mask(fxm);
|
||
}
|
||
RzILOpEffect *set_cr = SETL("cr", LOGAND(UNSIGNED(32, VARG(rS)), U32(mask)));
|
||
return SEQ2(set_cr, ppc_sync_crx_cr(false, mask));
|
||
}
|
||
case PPC_INS_MFCR:
|
||
return SEQ2(ppc_sync_crx_cr(true, 0x0), SETG(rT, EXTZ(VARL("cr"))));
|
||
|
||
// Note: We do not update CR after the OCRF operations.
|
||
case PPC_INS_MTOCRF:
|
||
case PPC_INS_MFOCRF:
|
||
#if CS_NEXT_VERSION >= 6
|
||
rS = cs_reg_name(handle, INSOP(1).reg);
|
||
rT = cs_reg_name(handle, INSOP(0).reg);
|
||
spr_name = rT;
|
||
size = 4;
|
||
set_val = SETL("val", VARG(rS));
|
||
#else
|
||
NOT_IMPLEMENTED;
|
||
#endif
|
||
break;
|
||
// IBM POWER specific Segment Register
|
||
case PPC_INS_MTSRIN:
|
||
case PPC_INS_MFSRIN:
|
||
// Indirect set.
|
||
case PPC_INS_MFSR:
|
||
case PPC_INS_MTSR:
|
||
// Direct set.
|
||
NOT_IMPLEMENTED;
|
||
case PPC_INS_MFLR:
|
||
case PPC_INS_MTLR:
|
||
spr_name = "lr";
|
||
break;
|
||
case PPC_INS_MFCTR:
|
||
case PPC_INS_MTCTR:
|
||
spr_name = "ctr";
|
||
break;
|
||
#if CS_NEXT_VERSION < 6
|
||
case PPC_INS_MFXER:
|
||
case PPC_INS_MTXER:
|
||
if (id == PPC_INS_MTXER) {
|
||
return ppc_set_xer(VARG(rS), mode);
|
||
}
|
||
spr_name = "xer";
|
||
set_val = SETL("val", ppc_get_xer(mode));
|
||
break;
|
||
#endif
|
||
case PPC_INS_MFSPR:
|
||
case PPC_INS_MTSPR: {
|
||
#if CS_NEXT_VERSION >= 6
|
||
if (insn->alias_id == PPC_INS_ALIAS_MTXER) {
|
||
return ppc_set_xer(VARG(rS), mode);
|
||
} else if (insn->alias_id == PPC_INS_ALIAS_MFXER) {
|
||
set_val = SETL("val", ppc_get_xer(mode));
|
||
break;
|
||
}
|
||
#endif
|
||
ut32 spr = INSOP(1).imm;
|
||
switch (spr) {
|
||
default:
|
||
// TODO Invoke different exception handlers. But they are not implemented yet.
|
||
NOT_IMPLEMENTED;
|
||
case 808:
|
||
case 809:
|
||
case 810:
|
||
case 811:
|
||
// Reserved. Treated as No-ops
|
||
RZ_LOG_WARN("Reserved SPR instruction encountered at 0x%" PFMT64x "\n", (ut64)insn->address);
|
||
return EMPTY();
|
||
case 1:
|
||
case 3:
|
||
case 8:
|
||
case 9:
|
||
case 13:
|
||
case 256:
|
||
case 769:
|
||
case 770:
|
||
case 771:
|
||
case 772:
|
||
case 773:
|
||
case 774:
|
||
case 775:
|
||
case 776:
|
||
case 779:
|
||
case 800:
|
||
case 801:
|
||
case 802:
|
||
case 803:
|
||
case 804:
|
||
case 805:
|
||
case 806:
|
||
case 898:
|
||
case 815:
|
||
case 896:
|
||
NOT_IMPLEMENTED;
|
||
}
|
||
break;
|
||
}
|
||
// WRITE/READ only
|
||
case PPC_INS_MTFSB0:
|
||
case PPC_INS_MTFSB1:
|
||
case PPC_INS_MTFSF:
|
||
case PPC_INS_MFFS:
|
||
case PPC_INS_MFTB:
|
||
#if CS_NEXT_VERSION < 6
|
||
case PPC_INS_MFRTCU:
|
||
case PPC_INS_MFRTCL:
|
||
NOT_IMPLEMENTED;
|
||
// Not yet handled
|
||
case PPC_INS_MFBR0:
|
||
case PPC_INS_MFBR1:
|
||
case PPC_INS_MFBR2:
|
||
case PPC_INS_MFBR3:
|
||
case PPC_INS_MFBR4:
|
||
case PPC_INS_MFBR5:
|
||
case PPC_INS_MFBR6:
|
||
case PPC_INS_MFBR7:
|
||
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:
|
||
NOT_IMPLEMENTED;
|
||
case PPC_INS_MFDSCR:
|
||
case PPC_INS_MTDSCR:
|
||
NOT_IMPLEMENTED;
|
||
case PPC_INS_MFDSISR:
|
||
case PPC_INS_MFDAR:
|
||
case PPC_INS_MFSRR2:
|
||
case PPC_INS_MFSRR3:
|
||
case PPC_INS_MFCFAR:
|
||
case PPC_INS_MFAMR:
|
||
case PPC_INS_MFPID:
|
||
case PPC_INS_MFTBLO:
|
||
case PPC_INS_MFTBHI:
|
||
#if CS_NEXT_VERSION >= 6
|
||
case PPC_INS_MFDBATU0:
|
||
case PPC_INS_MFDBATL0:
|
||
case PPC_INS_MFDBATU1:
|
||
case PPC_INS_MFDBATL1:
|
||
case PPC_INS_MFDBATU2:
|
||
case PPC_INS_MFDBATL2:
|
||
case PPC_INS_MFDBATU3:
|
||
case PPC_INS_MFDBATL3:
|
||
case PPC_INS_MFIBATU0:
|
||
case PPC_INS_MFIBATL0:
|
||
case PPC_INS_MFIBATU1:
|
||
case PPC_INS_MFIBATL1:
|
||
case PPC_INS_MFIBATU2:
|
||
case PPC_INS_MFIBATL2:
|
||
case PPC_INS_MFIBATU3:
|
||
case PPC_INS_MFIBATL3:
|
||
#endif
|
||
case PPC_INS_MFDBATU:
|
||
case PPC_INS_MFDBATL:
|
||
case PPC_INS_MFIBATU:
|
||
case PPC_INS_MFIBATL:
|
||
case PPC_INS_MFDCCR:
|
||
case PPC_INS_MFICCR:
|
||
case PPC_INS_MFDEAR:
|
||
case PPC_INS_MFESR:
|
||
case PPC_INS_MFSPEFSCR:
|
||
case PPC_INS_MFTCR:
|
||
case PPC_INS_MFASR:
|
||
case PPC_INS_MFPVR:
|
||
case PPC_INS_MFTBU:
|
||
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:
|
||
#if CS_NEXT_VERSION >= 6
|
||
case PPC_INS_MTDBATU0:
|
||
case PPC_INS_MTDBATL0:
|
||
case PPC_INS_MTDBATU1:
|
||
case PPC_INS_MTDBATL1:
|
||
case PPC_INS_MTDBATU2:
|
||
case PPC_INS_MTDBATL2:
|
||
case PPC_INS_MTDBATU3:
|
||
case PPC_INS_MTDBATL3:
|
||
case PPC_INS_MTIBATU0:
|
||
case PPC_INS_MTIBATL0:
|
||
case PPC_INS_MTIBATU1:
|
||
case PPC_INS_MTIBATL1:
|
||
case PPC_INS_MTIBATU2:
|
||
case PPC_INS_MTIBATL2:
|
||
case PPC_INS_MTIBATU3:
|
||
case PPC_INS_MTIBATL3:
|
||
#endif
|
||
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
|
||
NOT_IMPLEMENTED;
|
||
}
|
||
if (set_val) {
|
||
RzILOpEffect *write_spr = ppc_moves_to_spr(id) ? SETG(spr_name, UNSIGNED(size, VARL("val"))) : SETG(rT, EXTZ(VARL("val")));
|
||
return SEQ2(set_val, write_spr);
|
||
}
|
||
return ppc_moves_to_spr(id) ? SETG(spr_name, UNSIGNED(size, VARG(rS))) : SETG(rT, EXTZ(VARG(spr_name)));
|
||
}
|
||
|
||
/**
|
||
*
|
||
* NOTE: Shift instructions are not implemented as in the programmer reference manual.
|
||
* The manual uses rotate, here we simply use SHIFT ops.
|
||
*/
|
||
static RzILOpEffect *shift_and_rotate(RZ_BORROW csh handle, RZ_BORROW cs_insn *insn, const cs_mode mode) {
|
||
rz_return_val_if_fail(handle && insn, EMPTY());
|
||
ut32 id = insn->id;
|
||
bool sets_cr0 = insn->detail->ppc.update_cr0;
|
||
|
||
// READ
|
||
const char *rA = cs_reg_name(handle, INSOP(0).reg);
|
||
const char *rS = cs_reg_name(handle, INSOP(1).reg);
|
||
const char *rB = cs_reg_name(handle, INSOP(2).reg);
|
||
ut64 sH = INSOP(2).imm;
|
||
ut64 mB = INSOP(3).imm;
|
||
ut64 mE = INSOP(4).imm;
|
||
ut8 b, e; // Mask begin/end
|
||
bool all_bits_set = false;
|
||
|
||
RzILOpPure *n; // Shift/rotate steps
|
||
RzILOpPure *r; // Rotate result
|
||
RzILOpPure *into_rA = NULL;
|
||
RzILOpPure *ca_val; // Arithmetic shift instructions set the ca field.
|
||
RzILOpEffect *set_mask = NULL, *set_ca = NULL, *update_cr0 = NULL;
|
||
|
||
// How to read instruction ids:
|
||
// Letter Meaning
|
||
// R/S Rotate, Shift
|
||
// L/R Left, Right
|
||
// W/D Word, Double Word
|
||
// A/I Algebraic, Immediate
|
||
// C/CL/CR Clear, clear left/right
|
||
// M/NM/MI Mask, AND with mask, mask insert
|
||
|
||
// FIXME: With update to auto-sync ppc arch
|
||
#if CS_API_MAJOR == 5 && CS_API_MINOR == 0 && CS_NEXT_VERSION < 6
|
||
// weird bug on capstone v5.0
|
||
if (id == PPC_INS_CLRLDI && !strcmp(insn->mnemonic, "rldicl")) {
|
||
id = PPC_INS_RLDICL;
|
||
} else if (id == PPC_INS_CLRLWI && !strcmp(insn->mnemonic, "rlwinm")) {
|
||
id = PPC_INS_RLWINM;
|
||
}
|
||
#endif
|
||
#if CS_NEXT_VERSION >= 6
|
||
if (insn->alias_id == PPC_INS_ALIAS_SLWI) {
|
||
id = PPC_INS_SLWI;
|
||
} else if (insn->alias_id == PPC_INS_ALIAS_SRWI) {
|
||
id = PPC_INS_SRWI;
|
||
} else if (insn->alias_id == PPC_INS_ALIAS_SLDI) {
|
||
id = PPC_INS_SLDI;
|
||
}
|
||
#endif
|
||
|
||
switch (id) {
|
||
default:
|
||
NOT_IMPLEMENTED;
|
||
#if CS_NEXT_VERSION < 6
|
||
case PPC_INS_ROTLW:
|
||
case PPC_INS_ROTLWI:
|
||
#endif
|
||
case PPC_INS_RLWIMI:
|
||
case PPC_INS_RLWINM:
|
||
case PPC_INS_RLWNM:
|
||
#if CS_NEXT_VERSION >= 6
|
||
if (insn->alias_id == PPC_INS_ALIAS_CLRLWI ||
|
||
insn->alias_id == PPC_INS_ALIAS_CLRLWI_) {
|
||
break; // Handle down below
|
||
}
|
||
if (id == PPC_INS_RLWNM) {
|
||
#else
|
||
if (id == PPC_INS_RLWNM || id == PPC_INS_ROTLW) {
|
||
#endif
|
||
n = CAST(6, IL_FALSE, LOGAND(VARG(rB), UA(0x1f)));
|
||
} else {
|
||
n = U8(sH);
|
||
}
|
||
r = PPC_IL_ROTL32(UNSIGNED(32, VARG(rS)), n);
|
||
#if CS_NEXT_VERSION >= 6
|
||
b = mB + 32;
|
||
e = mE + 32;
|
||
#else
|
||
if (id == PPC_INS_ROTLW || id == PPC_INS_ROTLWI) {
|
||
b = 32; // mb: 0 + 32
|
||
e = 63; // me: 31 + 32
|
||
} else {
|
||
b = mB + 32;
|
||
e = mE + 32;
|
||
}
|
||
#endif
|
||
// Mask has all bits set.
|
||
all_bits_set = (((b - 1) & 0x3f) == e);
|
||
set_mask = all_bits_set ? NULL : SET_MASK(U8(b), U8(e));
|
||
into_rA = all_bits_set ? r : LOGAND(r, VARL("mask"));
|
||
if (id == PPC_INS_RLWIMI && !all_bits_set) {
|
||
into_rA = LOGOR(into_rA, LOGAND(VARG(rA), LOGNOT(VARL("mask"))));
|
||
}
|
||
break;
|
||
#if CS_NEXT_VERSION < 6
|
||
case PPC_INS_ROTLD:
|
||
case PPC_INS_ROTLDI:
|
||
#endif
|
||
case PPC_INS_RLDCL:
|
||
case PPC_INS_RLDCR:
|
||
case PPC_INS_RLDIC:
|
||
case PPC_INS_RLDICL:
|
||
case PPC_INS_RLDICR:
|
||
case PPC_INS_RLDIMI:
|
||
#if CS_NEXT_VERSION >= 6
|
||
if (insn->alias_id == PPC_INS_ALIAS_CLRLDI ||
|
||
insn->alias_id == PPC_INS_ALIAS_CLRLDI_) {
|
||
break; // Handle below
|
||
}
|
||
if (id == PPC_INS_RLDCR || id == PPC_INS_RLDCL) {
|
||
#else
|
||
if (id == PPC_INS_RLDCR || id == PPC_INS_RLDCL || id == PPC_INS_ROTLD) {
|
||
#endif
|
||
// For these instruction ME is the third operand, not MB.
|
||
mE = INSOP(3).imm;
|
||
n = UNSIGNED(8, VARG(rB));
|
||
} else if (id == PPC_INS_RLDICR) {
|
||
mE = INSOP(3).imm;
|
||
n = U8(sH);
|
||
} else {
|
||
n = U8(sH);
|
||
}
|
||
n = LOGAND(U8(0x3f), n);
|
||
r = PPC_IL_ROTL64(VARG(rS), n);
|
||
#if CS_NEXT_VERSION >= 6
|
||
if (id == PPC_INS_RLDICR || id == PPC_INS_RLDCR) {
|
||
b = 0;
|
||
e = mE;
|
||
} else {
|
||
b = mB;
|
||
if (id == PPC_INS_RLDCL || id == PPC_INS_RLDICL) {
|
||
e = 63;
|
||
} else if (id == PPC_INS_RLDIMI) {
|
||
e = (63 - sH) & 0x3f;
|
||
} else {
|
||
e = sH;
|
||
}
|
||
}
|
||
#else
|
||
if (id == PPC_INS_RLDICR || id == PPC_INS_RLDCR || id == PPC_INS_ROTLDI || id == PPC_INS_ROTLD) {
|
||
b = 0;
|
||
if (id == PPC_INS_ROTLDI || id == PPC_INS_ROTLD) {
|
||
e = 63;
|
||
} else {
|
||
e = mE;
|
||
}
|
||
} else {
|
||
b = mB;
|
||
if (id == PPC_INS_RLDCL || id == PPC_INS_RLDICL) {
|
||
e = 63;
|
||
} else if (id == PPC_INS_RLDIMI) {
|
||
e = (63 - sH) & 0x3f;
|
||
} else {
|
||
e = sH;
|
||
}
|
||
}
|
||
#endif
|
||
|
||
all_bits_set = (((b - 1) & 0x3f) == e);
|
||
set_mask = all_bits_set ? NULL : SET_MASK(U8(b), U8(e));
|
||
into_rA = all_bits_set ? r : LOGAND(r, VARL("mask"));
|
||
if (id == PPC_INS_RLDIMI && !all_bits_set) {
|
||
into_rA = LOGOR(into_rA, LOGAND(VARG(rA), LOGNOT(VARL("mask"))));
|
||
}
|
||
break;
|
||
case PPC_INS_SLDI:
|
||
#if CS_NEXT_VERSION < 6
|
||
// Currently broken in rizins capstone version.
|
||
// Immediate is not in instruction.
|
||
NOT_IMPLEMENTED;
|
||
#endif
|
||
case PPC_INS_SLD:
|
||
case PPC_INS_SRD:
|
||
case PPC_INS_SLWI:
|
||
case PPC_INS_SRWI:
|
||
if (id == PPC_INS_SLD || id == PPC_INS_SRD) {
|
||
n = VARG(rB);
|
||
} else {
|
||
n = UA(sH);
|
||
}
|
||
n = LOGAND(UA(0x3f), n);
|
||
if (id == PPC_INS_SRD) {
|
||
into_rA = SHIFTR0(VARG(rS), n);
|
||
} else if (id == PPC_INS_SRWI) {
|
||
into_rA = SHIFTR0(LOGAND(VARG(rS), UA(0xffffffff)), n);
|
||
} else {
|
||
into_rA = SHIFTL0(VARG(rS), n);
|
||
}
|
||
if (id == PPC_INS_SLWI || id == PPC_INS_SRWI) {
|
||
into_rA = LOGAND(into_rA, UA(0xffffffff)); // Clear high 32bits
|
||
}
|
||
break;
|
||
case PPC_INS_SRAD:
|
||
case PPC_INS_SRADI:
|
||
if (id == PPC_INS_SRAD) {
|
||
n = CAST(6, IL_FALSE, LOGAND(VARG(rB), UA(0x3f)));
|
||
} else {
|
||
n = U8(sH);
|
||
}
|
||
into_rA = SHIFTRA(VARG(rS), n);
|
||
// Set ca to 1 if RS is negative and 1s were shifted out.
|
||
ca_val = ITE(AND(MSB(VARG(rS)),
|
||
NON_ZERO(MOD(VARG(rS), EXTZ(SHIFTL0(UA(1), DUP(n)))))), // (RS % (1 << n)) != 0
|
||
IL_TRUE,
|
||
IL_FALSE);
|
||
set_ca = SETG("ca", ca_val);
|
||
break;
|
||
case PPC_INS_SLW:
|
||
case PPC_INS_SRW:
|
||
into_rA = (id == PPC_INS_SLW) ? SHIFTL0(UNSIGNED(32, VARG(rS)), VARG(rB)) : SHIFTR0(UNSIGNED(32, VARG(rS)), VARG(rB));
|
||
if (IN_64BIT_MODE) {
|
||
into_rA = APPEND(U32(0), into_rA);
|
||
}
|
||
break;
|
||
case PPC_INS_SRAW:
|
||
case PPC_INS_SRAWI:
|
||
n = (id == PPC_INS_SRAW) ? CAST(6, IL_FALSE, LOGAND(VARG(rB), UA(0x3f))) : U8(sH);
|
||
into_rA = EXTS(SHIFTRA(UNSIGNED(32, VARG(rS)), n));
|
||
|
||
// Set ca to 1 if RS is negative and 1s were shifted out.
|
||
ca_val = ITE(AND(MSB(SIGNED(32, VARG(rS))), // (RS < 0) &&
|
||
NON_ZERO(MOD(UNSIGNED(32, VARG(rS)), UNSIGNED(32, SHIFTL0(UA(1), DUP(n)))))), // (RS % (1 << n)) != 0
|
||
IL_TRUE,
|
||
IL_FALSE);
|
||
set_ca = SETG("ca", ca_val);
|
||
break;
|
||
#if CS_NEXT_VERSION < 6
|
||
case PPC_INS_CLRLDI:
|
||
case PPC_INS_CLRLWI:
|
||
r = VARG(rS);
|
||
b = (id == PPC_INS_CLRLWI) ? INSOP(2).imm + 32 : INSOP(2).imm;
|
||
e = 63;
|
||
all_bits_set = (((b - 1) & 0x3f) == e);
|
||
set_mask = all_bits_set ? NULL : SET_MASK(U8(b), U8(e));
|
||
into_rA = all_bits_set ? r : LOGAND(r, VARL("mask"));
|
||
}
|
||
#else
|
||
}
|
||
if (insn->alias_id == PPC_INS_ALIAS_CLRLDI ||
|
||
insn->alias_id == PPC_INS_ALIAS_CLRLWI ||
|
||
insn->alias_id == PPC_INS_ALIAS_CLRLDI_ ||
|
||
insn->alias_id == PPC_INS_ALIAS_CLRLWI_) {
|
||
r = VARG(rS);
|
||
b = (insn->alias_id == PPC_INS_ALIAS_CLRLWI) ? INSOP(3).imm + 32 : INSOP(3).imm;
|
||
e = 63;
|
||
all_bits_set = (((b - 1) & 0x3f) == e);
|
||
set_mask = all_bits_set ? NULL : SET_MASK(U8(b), U8(e));
|
||
into_rA = all_bits_set ? r : LOGAND(r, VARL("mask"));
|
||
}
|
||
#endif
|
||
|
||
RzILOpPure *zero = UA(0);
|
||
RzILOpPure *old_res = VARL("result");
|
||
update_cr0 = sets_cr0 ? ppc_cmp_set_cr(old_res, zero, true, "cr0", mode) : EMPTY();
|
||
set_mask = set_mask ? set_mask : EMPTY();
|
||
set_ca = set_ca ? set_ca : EMPTY();
|
||
rz_il_op_pure_free(zero);
|
||
|
||
return SEQ5(set_mask, set_ca, SETL("result", into_rA), SETG(rA, old_res), update_cr0);
|
||
}
|
||
|
||
static RzILOpEffect *sys(RZ_BORROW csh handle, RZ_BORROW cs_insn *insn, const cs_mode mode) {
|
||
rz_return_val_if_fail(handle && insn, EMPTY());
|
||
ut32 id = insn->id;
|
||
|
||
switch (id) {
|
||
default:
|
||
NOT_IMPLEMENTED;
|
||
case PPC_INS_SC:
|
||
case PPC_INS_TRAP:
|
||
case PPC_INS_TW:
|
||
case PPC_INS_TD:
|
||
case PPC_INS_TWI:
|
||
case PPC_INS_TDI:
|
||
return NOP();
|
||
}
|
||
}
|
||
|
||
static RzILOpEffect *cr_logical(RZ_BORROW csh handle, RZ_BORROW cs_insn *insn, const cs_mode mode) {
|
||
rz_return_val_if_fail(handle && insn, EMPTY());
|
||
ut32 id = insn->id;
|
||
|
||
if (id == PPC_INS_MCRF) {
|
||
return SETG(cs_reg_name(handle, INSOP(0).reg), VARG(cs_reg_name(handle, INSOP(1).reg)));
|
||
}
|
||
NOT_IMPLEMENTED;
|
||
}
|
||
|
||
/**
|
||
* \brief Returns the RZIL implementation of a given capstone instruction.
|
||
* Or NULL if the instruction is not yet implemented.
|
||
*
|
||
* \param handle The capstone handle.
|
||
* \param insn The capstone instruction.
|
||
* \param mode The capstone mode.
|
||
* \return RzILOpEffect* Sequence of effects which emulate the instruction.
|
||
*/
|
||
RZ_IPI RzILOpEffect *rz_ppc_cs_get_il_op(RZ_BORROW csh handle, RZ_BORROW cs_insn *insn, const cs_mode mode) {
|
||
rz_return_val_if_fail(handle && insn, EMPTY());
|
||
rz_return_val_if_fail(insn->detail, EMPTY());
|
||
RzILOpEffect *lop;
|
||
switch (insn->id) {
|
||
default:
|
||
NOT_IMPLEMENTED;
|
||
case PPC_INS_INVALID:
|
||
// TODO Exception
|
||
NOT_IMPLEMENTED;
|
||
case PPC_INS_NOP:
|
||
#if CS_API_MAJOR < 5
|
||
// bug on xori in capstone v5
|
||
case PPC_INS_XNOP:
|
||
#endif
|
||
case PPC_INS_DCBT:
|
||
case PPC_INS_DCBTST:
|
||
// Everything is executed linear => Sync instructions are NOP()s.
|
||
case PPC_INS_ISYNC:
|
||
case PPC_INS_SYNC:
|
||
#if CS_NEXT_VERSION < 6
|
||
case PPC_INS_LWSYNC:
|
||
case PPC_INS_MSYNC:
|
||
case PPC_INS_PTESYNC:
|
||
#endif
|
||
case PPC_INS_TLBSYNC:
|
||
lop = NOP();
|
||
break;
|
||
case PPC_INS_ADD:
|
||
case PPC_INS_ADDC:
|
||
case PPC_INS_ADDE:
|
||
case PPC_INS_ADDI:
|
||
case PPC_INS_ADDIC:
|
||
case PPC_INS_ADDIS:
|
||
case PPC_INS_ADDME:
|
||
case PPC_INS_ADDZE:
|
||
lop = add_sub_op(handle, insn, true, mode);
|
||
break;
|
||
case PPC_INS_NEG:
|
||
case PPC_INS_SUBF:
|
||
case PPC_INS_SUBFC:
|
||
case PPC_INS_SUBFE:
|
||
case PPC_INS_SUBFIC:
|
||
case PPC_INS_SUBFME:
|
||
case PPC_INS_SUBFZE:
|
||
lop = add_sub_op(handle, insn, false, mode);
|
||
break;
|
||
case PPC_INS_DIVD:
|
||
case PPC_INS_DIVDU:
|
||
case PPC_INS_DIVW:
|
||
case PPC_INS_DIVWU:
|
||
case PPC_INS_MULHD:
|
||
case PPC_INS_MULHDU:
|
||
case PPC_INS_MULHW:
|
||
case PPC_INS_MULHWU:
|
||
case PPC_INS_MULLD:
|
||
case PPC_INS_MULLI:
|
||
case PPC_INS_MULLW:
|
||
lop = div_mul_op(handle, insn, mode);
|
||
break;
|
||
case PPC_INS_LI:
|
||
case PPC_INS_LIS:
|
||
case PPC_INS_LA:
|
||
case PPC_INS_LBZ:
|
||
case PPC_INS_LBZU:
|
||
case PPC_INS_LBZUX:
|
||
case PPC_INS_LBZX:
|
||
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_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_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_LBZCIX:
|
||
case PPC_INS_LHZCIX:
|
||
case PPC_INS_LWZCIX:
|
||
case PPC_INS_LDCIX:
|
||
lop = load_op(handle, insn, mode);
|
||
break;
|
||
case PPC_INS_STB:
|
||
case PPC_INS_STBU:
|
||
case PPC_INS_STBUX:
|
||
case PPC_INS_STBX:
|
||
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_STFD:
|
||
case PPC_INS_STFDU:
|
||
case PPC_INS_STFDUX:
|
||
case PPC_INS_STFDX:
|
||
case PPC_INS_STFIWX:
|
||
case PPC_INS_STFS:
|
||
case PPC_INS_STFSU:
|
||
case PPC_INS_STFSUX:
|
||
case PPC_INS_STFSX:
|
||
case PPC_INS_STH:
|
||
case PPC_INS_STHBRX:
|
||
case PPC_INS_STHU:
|
||
case PPC_INS_STHUX:
|
||
case PPC_INS_STHX:
|
||
case PPC_INS_STMW:
|
||
case PPC_INS_STSWI:
|
||
case PPC_INS_STVEBX:
|
||
case PPC_INS_STVEHX:
|
||
case PPC_INS_STVEWX:
|
||
case PPC_INS_STVX:
|
||
case PPC_INS_STVXL:
|
||
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_STXSDX:
|
||
case PPC_INS_STXVD2X:
|
||
case PPC_INS_STXVW4X:
|
||
case PPC_INS_DCBZ:
|
||
case PPC_INS_STHCIX:
|
||
case PPC_INS_STWCIX:
|
||
case PPC_INS_STBCIX:
|
||
case PPC_INS_STDCIX:
|
||
lop = store_op(handle, insn, mode);
|
||
break;
|
||
#if CS_NEXT_VERSION < 6
|
||
case PPC_INS_MR:
|
||
#endif
|
||
case PPC_INS_AND:
|
||
case PPC_INS_ANDC:
|
||
case PPC_INS_ANDIS:
|
||
case PPC_INS_ANDI:
|
||
case PPC_INS_OR:
|
||
case PPC_INS_ORC:
|
||
case PPC_INS_ORI:
|
||
case PPC_INS_ORIS:
|
||
case PPC_INS_NAND:
|
||
case PPC_INS_NOR:
|
||
#if CS_API_MAJOR == 5 && CS_NEXT_VERSION < 6
|
||
// bug on xori in capstone v5
|
||
case PPC_INS_XNOP:
|
||
#endif
|
||
case PPC_INS_XOR:
|
||
case PPC_INS_XORI:
|
||
#if CS_NEXT_VERSION >= 6
|
||
if (insn->is_alias && insn->alias_id == PPC_INS_ALIAS_XNOP) {
|
||
return NOP();
|
||
}
|
||
#endif
|
||
// fallthrough
|
||
case PPC_INS_XORIS:
|
||
case PPC_INS_EQV:
|
||
case PPC_INS_EXTSB:
|
||
case PPC_INS_EXTSH:
|
||
case PPC_INS_EXTSW:
|
||
case PPC_INS_CNTLZD:
|
||
case PPC_INS_CNTLZW:
|
||
case PPC_INS_POPCNTD:
|
||
case PPC_INS_POPCNTW:
|
||
case PPC_INS_CMPB:
|
||
#if CS_API_MAJOR == 5
|
||
case PPC_INS_CMPRB:
|
||
case PPC_INS_CMPEQB:
|
||
#endif
|
||
lop = bitwise_op(handle, insn, mode);
|
||
break;
|
||
#if CS_API_MAJOR == 5 && CS_NEXT_VERSION < 6
|
||
case PPC_INS_CMP:
|
||
case PPC_INS_CMPI:
|
||
case PPC_INS_CMPL:
|
||
case PPC_INS_CMPLI:
|
||
#endif
|
||
case PPC_INS_CMPD:
|
||
case PPC_INS_CMPDI:
|
||
case PPC_INS_CMPLD:
|
||
case PPC_INS_CMPLDI:
|
||
case PPC_INS_CMPLW:
|
||
case PPC_INS_CMPLWI:
|
||
case PPC_INS_CMPW:
|
||
case PPC_INS_CMPWI:
|
||
lop = compare_op(handle, insn, mode);
|
||
break;
|
||
case PPC_INS_B:
|
||
case PPC_INS_BA:
|
||
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_BCTR:
|
||
case PPC_INS_BCTRL:
|
||
case PPC_INS_BL:
|
||
case PPC_INS_BLA:
|
||
case PPC_INS_BLR:
|
||
case PPC_INS_BLRL:
|
||
case PPC_INS_BCA:
|
||
case PPC_INS_BCLA:
|
||
#if CS_NEXT_VERSION < 6
|
||
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:
|
||
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:
|
||
#endif
|
||
#if CS_API_MAJOR == 5 && CS_NEXT_VERSION < 6
|
||
case PPC_INS_BCDCFN:
|
||
case PPC_INS_BCDCFSQ:
|
||
case PPC_INS_BCDCFZ:
|
||
case PPC_INS_BCDCPSGN:
|
||
case PPC_INS_BCDCTN:
|
||
case PPC_INS_BCDCTSQ:
|
||
case PPC_INS_BCDCTZ:
|
||
case PPC_INS_BCDS:
|
||
case PPC_INS_BCDSETSGN:
|
||
case PPC_INS_BCDSR:
|
||
case PPC_INS_BCDTRUNC:
|
||
case PPC_INS_BCDUS:
|
||
case PPC_INS_BCDUTRUNC:
|
||
case PPC_INS_BDNZFLR:
|
||
case PPC_INS_BDNZFLRL:
|
||
case PPC_INS_BDNZTLR:
|
||
case PPC_INS_BDNZTLRL:
|
||
case PPC_INS_BDZFLR:
|
||
case PPC_INS_BDZFLRL:
|
||
case PPC_INS_BDZTLR:
|
||
case PPC_INS_BDZTLRL:
|
||
case PPC_INS_BEQ:
|
||
case PPC_INS_BEQA:
|
||
case PPC_INS_BEQCTR:
|
||
case PPC_INS_BEQCTRL:
|
||
case PPC_INS_BEQL:
|
||
case PPC_INS_BEQLA:
|
||
case PPC_INS_BEQLR:
|
||
case PPC_INS_BEQLRL:
|
||
case PPC_INS_BF:
|
||
case PPC_INS_BFA:
|
||
case PPC_INS_BFCTR:
|
||
case PPC_INS_BFCTRL:
|
||
case PPC_INS_BFL:
|
||
case PPC_INS_BFLA:
|
||
case PPC_INS_BFLR:
|
||
case PPC_INS_BFLRL:
|
||
case PPC_INS_BGE:
|
||
case PPC_INS_BGEA:
|
||
case PPC_INS_BGECTR:
|
||
case PPC_INS_BGECTRL:
|
||
case PPC_INS_BGEL:
|
||
case PPC_INS_BGELA:
|
||
case PPC_INS_BGELR:
|
||
case PPC_INS_BGELRL:
|
||
case PPC_INS_BGT:
|
||
case PPC_INS_BGTA:
|
||
case PPC_INS_BGTCTR:
|
||
case PPC_INS_BGTCTRL:
|
||
case PPC_INS_BGTL:
|
||
case PPC_INS_BGTLA:
|
||
case PPC_INS_BGTLR:
|
||
case PPC_INS_BGTLRL:
|
||
case PPC_INS_BLE:
|
||
case PPC_INS_BLEA:
|
||
case PPC_INS_BLECTR:
|
||
case PPC_INS_BLECTRL:
|
||
case PPC_INS_BLEL:
|
||
case PPC_INS_BLELA:
|
||
case PPC_INS_BLELR:
|
||
case PPC_INS_BLELRL:
|
||
case PPC_INS_BLT:
|
||
case PPC_INS_BLTA:
|
||
case PPC_INS_BLTCTR:
|
||
case PPC_INS_BLTCTRL:
|
||
case PPC_INS_BLTL:
|
||
case PPC_INS_BLTLA:
|
||
case PPC_INS_BLTLR:
|
||
case PPC_INS_BLTLRL:
|
||
case PPC_INS_BNE:
|
||
case PPC_INS_BNEA:
|
||
case PPC_INS_BNECTR:
|
||
case PPC_INS_BNECTRL:
|
||
case PPC_INS_BNEL:
|
||
case PPC_INS_BNELA:
|
||
case PPC_INS_BNELR:
|
||
case PPC_INS_BNELRL:
|
||
case PPC_INS_BNG:
|
||
case PPC_INS_BNGA:
|
||
case PPC_INS_BNGCTR:
|
||
case PPC_INS_BNGCTRL:
|
||
case PPC_INS_BNGL:
|
||
case PPC_INS_BNGLA:
|
||
case PPC_INS_BNGLR:
|
||
case PPC_INS_BNGLRL:
|
||
case PPC_INS_BNL:
|
||
case PPC_INS_BNLA:
|
||
case PPC_INS_BNLCTR:
|
||
case PPC_INS_BNLCTRL:
|
||
case PPC_INS_BNLL:
|
||
case PPC_INS_BNLLA:
|
||
case PPC_INS_BNLLR:
|
||
case PPC_INS_BNLLRL:
|
||
case PPC_INS_BNS:
|
||
case PPC_INS_BNSA:
|
||
case PPC_INS_BNSCTR:
|
||
case PPC_INS_BNSCTRL:
|
||
case PPC_INS_BNSL:
|
||
case PPC_INS_BNSLA:
|
||
case PPC_INS_BNSLR:
|
||
case PPC_INS_BNSLRL:
|
||
case PPC_INS_BNU:
|
||
case PPC_INS_BNUA:
|
||
case PPC_INS_BNUCTR:
|
||
case PPC_INS_BNUCTRL:
|
||
case PPC_INS_BNUL:
|
||
case PPC_INS_BNULA:
|
||
case PPC_INS_BNULR:
|
||
case PPC_INS_BNULRL:
|
||
case PPC_INS_BPERMD:
|
||
case PPC_INS_BSO:
|
||
case PPC_INS_BSOA:
|
||
case PPC_INS_BSOCTR:
|
||
case PPC_INS_BSOCTRL:
|
||
case PPC_INS_BSOL:
|
||
case PPC_INS_BSOLA:
|
||
case PPC_INS_BSOLR:
|
||
case PPC_INS_BSOLRL:
|
||
case PPC_INS_BT:
|
||
case PPC_INS_BTA:
|
||
case PPC_INS_BTCTR:
|
||
case PPC_INS_BTCTRL:
|
||
case PPC_INS_BTL:
|
||
case PPC_INS_BTLA:
|
||
case PPC_INS_BTLR:
|
||
case PPC_INS_BTLRL:
|
||
case PPC_INS_BUN:
|
||
case PPC_INS_BUNA:
|
||
case PPC_INS_BUNCTR:
|
||
case PPC_INS_BUNCTRL:
|
||
case PPC_INS_BUNL:
|
||
case PPC_INS_BUNLA:
|
||
case PPC_INS_BUNLR:
|
||
case PPC_INS_BUNLRL:
|
||
#endif
|
||
lop = branch_op(handle, insn, mode);
|
||
break;
|
||
// These instruction are not in the ISA manual v3.1B.
|
||
case PPC_INS_BRINC:
|
||
NOT_IMPLEMENTED;
|
||
case PPC_INS_MFCR:
|
||
case PPC_INS_MFCTR:
|
||
case PPC_INS_MFDCR:
|
||
case PPC_INS_MFFS:
|
||
case PPC_INS_MFLR:
|
||
case PPC_INS_MFMSR:
|
||
case PPC_INS_MFOCRF:
|
||
case PPC_INS_MFSPR:
|
||
case PPC_INS_MFSR:
|
||
case PPC_INS_MFSRIN:
|
||
case PPC_INS_MFTB:
|
||
case PPC_INS_MFVSCR:
|
||
case PPC_INS_MTCRF:
|
||
case PPC_INS_MTCTR:
|
||
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_MFBR0:
|
||
case PPC_INS_MFBR1:
|
||
case PPC_INS_MFBR2:
|
||
case PPC_INS_MFBR3:
|
||
case PPC_INS_MFBR4:
|
||
case PPC_INS_MFBR5:
|
||
case PPC_INS_MFBR6:
|
||
case PPC_INS_MFBR7:
|
||
case PPC_INS_MFXER:
|
||
case PPC_INS_MFRTCU:
|
||
case PPC_INS_MFRTCL:
|
||
case PPC_INS_MFDSCR:
|
||
case PPC_INS_MFDSISR:
|
||
case PPC_INS_MFDAR:
|
||
case PPC_INS_MFSRR2:
|
||
case PPC_INS_MFSRR3:
|
||
case PPC_INS_MFCFAR:
|
||
case PPC_INS_MFAMR:
|
||
case PPC_INS_MFPID:
|
||
case PPC_INS_MFTBLO:
|
||
case PPC_INS_MFTBHI:
|
||
case PPC_INS_MFDBATU:
|
||
case PPC_INS_MFDBATL:
|
||
case PPC_INS_MFIBATU:
|
||
case PPC_INS_MFIBATL:
|
||
case PPC_INS_MFTBU:
|
||
case PPC_INS_MTCR:
|
||
case PPC_INS_MTDBATU:
|
||
case PPC_INS_MTDBATL:
|
||
case PPC_INS_MTIBATU:
|
||
case PPC_INS_MTIBATL:
|
||
case PPC_INS_MFDCCR:
|
||
case PPC_INS_MFICCR:
|
||
case PPC_INS_MFDEAR:
|
||
case PPC_INS_MFESR:
|
||
case PPC_INS_MFSPEFSCR:
|
||
case PPC_INS_MFTCR:
|
||
case PPC_INS_MFASR:
|
||
case PPC_INS_MFPVR:
|
||
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_MTDCCR:
|
||
case PPC_INS_MTICCR:
|
||
case PPC_INS_MTDEAR:
|
||
case PPC_INS_MTESR:
|
||
case PPC_INS_MTSPEFSCR:
|
||
case PPC_INS_MTTCR:
|
||
#endif
|
||
lop = move_from_to_spr_op(handle, insn, mode);
|
||
break;
|
||
case PPC_INS_ISEL:
|
||
NOT_IMPLEMENTED;
|
||
break;
|
||
case PPC_INS_CREQV:
|
||
case PPC_INS_CRXOR:
|
||
case PPC_INS_CRAND:
|
||
case PPC_INS_CRANDC:
|
||
case PPC_INS_CRNAND:
|
||
case PPC_INS_CRNOR:
|
||
case PPC_INS_CROR:
|
||
case PPC_INS_CRORC:
|
||
#if CS_NEXT_VERSION < 6
|
||
case PPC_INS_CRSET:
|
||
case PPC_INS_CRNOT:
|
||
case PPC_INS_CRMOVE:
|
||
case PPC_INS_CRCLR:
|
||
#endif
|
||
NOT_IMPLEMENTED;
|
||
case PPC_INS_MCRF:
|
||
lop = cr_logical(handle, insn, mode);
|
||
break;
|
||
// Rotate and rotate
|
||
case PPC_INS_RLDCL:
|
||
case PPC_INS_RLDCR:
|
||
case PPC_INS_RLDIC:
|
||
case PPC_INS_RLDICL:
|
||
case PPC_INS_RLDICR:
|
||
case PPC_INS_RLDIMI:
|
||
case PPC_INS_RLWIMI:
|
||
case PPC_INS_RLWINM:
|
||
case PPC_INS_RLWNM:
|
||
#if CS_NEXT_VERSION < 6
|
||
case PPC_INS_ROTLD:
|
||
case PPC_INS_ROTLDI:
|
||
case PPC_INS_CLRLDI:
|
||
case PPC_INS_ROTLWI:
|
||
case PPC_INS_CLRLWI:
|
||
case PPC_INS_ROTLW:
|
||
#endif
|
||
case PPC_INS_SLD:
|
||
case PPC_INS_SLW:
|
||
case PPC_INS_SRAD:
|
||
case PPC_INS_SRADI:
|
||
case PPC_INS_SRAW:
|
||
case PPC_INS_SRAWI:
|
||
case PPC_INS_SRD:
|
||
case PPC_INS_SRW:
|
||
case PPC_INS_SLWI:
|
||
case PPC_INS_SLDI:
|
||
case PPC_INS_SRWI:
|
||
lop = shift_and_rotate(handle, insn, mode);
|
||
break;
|
||
case PPC_INS_SC:
|
||
case PPC_INS_TRAP:
|
||
case PPC_INS_TW:
|
||
case PPC_INS_TD:
|
||
case PPC_INS_TWI:
|
||
case PPC_INS_TDI:
|
||
lop = sys(handle, insn, mode);
|
||
break;
|
||
}
|
||
|
||
return lop;
|
||
}
|
||
|
||
#include <rz_il/rz_il_opbuilder_end.h>
|