summaryrefslogtreecommitdiff
path: root/arch/powerpc/disassembler.cpp
diff options
context:
space:
mode:
authorGalen Williamson <galen@vector35.com>2025-06-29 11:23:58 -0400
committerGalen Williamson <galen@vector35.com>2025-06-29 18:28:48 -0400
commite8206bc27e1633c7e4c99907f43bf67d2f200cd5 (patch)
tree2315bec284e9bb692a0eb8986578fe18e2289d23 /arch/powerpc/disassembler.cpp
parent91f1eccb6c41a7ef60fd66387ffae075f88141a4 (diff)
[powerpc] Full merge of PowerPC disassembler PRs, including:
* Remove dependency on capstone for PowerPC disassembly #6292 * Add support for PowerPC VLE instruction set #6740 * Add support for paired-single instructions #6821 * Various post-merge fixes and tweaks to disassembly and lifting * Removal of dependence on capstone for assembler's scoring mechanism (capstone currently disabled, but not removed from codebase yet)
Diffstat (limited to 'arch/powerpc/disassembler.cpp')
-rw-r--r--arch/powerpc/disassembler.cpp467
1 files changed, 165 insertions, 302 deletions
diff --git a/arch/powerpc/disassembler.cpp b/arch/powerpc/disassembler.cpp
index 2a421971..df444fe8 100644
--- a/arch/powerpc/disassembler.cpp
+++ b/arch/powerpc/disassembler.cpp
@@ -1,358 +1,221 @@
-/******************************************************************************
-See disassembler.h for more information about how this fits into the PPC
-architecture plugin picture.
-******************************************************************************/
+#include <string>
+#include <vector>
-#include <string.h> // strcpy, etc.
+#include "binaryninjaapi.h"
#define MYLOG(...) while(0);
-//#include <binaryninjaapi.h>
-//#define MYLOG BinaryNinja::LogDebug
+// #define MYLOG BinaryNinja::LogWarn
+// #define MYLOG printf
+using namespace std;
+using namespace BinaryNinja; // for ::LogDebug, etc.
+
+#include "decode/decode.h"
#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)
+bool FillInstruction(Instruction* instruction, const uint8_t* data, size_t length, uint64_t address, uint32_t extraFlags = 0, uint32_t decodeFlags = DECODE_FLAGS_PPC64, BNEndianness endian=LittleEndian)
{
- 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)
+ instruction->numBytes = length;
+ switch (instruction->numBytes)
{
- handle = (struct cs_struct*)handle_big;
- }
- else if (handle_lil != 0)
+ case 2:
{
- 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;
+ uint16_t word16 = *(const uint16_t *) data;
+ // VLE is always big-endian
+ word16 = bswap16(word16);
- if (handle != 0)
- {
- handle->printer(&tempmc, &ss, handle->printer_info);
+ return Decompose16(instruction, word16, address, decodeFlags | extraFlags);
}
- // 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));
-}
+ case 4:
+ {
+ uint32_t word32 = *(const uint32_t *) data;
-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;
+ // VLE is always big endian
+ if (((decodeFlags & DECODE_FLAGS_VLE) != 0) || endian == BigEndian)
+ word32 = bswap32(word32);
- if(bigendian == true)
- {
- insword = bswap32(insword);
+ return Decompose32(instruction, word32, address, decodeFlags | extraFlags);
}
- 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:
+ MYLOG("FillInstruction: unrecognized length %d", length);
+ LogWarn("FillInstruction: unrecognized length %d", length);
return false;
}
- return true;
}
-extern "C" int
-powerpc_init(int cs_mode_arg)
+bool PushOperandTokens(string& result, const Operand* op)
{
- int rc = -1;
+ char buf[32];
+ switch (op->cls)
+ {
+ case PPC_OP_REG_CRFD_IMPLY0:
+ case PPC_OP_REG_CRFS_IMPLY0:
+ if (op->reg == PPC_REG_CRF0)
+ return false;
- MYLOG("powerpc_init()\n");
+ result += PowerPCRegisterName(op->reg);
- if(handle_lil || handle_big) {
- MYLOG("ERROR: already initialized!\n");
- goto cleanup;
- }
+ break;
- /* initialize capstone handle */
- 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;
- }
+ case PPC_OP_REG_RA:
+ case PPC_OP_REG_RB:
+ case PPC_OP_REG_RD:
+ case PPC_OP_REG_RS:
+ case PPC_OP_REG_FRA:
+ case PPC_OP_REG_FRB:
+ case PPC_OP_REG_FRC:
+ case PPC_OP_REG_FRD:
+ case PPC_OP_REG_FRS:
+ case PPC_OP_REG_CRFD:
+ case PPC_OP_REG_CRFS:
+ case PPC_OP_REG_AV_VA:
+ case PPC_OP_REG_AV_VB:
+ case PPC_OP_REG_AV_VC:
+ case PPC_OP_REG_AV_VD:
+ case PPC_OP_REG_AV_VS:
+ case PPC_OP_REG_VSX_RA:
+ case PPC_OP_REG_VSX_RA_DWORD0:
+ case PPC_OP_REG_VSX_RB:
+ case PPC_OP_REG_VSX_RB_DWORD0:
+ case PPC_OP_REG_VSX_RC:
+ case PPC_OP_REG_VSX_RC_DWORD0:
+ case PPC_OP_REG_VSX_RD:
+ case PPC_OP_REG_VSX_RD_DWORD0:
+ case PPC_OP_REG_VSX_RS:
+ case PPC_OP_REG_VSX_RS_DWORD0:
+ result += PowerPCRegisterName(op->reg);
+ break;
- 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;
- }
+ case PPC_OP_UIMM:
+ snprintf(buf, sizeof(buf), "0x%" PRIx64, op->uimm);
+ result += buf;
+ break;
- cs_option(handle_big, CS_OPT_DETAIL, CS_OPT_ON);
- cs_option(handle_lil, CS_OPT_DETAIL, CS_OPT_ON);
+ case PPC_OP_SIMM:
+ if (op->simm < 0 && op->simm > -0x10000)
+ snprintf(buf, sizeof(buf), "-0x%llx", -op->simm);
+ else
+ snprintf(buf, sizeof(buf), "0x%llx", op->simm);
+ result += buf;
+ break;
- rc = 0;
- cleanup:
- if(rc) {
- powerpc_release();
- }
+ case PPC_OP_LABEL:
+ snprintf(buf, sizeof(buf), "0x%llx", op->label);
+ result += buf;
+ break;
- return rc;
-}
+ case PPC_OP_CRBIT_A:
+ case PPC_OP_CRBIT_B:
+ case PPC_OP_CRBIT_D:
+ result += GetCRBitName(op->crbit);
+ break;
-extern "C" void
-powerpc_release(void)
-{
- if(handle_lil) {
- cs_close(&handle_lil);
- handle_lil = 0;
- }
+ case PPC_OP_MEM_RA:
+ // eg: lwz r11, 8(r11)
+ //
+ // TODO: it would be nice to have the option to print these
+ // in hex; printed in decimal now for backwards compatibility
+ snprintf(buf, sizeof(buf), "%d", op->mem.offset);
+ result += buf;
+
+ result += "(";
+ if (op->mem.reg == PPC_REG_GPR0)
+ result += "0";
+ else
+ result += PowerPCRegisterName(op->mem.reg);
+ result += ")";
+ break;
- if(handle_big) {
- cs_close(&handle_big);
- handle_big = 0;
+ default:
+ //MYLOG("pushing a ???\n");
+ result += "???";
}
+
+ return true;
}
-extern "C" int
-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 disassemble(uint8_t *data, uint32_t addr, string& result)
{
int rc = -1;
- res->status = STATUS_ERROR_UNSPEC;
-
- if(!handle_lil) {
- powerpc_init(cs_mode_arg);
- }
-
- //typedef struct cs_insn {
- // unsigned int id; /* see capstone/ppc.h for PPC_INS_ADD, etc. */
- // uint64_t address;
- // uint16_t size;
- // uint8_t bytes[16];
- // char mnemonic[32]; /* string */
- // char op_str[160]; /* string */
- // cs_detail *detail; /* need CS_OP_DETAIL ON and CS_OP_SKIPDATA is OFF */
- //} cs_insn;
- // where cs_detail is some details + architecture specific part
- // typedef struct cs_detail {
- // uint8_t regs_read[12];
- // uint8_t regs_read_count;
- // uint8_t regs_write;
- // uint8_t regs_write_count;
- // uint8_t groups[8];
- // uint8_t groups_count;
- // cs_ppc *ppc;
- // }
+ char buf[32];
+ size_t strlenMnem;
+ Instruction instruction;
+ const char* mnemonic = NULL;
+ size_t len = 4;
+ uint32_t decodeFlags = DECODE_FLAGS_PPC64;
- // and finally ppc is:
- // typedef struct cs_ppc {
- // ppc_bc bc; /* branch code, see capstone/ppc.h for PPC_BC_LT, etc. */
- // ppc_bh bh; /* branch hint, see capstone/ppc.h for PPC_BH_PLUS, etc. */
- // bool update_cr0;
- // uint8_t op_count;
- // cs_ppc_op operands[8];
- // } cs_ppc;
-
- // and each operand is:
- // typedef struct cs_ppc_op {
- // ppc_op_type type; /* see capstone/ppc.h for PPC_OP_REG, etc. */
- // union {
- // unsigned int reg; // register value for REG operand
- // int32_t imm; // immediate value for IMM operand
- // ppc_op_mem mem; // struct ppc_op_mem { uint base; int disp }
- // ppc_op_crx crx; // struct ppc_op_crx { uint scale, uint reg }
- // };
- // } 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 */
-
- /* which handle to use?
- BIG end or LITTLE end? */
- handle = handle_big;
- 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) {
- MYLOG("ERROR: cs_disasm() returned %" PRIdPTR " (cs_errno:%d)\n", n, cs_errno(handle));
- goto cleanup;
+ //MYLOG("%s()\n", __func__);
+ size_t instructionLength = GetInstructionLength(data, len, decodeFlags);
+ if (instructionLength == 0)
+ {
+ MYLOG("ERROR: not enough bytes for instruction\n");
+ return false;
}
- /* set the status */
- res->status = STATUS_SUCCESS;
-
- /* copy the instruction struct, and detail sub struct to result */
- memcpy(&(res->insn), insn, sizeof(cs_insn));
- memcpy(&(res->detail), insn->detail, sizeof(cs_detail));
-
- rc = 0;
- cleanup:
- if(insn) {
- cs_free(insn, 1);
- insn = 0;
+ len = instructionLength;
+ if (!FillInstruction(&instruction, data, instructionLength, addr))
+ {
+ MYLOG("ERROR: FillInstruction()\n");
+ return -1;
}
- return rc;
-}
-extern "C" int
-powerpc_disassemble(struct decomp_result *res, char *buf, size_t len)
-{
- /* ideally the "heavy" string disassemble result is derived from light data
- in the decomposition result, but capstone doesn't make this distinction */
- int rc = -1;
+ /* mnemonic */
+ mnemonic = GetMnemonic(&instruction);
- if(len < strlen(res->insn.mnemonic)+strlen(res->insn.op_str) + 2) {
- MYLOG("ERROR: insufficient room\n");
- goto cleanup;
- }
+ result = mnemonic;
+ if (instruction.numOperands > 0)
+ result += " ";
- strcpy(buf, res->insn.mnemonic);
- strcat(buf, " ");
- strcat(buf, res->insn.op_str);
+ OperandsList operand_list;
+ memset(&operand_list, 0, sizeof(operand_list));
- rc = 0;
- cleanup:
- return rc;
-}
+ // The default is to just copy every operand, to simplify the
+ // alternate code path for special cases
+ operand_list.numOperands = instruction.numOperands;
+ for (int i = 0; i < instruction.numOperands; ++i)
+ {
+ operand_list.operands[i] = instruction.operands[i];
+ }
-static const char* const gqr_array[] = {"gqr0", "gqr1", "gqr2", "gqr3", "gqr4", "gqr5", "gqr6", "gqr7"};
+ switch(instruction.id)
+ {
+ case PPC_ID_BCx:
+ FillBcxOperands(&operand_list, &instruction);
+ break;
+ case PPC_ID_BCCTRx:
+ FillBcctrxOperands(&operand_list, &instruction);
+ break;
+ case PPC_ID_BCLRx:
+ FillBclrxOperands(&operand_list, &instruction);
+ break;
+ case PPC_ID_VLE_SE_BC:
+ FillVle16BcOperands(&operand_list, &instruction);
+ break;
+ case PPC_ID_VLE_E_BCx:
+ FillVle32BcxOperands(&operand_list, &instruction);
+ break;
+ default:
+ // Already copied by default
+ ;
+ }
-extern "C" const char *
-powerpc_reg_to_str(uint32_t rid, int cs_mode_arg)
-{
- if ((cs_mode_arg & CS_MODE_PS) != 0)
+ for (int i = 0; i < operand_list.numOperands; ++i)
{
- if ((rid >= PPC_REG_BN_GQR0) && (rid < PPC_REG_BN_ENDING))
+ Operand* op = &(operand_list.operands[i]);
+ bool was_pushed = PushOperandTokens(result, op);
+
+ if (was_pushed && i < operand_list.numOperands - 1)
{
- return gqr_array[rid - PPC_REG_BN_GQR0];
+ //MYLOG("pushing a comma\n");
+ result += ", ";
}
}
-
- if(!handle_lil) {
- powerpc_init(cs_mode_arg);
- }
-
- return cs_reg_name(handle_lil, rid);
-}
-
-extern "C" const uint32_t
-powerpc_crx_to_reg(uint32_t rid)
-{
- if (rid >= PPC_REG_CR0EQ && rid <= PPC_REG_CR7EQ)
- return (rid - PPC_REG_CR0EQ) * 4 + 2;
- else if (rid >= PPC_REG_CR0GT && rid <= PPC_REG_CR7GT)
- return (rid - PPC_REG_CR0GT) * 4 + 1;
- else if (rid >= PPC_REG_CR0LT && rid <= PPC_REG_CR7LT)
- return (rid - PPC_REG_CR0LT) * 4 + 0;
- else if (rid >= PPC_REG_CR0UN && rid <= PPC_REG_CR7UN)
- return (rid - PPC_REG_CR0UN) * 4 + 3;
- else
- return rid;
+ return 0;
}