summaryrefslogtreecommitdiff
path: root/arch/powerpc/disassembler.cpp
diff options
context:
space:
mode:
authorGalen Williamson <galen@vector35.com>2024-12-16 13:40:25 -0500
committerGalen Williamson <galen@vector35.com>2024-12-16 13:40:25 -0500
commit8ea98cab89afb0c14073141f30203486d1333b07 (patch)
treea749ae52d4c590c06876b0e02656ad811883b082 /arch/powerpc/disassembler.cpp
parentb34ddd03f71c8637a80a264a3ea757b8295d482b (diff)
[ppc] merges PR Vector35/binaryninja-api#5176 (adds support for "psq" Quantized Floating Point instructions, fixes/completes 64-bit instruction/register support)
Diffstat (limited to 'arch/powerpc/disassembler.cpp')
-rw-r--r--arch/powerpc/disassembler.cpp174
1 files changed, 166 insertions, 8 deletions
diff --git a/arch/powerpc/disassembler.cpp b/arch/powerpc/disassembler.cpp
index 66e40d32..2a421971 100644
--- a/arch/powerpc/disassembler.cpp
+++ b/arch/powerpc/disassembler.cpp
@@ -12,14 +12,158 @@ architecture plugin picture.
//#define MYLOG BinaryNinja::LogDebug
#include "disassembler.h"
+#include "util.h"
/* have to do this... while options can be toggled after initialization (thru
cs_option(), the modes cannot, and endianness is considered a mode) */
thread_local csh handle_lil = 0;
thread_local csh handle_big = 0;
+int DoesQualifyForLocalDisassembly(const uint8_t *data, bool bigendian)
+{
+ uint32_t insword = *(uint32_t *)data;
+ int result = PPC_INS_INVALID;
+ uint32_t tmp = 0;
+
+ if(bigendian == true)
+ {
+ insword = bswap32(insword);
+ }
+
+ // 111111xxx00xxxxxxxxxx00001000000 <- fcmpo
+ tmp = insword & 0xFC6007FF;
+ if (tmp==0xFC000040)
+ result = PPC_INS_BN_FCMPO;
+ // 111100xxxxxxxxxxxxxxx00111010xxx <- xxpermr
+ if((insword & 0xFC0007F8) == 0xF00001D0)
+ result = PPC_INS_BN_XXPERMR;
+
+ return result;
+}
+
+void ppc_fcmpo(uint32_t insword, decomp_result *res)
+{
+ unsigned regtmp = 0;
+
+ // 111111AAA00BBBBBCCCCC00001000000 "fcmpo crA,fB,fC"
+ regtmp = PPC_REG_CR0 + ((insword >> 23) & 7);
+ res->detail.ppc.operands[0].reg = (ppc_reg)(regtmp);
+ res->detail.ppc.operands[0].type = PPC_OP_REG;
+
+ regtmp = PPC_REG_F0 + ((insword >> 16) & 31);
+ res->detail.ppc.operands[1].reg = (ppc_reg)(regtmp);
+ res->detail.ppc.operands[1].type = PPC_OP_REG;
+
+ regtmp = PPC_REG_F0 + ((insword >> 11) & 31);
+ res->detail.ppc.operands[2].reg = (ppc_reg)(regtmp);
+ res->detail.ppc.operands[2].type = PPC_OP_REG;
+
+
+#ifdef FORCE_TEST
+ SStream ss;
+ struct cs_struct* handle = 0;
+ struct MCInst tempmc = {0};
+ char* first_space = 0;
+
+ // SStream_Init(&ss);
+ ss.index = 0;
+ ss.buffer[0] = '\0';
+ regtmp = PPC_REG_CR0 + ((insword >> 23) & 7);
+ tempmc.Operands[0].MachineOperandType = MCOperand::kRegister;
+ tempmc.Operands[0].Kind = 1;
+ tempmc.Operands[0].RegVal = regtmp;
+ regtmp = PPC_REG_F0 + ((insword >> 16) & 31);
+ tempmc.Operands[1].MachineOperandType = MCOperand::kRegister;
+ tempmc.Operands[1].Kind = 1;
+ tempmc.Operands[1].RegVal = regtmp;
+ regtmp = PPC_REG_F0 + ((insword >> 11) & 31);
+ tempmc.Operands[2].Kind = 1;
+ tempmc.Operands[2].MachineOperandType = MCOperand::kRegister;
+ tempmc.Operands[2].RegVal = regtmp;
+
+ // temporarily set this so that print processing succeeds
+ res->insn.id = PPC_INS_FCMPU;
+
+ if (handle_big != 0)
+ {
+ handle = (struct cs_struct*)handle_big;
+ }
+ else if (handle_lil != 0)
+ {
+ handle = (struct cs_struct*)handle_lil;
+ }
+
+#define PPC_FCMPUS 804
+
+ tempmc.csh = handle;
+ tempmc.Opcode = PPC_FCMPUS;
+ tempmc.flat_insn = &res->insn;
+ tempmc.flat_insn->detail = &res->detail;
+
+ if (handle != 0)
+ {
+ handle->printer(&tempmc, &ss, handle->printer_info);
+ }
+
+ // replace the 'fcmpu' with 'fcmpo'
+ first_space = strchr(ss.buffer, ' ');
+ strncpy(res->insn.op_str, first_space + 1, sizeof(res->insn.op_str));
+#endif
+
+ strncpy(res->insn.mnemonic, "fcmpo", sizeof(res->insn.mnemonic));
+
+ // reset this to the target value
+ res->insn.id = PPC_INS_BN_FCMPO;
+ res->detail.ppc.op_count = 3;
+}
+
+void ppc_xxpermr(uint32_t insword, decomp_result *res)
+{
+ // 111100AAAAABBBBBCCCCC00011010BCA "xxpermr vsA,vsB,vsC"
+ int a = ((insword & 0x3E00000)>>21)|((insword & 0x1)<<5);
+ int b = ((insword & 0x1F0000)>>16)|((insword & 0x4)<<3);
+ int c = ((insword & 0xF800)>>11)|((insword & 0x2)<<4);
+
+ res->detail.ppc.operands[0].reg = (ppc_reg)(PPC_REG_VS0 + a);
+ res->detail.ppc.operands[0].type = PPC_OP_REG;
+ res->detail.ppc.operands[1].reg = (ppc_reg)(PPC_REG_VS0 + b);
+ res->detail.ppc.operands[1].type = PPC_OP_REG;
+ res->detail.ppc.operands[2].reg = (ppc_reg)(PPC_REG_VS0 + c);
+ res->detail.ppc.operands[2].type = PPC_OP_REG;
+
+ res->insn.id = PPC_INS_BN_XXPERMR;
+ res->detail.ppc.op_count = 3;
+ strncpy(res->insn.mnemonic, "xxpermr", sizeof(res->insn.mnemonic));
+}
+
+bool PerformLocalDisassembly(const uint8_t *data, uint64_t addr, size_t &len, decomp_result* res, bool bigendian)
+{
+ uint32_t local_op = 0;
+ uint32_t insword = *(uint32_t *)data;
+
+ if(bigendian == true)
+ {
+ insword = bswap32(insword);
+ }
+
+ local_op = DoesQualifyForLocalDisassembly(data, bigendian);
+
+ switch(local_op)
+ {
+ case PPC_INS_BN_FCMPO:
+ ppc_fcmpo(insword, res);
+ break;
+ case PPC_INS_BN_XXPERMR:
+ ppc_xxpermr(insword, res);
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
extern "C" int
-powerpc_init(void)
+powerpc_init(int cs_mode_arg)
{
int rc = -1;
@@ -31,12 +175,12 @@ powerpc_init(void)
}
/* initialize capstone handle */
- if(cs_open(CS_ARCH_PPC, CS_MODE_BIG_ENDIAN, &handle_big) != CS_ERR_OK) {
+ if(cs_open(CS_ARCH_PPC, (cs_mode)((int)CS_MODE_BIG_ENDIAN | cs_mode_arg), &handle_big) != CS_ERR_OK) {
MYLOG("ERROR: cs_open()\n");
goto cleanup;
}
- if(cs_open(CS_ARCH_PPC, CS_MODE_LITTLE_ENDIAN, &handle_lil) != CS_ERR_OK) {
+ if(cs_open(CS_ARCH_PPC, (cs_mode)((int)CS_MODE_LITTLE_ENDIAN | cs_mode_arg), &handle_lil) != CS_ERR_OK) {
MYLOG("ERROR: cs_open()\n");
goto cleanup;
}
@@ -68,14 +212,14 @@ powerpc_release(void)
}
extern "C" int
-powerpc_decompose(const uint8_t *data, int size, uint32_t addr, bool lil_end,
- struct decomp_result *res, bool is_64bit)
+powerpc_decompose(const uint8_t *data, int size, uint64_t addr, bool lil_end,
+ struct decomp_result *res, bool is_64bit, int cs_mode_arg)
{
int rc = -1;
res->status = STATUS_ERROR_UNSPEC;
if(!handle_lil) {
- powerpc_init();
+ powerpc_init(cs_mode_arg);
}
//typedef struct cs_insn {
@@ -120,6 +264,7 @@ powerpc_decompose(const uint8_t *data, int size, uint32_t addr, bool lil_end,
// } cs_ppc_op;
csh handle;
+ struct cs_struct *hand_tmp = 0;
cs_insn *insn = 0; /* instruction information
cs_disasm() will allocate array of cs_insn here */
@@ -129,6 +274,9 @@ powerpc_decompose(const uint8_t *data, int size, uint32_t addr, bool lil_end,
if(lil_end) handle = handle_lil;
res->handle = handle;
+ hand_tmp = (struct cs_struct *)handle;
+ hand_tmp->mode = (cs_mode)((int)hand_tmp->mode | cs_mode_arg);
+
/* call */
size_t n = cs_disasm(handle, data, size, addr, 1, &insn);
if(n != 1) {
@@ -173,11 +321,21 @@ powerpc_disassemble(struct decomp_result *res, char *buf, size_t len)
return rc;
}
+static const char* const gqr_array[] = {"gqr0", "gqr1", "gqr2", "gqr3", "gqr4", "gqr5", "gqr6", "gqr7"};
+
extern "C" const char *
-powerpc_reg_to_str(uint32_t rid)
+powerpc_reg_to_str(uint32_t rid, int cs_mode_arg)
{
+ if ((cs_mode_arg & CS_MODE_PS) != 0)
+ {
+ if ((rid >= PPC_REG_BN_GQR0) && (rid < PPC_REG_BN_ENDING))
+ {
+ return gqr_array[rid - PPC_REG_BN_GQR0];
+ }
+ }
+
if(!handle_lil) {
- powerpc_init();
+ powerpc_init(cs_mode_arg);
}
return cs_reg_name(handle_lil, rid);